From 4f6d400d0992594f8f12992fb6939378b12fadd6 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Mon, 14 Oct 2024 13:40:45 -0700 Subject: [PATCH 01/74] chore(hydroflow_datalog): update rust-sitter (#1495) The latest Rust Sitter drops the dependency on `tree-sitter-cli`, which eliminates many transitive dependencies. --- Cargo.lock | 353 ++++++------------------------ hydroflow_datalog_core/Cargo.toml | 4 +- 2 files changed, 69 insertions(+), 288 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 425c3ecc420a..19abd291761d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,15 +83,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "0.6.15" @@ -143,9 +134,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" dependencies = [ "backtrace", ] @@ -156,12 +147,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - [[package]] name = "async-channel" version = "2.3.1" @@ -488,9 +473,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.14" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "shlex", ] @@ -532,12 +517,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "chunked_transfer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" - [[package]] name = "ciborium" version = "0.2.2" @@ -795,16 +774,6 @@ dependencies = [ "syn 2.0.75", ] -[[package]] -name = "ctrlc" -version = "3.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" -dependencies = [ - "nix", - "windows-sys 0.59.0", -] - [[package]] name = "dashmap" version = "6.0.1" @@ -813,7 +782,7 @@ checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" dependencies = [ "cfg-if", "crossbeam-utils", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core 0.9.10", @@ -844,12 +813,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "difference" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" - [[package]] name = "differential-dataflow-master" version = "0.13.0-dev.1" @@ -873,27 +836,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - [[package]] name = "dunce" version = "1.0.5" @@ -984,18 +926,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" -[[package]] -name = "filetime" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1011,16 +941,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs4" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e180ac76c23b45e767bd7ae9579bc0bb458618c4bc71835926e098e61d15f8" -dependencies = [ - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "futures" version = "0.3.30" @@ -1187,6 +1107,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heck" version = "0.4.1" @@ -1226,15 +1152,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "html-escape" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" -dependencies = [ - "utf8-width", -] - [[package]] name = "http" version = "0.2.12" @@ -1252,12 +1169,6 @@ version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "humantime" version = "2.1.0" @@ -1354,7 +1265,7 @@ dependencies = [ "rand_distr", "ref-cast", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "sealed", "serde", "serde_json", @@ -1548,12 +1459,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -1717,33 +1628,12 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" -[[package]] -name = "libloading" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets 0.52.6", -] - [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.6.0", - "libc", - "redox_syscall 0.5.3", -] - [[package]] name = "libssh2-sys" version = "0.3.0" @@ -2030,12 +1920,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "overload" version = "0.1.1" @@ -2464,17 +2348,6 @@ dependencies = [ "bitflags 2.6.0", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - [[package]] name = "ref-cast" version = "1.0.23" @@ -2563,9 +2436,9 @@ dependencies = [ [[package]] name = "rust-sitter" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f69b9a5d53b74db5166799a0024c2849e144c652dd6253c5bf58dfe086798cbc" +checksum = "890cd29b9cd344678003f03f22d8b171a4d4b333f1ba88e2804b1ef7dd39b1d5" dependencies = [ "rust-sitter-macro", "tree-sitter-c2rust", @@ -2573,41 +2446,41 @@ dependencies = [ [[package]] name = "rust-sitter-common" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b559ebfd4114d398a36dfe25d7221bf84839fc3ef1309a6b7f4d1eece78dc690" +checksum = "3d8e1c8d26b8d5fb23415a5123588669e776ad934abbfbe6d8677ed322694216" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.75", ] [[package]] name = "rust-sitter-macro" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8238447de92f7104ddbda8b5fd38a9be055229373283ef42b774b340d8117def" +checksum = "07defe73314513d2b671b4c81603a236193eabe8ec7afd480dd3ed769dd75fb6" dependencies = [ "proc-macro2", "quote", "rust-sitter-common", - "syn 1.0.109", + "syn 2.0.75", ] [[package]] name = "rust-sitter-tool" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b840052f42d08fb67d13f68b72f1c41f99865d83239f4edff8fa1c6fd6fa0a12" +checksum = "9e666bbe8cf47a6ba0c88da4f2b98ebadb98bc1b5b9723aa5784137ae93f9436" dependencies = [ "cc", "rust-sitter-common", "serde", "serde_json", - "syn 1.0.109", - "syn-inline-mod 0.5.0", + "syn 2.0.75", + "syn-inline-mod", "tempfile", "tree-sitter", - "tree-sitter-cli", + "tree-sitter-generate", ] [[package]] @@ -2622,6 +2495,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc_version" version = "0.2.3" @@ -2715,9 +2594,9 @@ checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -2735,9 +2614,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -2746,9 +2625,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "indexmap", "itoa", @@ -2939,7 +2818,7 @@ dependencies = [ "quote", "sha256", "syn 2.0.75", - "syn-inline-mod 0.6.0", + "syn-inline-mod", ] [[package]] @@ -2964,6 +2843,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" +[[package]] +name = "streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" + [[package]] name = "strsim" version = "0.11.1" @@ -2992,16 +2877,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn-inline-mod" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b670f535364c67358ecffb60b9f2579f9b45d3c71e8cca6d45d22ee0fadaa7eb" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "syn-inline-mod" version = "0.6.0" @@ -3158,18 +3033,6 @@ dependencies = [ "timely-logging-master", ] -[[package]] -name = "tiny_http" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" -dependencies = [ - "ascii", - "chunked_transfer", - "httpdate", - "log", -] - [[package]] name = "tinytemplate" version = "1.2.1" @@ -3384,123 +3247,59 @@ dependencies = [ [[package]] name = "tree-sitter" -version = "0.22.6" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca" +checksum = "f9871f16d6cf5c4757dcf30d5d2172a2df6987c510c017bbb7abfb7f9aa24d06" dependencies = [ "cc", "regex", + "regex-syntax 0.8.4", + "streaming-iterator", + "tree-sitter-language", ] [[package]] name = "tree-sitter-c2rust" -version = "0.22.6" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62cd4f1075a82f3c4ae5e93dc39d4e0765d132a6d2773b3d86214fcc54e6d1e9" +checksum = "a5f97c8ed19295f92e02af90c915c72d0836e08132f155709bc45b66acdfde35" dependencies = [ "c2rust-bitfields", "once_cell", "regex", + "regex-syntax 0.8.4", + "streaming-iterator", + "tree-sitter-language", ] [[package]] -name = "tree-sitter-cli" -version = "0.22.6" +name = "tree-sitter-generate" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7437ac48e37e5014007527ed9281c00c333c9ad0731e1c8489c0eff667b99d5" +checksum = "169563a4e3713ad23a2a755446ed1e3af7db5cfc3b8beb0986b7e96f3619aec3" dependencies = [ - "ansi_term", - "anstyle", "anyhow", - "clap", - "ctrlc", - "difference", - "dirs", - "filetime", - "glob", "heck 0.5.0", - "html-escape", "indexmap", "indoc", "lazy_static", "log", - "memchr", "regex", "regex-syntax 0.8.4", - "rustc-hash", + "rustc-hash 2.0.0", "semver 1.0.23", "serde", - "serde_derive", "serde_json", "smallbitvec", - "tiny_http", "tree-sitter", - "tree-sitter-config", - "tree-sitter-highlight", - "tree-sitter-loader", - "tree-sitter-tags", - "walkdir", - "wasmparser", - "webbrowser", -] - -[[package]] -name = "tree-sitter-config" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d64b4608a1d822f56e3afcecabfa4915a768ea92bc44abad1ae32cd4c607ebd" -dependencies = [ - "anyhow", - "dirs", - "serde", - "serde_json", -] - -[[package]] -name = "tree-sitter-highlight" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaca0fe34fa96eec6aaa8e63308dbe1bafe65a6317487c287f93938959b21907" -dependencies = [ - "lazy_static", - "regex", - "thiserror", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-loader" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c9b13749644fbe22ec25c79861dc1e637ef4ab9e469fd820fcb30b10091293" -dependencies = [ - "anyhow", - "cc", - "dirs", - "fs4", - "indoc", - "libloading", - "once_cell", - "regex", - "serde", - "serde_json", - "tempfile", - "tree-sitter", - "tree-sitter-highlight", - "tree-sitter-tags", + "url", ] [[package]] -name = "tree-sitter-tags" -version = "0.22.6" +name = "tree-sitter-language" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34380416097ab36d1b4cd83f887d9e150ea4feaeb6ee9a5ecfe53d26839acc69" -dependencies = [ - "memchr", - "regex", - "thiserror", - "tree-sitter", -] +checksum = "e8ddffe35a0e5eeeadf13ff7350af564c6e73993a24db62caee1822b185c2600" [[package]] name = "try_match" @@ -3623,6 +3422,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -3631,12 +3431,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf8-width" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" - [[package]] name = "utf8parse" version = "0.2.2" @@ -3653,7 +3447,7 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" name = "variadics" version = "0.0.6" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", "sealed", "trybuild", ] @@ -3779,19 +3573,6 @@ dependencies = [ "syn 2.0.75", ] -[[package]] -name = "wasmparser" -version = "0.206.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39192edb55d55b41963db40fd49b0b542156f04447b5b512744a91d38567bdbc" -dependencies = [ - "ahash", - "bitflags 2.6.0", - "hashbrown", - "indexmap", - "semver 1.0.23", -] - [[package]] name = "web-sys" version = "0.3.70" diff --git a/hydroflow_datalog_core/Cargo.toml b/hydroflow_datalog_core/Cargo.toml index c2ee69bf0eac..f669063d3107 100644 --- a/hydroflow_datalog_core/Cargo.toml +++ b/hydroflow_datalog_core/Cargo.toml @@ -22,11 +22,11 @@ quote = "1.0.35" slotmap = "1.0.0" syn = { version = "2.0.46", features = [ "parsing", "extra-traits" ] } proc-macro2 = "1.0.74" -rust-sitter = "0.4.2" +rust-sitter = "0.4.3" hydroflow_lang = { path = "../hydroflow_lang", version = "^0.9.0" } [build-dependencies] -rust-sitter-tool = "0.4.2" +rust-sitter-tool = "0.4.3" [dev-dependencies] insta = "1.39" From e564b133a4db192e0331d427aa80ef52cd97608c Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Wed, 16 Oct 2024 14:14:17 -0700 Subject: [PATCH 02/74] chore: update `proc-macro2`, fixes span info, fix #729 (#1497) --- Cargo.lock | 4 ++-- hydroflow/tests/surface_warnings.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19abd291761d..bc00ec71c754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2127,9 +2127,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] diff --git a/hydroflow/tests/surface_warnings.rs b/hydroflow/tests/surface_warnings.rs index ae71f41c5aee..fa9826083cd5 100644 --- a/hydroflow/tests/surface_warnings.rs +++ b/hydroflow/tests/surface_warnings.rs @@ -17,7 +17,7 @@ fn test_degenerate_union() { { source_iter([1, 2, 3]) -> union() -> for_each(|x| result_send.send(x).unwrap()); }, - "Warning: `union` should have at least 2 input(s), actually has 1.\n --> $FILE:0:0", + "Warning: `union` should have at least 2 input(s), actually has 1.\n --> $FILE:2:38", }; df.run_available(); @@ -30,7 +30,7 @@ fn test_empty_union() { { union() -> for_each(|x: usize| println!("{}", x)); }, - "Warning: `union` should have at least 2 input(s), actually has 0.\n --> $FILE:0:0", + "Warning: `union` should have at least 2 input(s), actually has 0.\n --> $FILE:2:12", }; df.run_available(); } @@ -43,7 +43,7 @@ fn test_degenerate_tee() { { source_iter([1, 2, 3]) -> tee() -> for_each(|x| result_send.send(x).unwrap()); }, - "Warning: `tee` should have at least 2 output(s), actually has 1.\n --> $FILE:0:0" + "Warning: `tee` should have at least 2 output(s), actually has 1.\n --> $FILE:2:38" }; df.run_available(); @@ -59,7 +59,7 @@ fn test_empty_tee() { { source_iter([1, 2, 3]) -> inspect(|&x| output_inner.borrow_mut().push(x)) -> tee(); }, - "Warning: `tee` should have at least 2 output(s), actually has 0.\n --> $FILE:0:0", + "Warning: `tee` should have at least 2 output(s), actually has 0.\n --> $FILE:2:89", }; df.run_available(); @@ -86,7 +86,7 @@ pub fn test_warped_diamond() { nodes -> [0]init; new_node[1] -> map(|n| (n, 'b')) -> [1]init; }, - "Warning: `union` should have at least 2 input(s), actually has 1.\n --> $FILE:0:0", + "Warning: `union` should have at least 2 input(s), actually has 1.\n --> $FILE:3:20", }; df.run_available(); } @@ -111,8 +111,8 @@ pub fn test_warped_diamond_2() { ntwk = source_iter([4, 5, 6]) -> tee(); }, - "Warning: `union` should have at least 2 input(s), actually has 1.\n --> $FILE:0:0", - "Warning: `tee` should have at least 2 output(s), actually has 0.\n --> $FILE:0:0", + "Warning: `union` should have at least 2 input(s), actually has 1.\n --> $FILE:3:20", + "Warning: `tee` should have at least 2 output(s), actually has 0.\n --> $FILE:16:45", }; hf.run_available(); } From 47cb703e771f7d1c451ceb9d185ada96410949da Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Wed, 23 Oct 2024 10:17:37 -0700 Subject: [PATCH 03/74] style: fixes for nightly clippy (#1505) a couple few spurious `too_many_arguments` and a spurious `zombie_processes` still on current nightly (`clippy 0.1.84 (4392847410 2024-10-21)`) --- hydroflow/src/compiled/pull/anti_join.rs | 2 +- hydroflow/src/compiled/pull/cross_join.rs | 2 +- .../src/compiled/pull/symmetric_hash_join.rs | 4 +- hydroflow/src/scheduled/graph.rs | 8 +- hydroflow/src/scheduled/graph_ext.rs | 2 +- hydroflow/src/scheduled/net/mod.rs | 2 +- hydroflow/src/util/simulation.rs | 2 +- .../src/graph/ops/anti_join_multiset.rs | 3 +- hydroflow_plus/src/builder/built.rs | 9 +- hydroflow_plus/src/builder/mod.rs | 14 +- hydroflow_plus/src/deploy/deploy_graph.rs | 10 +- hydroflow_plus/src/deploy/in_memory_graph.rs | 8 +- hydroflow_plus/src/deploy/macro_runtime.rs | 6 +- hydroflow_plus/src/lib.rs | 2 +- hydroflow_plus/src/location.rs | 6 +- lattices/src/collections.rs | 226 +++++++++++------- lattices/src/test.rs | 4 +- lattices/src/vec_union.rs | 12 +- stageleft/src/lib.rs | 5 +- variadics/src/lib.rs | 38 ++- variadics/tests/boxed_refed.rs | 3 +- 21 files changed, 216 insertions(+), 152 deletions(-) diff --git a/hydroflow/src/compiled/pull/anti_join.rs b/hydroflow/src/compiled/pull/anti_join.rs index 9e11f691f1c1..97421b7de62d 100644 --- a/hydroflow/src/compiled/pull/anti_join.rs +++ b/hydroflow/src/compiled/pull/anti_join.rs @@ -12,7 +12,7 @@ where pos_state: &'a mut FxHashSet<(Key, V)>, } -impl<'a, Key, V, Ipos> Iterator for AntiJoin<'a, Key, V, Ipos> +impl Iterator for AntiJoin<'_, Key, V, Ipos> where Key: Eq + std::hash::Hash + Clone, V: Eq + std::hash::Hash + Clone, diff --git a/hydroflow/src/compiled/pull/cross_join.rs b/hydroflow/src/compiled/pull/cross_join.rs index 90f743d9bc92..6b9629fa65f9 100644 --- a/hydroflow/src/compiled/pull/cross_join.rs +++ b/hydroflow/src/compiled/pull/cross_join.rs @@ -30,7 +30,7 @@ where state: &'a mut CrossJoinState, } -impl<'a, I1, V1: 'static, I2, V2: 'static> Iterator for CrossJoin<'a, I1, V1, I2, V2> +impl Iterator for CrossJoin<'_, I1, V1, I2, V2> where V1: Eq + Clone, V2: Eq + Clone, diff --git a/hydroflow/src/compiled/pull/symmetric_hash_join.rs b/hydroflow/src/compiled/pull/symmetric_hash_join.rs index d240e6a347ad..1665c9940a05 100644 --- a/hydroflow/src/compiled/pull/symmetric_hash_join.rs +++ b/hydroflow/src/compiled/pull/symmetric_hash_join.rs @@ -18,8 +18,8 @@ where rhs_state: &'a mut RhsState, } -impl<'a, Key, I1, V1, I2, V2, LhsState, RhsState> Iterator - for SymmetricHashJoin<'a, Key, I1, V1, I2, V2, LhsState, RhsState> +impl Iterator + for SymmetricHashJoin<'_, Key, I1, V1, I2, V2, LhsState, RhsState> where Key: Eq + std::hash::Hash + Clone, V1: Clone, diff --git a/hydroflow/src/scheduled/graph.rs b/hydroflow/src/scheduled/graph.rs index 85d71d98bfe0..2d4e96962ee2 100644 --- a/hydroflow/src/scheduled/graph.rs +++ b/hydroflow/src/scheduled/graph.rs @@ -47,7 +47,7 @@ pub struct Hydroflow<'a> { /// See [`Self::diagnostics()`]. diagnostics: Option>>, } -impl<'a> Default for Hydroflow<'a> { +impl Default for Hydroflow<'_> { fn default() -> Self { let stratum_queues = vec![Default::default()]; // Always initialize stratum #0. let (event_queue_send, event_queue_recv) = mpsc::unbounded_channel(); @@ -69,7 +69,7 @@ impl<'a> Default for Hydroflow<'a> { } /// Methods for [`TeeingHandoff`] teeing and dropping. -impl<'a> Hydroflow<'a> { +impl Hydroflow<'_> { /// Tees a [`TeeingHandoff`]. pub fn teeing_handoff_tee( &mut self, @@ -794,7 +794,7 @@ impl<'a> Hydroflow<'a> { } } -impl<'a> Hydroflow<'a> { +impl Hydroflow<'_> { /// Alias for [`Context::request_task`]. pub fn request_task(&mut self, future: Fut) where @@ -814,7 +814,7 @@ impl<'a> Hydroflow<'a> { } } -impl<'a> Drop for Hydroflow<'a> { +impl Drop for Hydroflow<'_> { fn drop(&mut self) { self.abort_tasks(); } diff --git a/hydroflow/src/scheduled/graph_ext.rs b/hydroflow/src/scheduled/graph_ext.rs index 6f54413d832e..1d0be9d28dd6 100644 --- a/hydroflow/src/scheduled/graph_ext.rs +++ b/hydroflow/src/scheduled/graph_ext.rs @@ -126,7 +126,7 @@ pub trait GraphExt { W: 'static + Handoff + CanReceive; } -impl<'a> GraphExt for Hydroflow<'a> { +impl GraphExt for Hydroflow<'_> { subgraph_ext!(impl add_subgraph_sink, (recv_port: R), ()); subgraph_ext!( impl add_subgraph_2sink, diff --git a/hydroflow/src/scheduled/net/mod.rs b/hydroflow/src/scheduled/net/mod.rs index 48ffc2e6115f..10664f8a58d3 100644 --- a/hydroflow/src/scheduled/net/mod.rs +++ b/hydroflow/src/scheduled/net/mod.rs @@ -96,7 +96,7 @@ impl Message { } } -impl<'a> Hydroflow<'a> { +impl Hydroflow<'_> { fn register_read_tcp_stream(&mut self, reader: OwnedReadHalf) -> RecvPort> { let reader = FramedRead::new(reader, LengthDelimitedCodec::new()); let (send_port, recv_port) = self.make_edge("tcp ingress handoff"); diff --git a/hydroflow/src/util/simulation.rs b/hydroflow/src/util/simulation.rs index f1611be636e7..86b2bc785644 100644 --- a/hydroflow/src/util/simulation.rs +++ b/hydroflow/src/util/simulation.rs @@ -177,7 +177,7 @@ fn sink_from_fn(mut f: impl FnMut(T)) -> impl Sink { }) } -impl<'context> TransducerBuilderContext<'context> { +impl TransducerBuilderContext<'_> { /// Create a new inbox on the host with the given interface name. Returns a stream that can /// be read by the transducer using the source_stream hydroflow operator. pub fn new_inbox( diff --git a/hydroflow_lang/src/graph/ops/anti_join_multiset.rs b/hydroflow_lang/src/graph/ops/anti_join_multiset.rs index 3af7e569eca1..dbb18358b86c 100644 --- a/hydroflow_lang/src/graph/ops/anti_join_multiset.rs +++ b/hydroflow_lang/src/graph/ops/anti_join_multiset.rs @@ -8,6 +8,7 @@ use super::{ }; use crate::diagnostic::{Diagnostic, Level}; +// This implementation is largely redundant to ANTI_JOIN and should be DRY'ed /// > 2 input streams the first of type (K, T), the second of type K, /// > with output type (K, T) /// @@ -22,8 +23,6 @@ use crate::diagnostic::{Diagnostic, Level}; /// source_iter(vec!["dog", "cat", "gorilla"]) -> [neg]diff; /// diff = anti_join_multiset() -> assert_eq([("elephant", 3), ("elephant", 3)]); /// ``` - -// This implementation is largely redundant to ANTI_JOIN and should be DRY'ed pub const ANTI_JOIN_MULTISET: OperatorConstraints = OperatorConstraints { name: "anti_join_multiset", categories: &[OperatorCategory::MultiIn], diff --git a/hydroflow_plus/src/builder/built.rs b/hydroflow_plus/src/builder/built.rs index fd8659a0c12a..4caf767f0a6d 100644 --- a/hydroflow_plus/src/builder/built.rs +++ b/hydroflow_plus/src/builder/built.rs @@ -18,7 +18,7 @@ pub struct BuiltFlow<'a> { pub(super) _phantom: PhantomData<&'a mut &'a ()>, } -impl<'a> Drop for BuiltFlow<'a> { +impl Drop for BuiltFlow<'_> { fn drop(&mut self) { if !self.used { panic!("Dropped BuiltFlow without instantiating, you may have forgotten to call `compile` or `deploy`."); @@ -26,15 +26,12 @@ impl<'a> Drop for BuiltFlow<'a> { } } -impl<'a> BuiltFlow<'a> { +impl BuiltFlow<'_> { pub fn ir(&self) -> &Vec { &self.ir } - pub fn optimize_with( - mut self, - f: impl FnOnce(Vec) -> Vec, - ) -> BuiltFlow<'a> { + pub fn optimize_with(mut self, f: impl FnOnce(Vec) -> Vec) -> Self { self.used = true; BuiltFlow { ir: f(std::mem::take(&mut self.ir)), diff --git a/hydroflow_plus/src/builder/mod.rs b/hydroflow_plus/src/builder/mod.rs index c30f19883177..593b7fb23ffc 100644 --- a/hydroflow_plus/src/builder/mod.rs +++ b/hydroflow_plus/src/builder/mod.rs @@ -37,13 +37,13 @@ pub struct ClusterIds<'a, C> { pub(crate) _phantom: PhantomData<&'a mut &'a C>, } -impl<'a, C> Clone for ClusterIds<'a, C> { +impl Clone for ClusterIds<'_, C> { fn clone(&self) -> Self { *self } } -impl<'a, C> Copy for ClusterIds<'a, C> {} +impl Copy for ClusterIds<'_, C> {} impl<'a, C> FreeVariable<&'a Vec>> for ClusterIds<'a, C> { fn to_tokens(self) -> (Option, Option) @@ -72,15 +72,15 @@ pub(crate) struct ClusterSelfId<'a, C> { pub(crate) _phantom: PhantomData<&'a mut &'a C>, } -impl<'a, C> Clone for ClusterSelfId<'a, C> { +impl Clone for ClusterSelfId<'_, C> { fn clone(&self) -> Self { *self } } -impl<'a, C> Copy for ClusterSelfId<'a, C> {} +impl Copy for ClusterSelfId<'_, C> {} -impl<'a, C> FreeVariable> for ClusterSelfId<'a, C> { +impl FreeVariable> for ClusterSelfId<'_, C> { fn to_tokens(self) -> (Option, Option) where Self: Sized, @@ -118,7 +118,7 @@ pub struct FlowBuilder<'a> { _phantom: PhantomData<&'a mut &'a ()>, } -impl<'a> Drop for FlowBuilder<'a> { +impl Drop for FlowBuilder<'_> { fn drop(&mut self) { if !self.finalized { panic!("Dropped FlowBuilder without finalizing, you may have forgotten to call `with_default_optimize`, `optimize_with`, or `finalize`."); @@ -126,7 +126,7 @@ impl<'a> Drop for FlowBuilder<'a> { } } -impl<'a> QuotedContext for FlowBuilder<'a> { +impl QuotedContext for FlowBuilder<'_> { fn create() -> Self { FlowBuilder::new() } diff --git a/hydroflow_plus/src/deploy/deploy_graph.rs b/hydroflow_plus/src/deploy/deploy_graph.rs index 5b9da95314ce..218fbda3f5b5 100644 --- a/hydroflow_plus/src/deploy/deploy_graph.rs +++ b/hydroflow_plus/src/deploy/deploy_graph.rs @@ -574,7 +574,7 @@ impl Node for DeployExternal { fn update_meta(&mut self, _meta: &Self::Meta) {} } -impl<'a> ExternalSpec<'a, HydroDeploy> for Arc { +impl ExternalSpec<'_, HydroDeploy> for Arc { fn build(self, _id: usize, _name_hint: &str) -> DeployExternal { DeployExternal { next_port: Rc::new(RefCell::new(0)), @@ -753,7 +753,7 @@ impl DeployProcessSpec { } } -impl<'a> ProcessSpec<'a, HydroDeploy> for DeployProcessSpec { +impl ProcessSpec<'_, HydroDeploy> for DeployProcessSpec { fn build(self, id: usize, _name_hint: &str) -> DeployNode { DeployNode { id, @@ -764,7 +764,7 @@ impl<'a> ProcessSpec<'a, HydroDeploy> for DeployProcessSpec { } } -impl<'a> ProcessSpec<'a, HydroDeploy> for TrybuildHost { +impl ProcessSpec<'_, HydroDeploy> for TrybuildHost { fn build(mut self, id: usize, name_hint: &str) -> DeployNode { self.name_hint = Some(format!("{} (process {id})", name_hint)); DeployNode { @@ -785,7 +785,7 @@ impl DeployClusterSpec { } } -impl<'a> ClusterSpec<'a, HydroDeploy> for DeployClusterSpec { +impl ClusterSpec<'_, HydroDeploy> for DeployClusterSpec { fn build(self, id: usize, _name_hint: &str) -> DeployCluster { DeployCluster { id, @@ -799,7 +799,7 @@ impl<'a> ClusterSpec<'a, HydroDeploy> for DeployClusterSpec { } } -impl<'a> ClusterSpec<'a, HydroDeploy> for Vec { +impl ClusterSpec<'_, HydroDeploy> for Vec { fn build(self, id: usize, name_hint: &str) -> DeployCluster { let name_hint = format!("{} (cluster {id})", name_hint); DeployCluster { diff --git a/hydroflow_plus/src/deploy/in_memory_graph.rs b/hydroflow_plus/src/deploy/in_memory_graph.rs index a794dbb8c32e..aeee8233c8cf 100644 --- a/hydroflow_plus/src/deploy/in_memory_graph.rs +++ b/hydroflow_plus/src/deploy/in_memory_graph.rs @@ -4,7 +4,7 @@ use super::{LocalDeploy, Node, ProcessSpec}; pub struct SingleProcessGraph {} -impl<'a> LocalDeploy<'a> for SingleProcessGraph { +impl LocalDeploy<'_> for SingleProcessGraph { type Process = SingleNode; type Cluster = SingleNode; type ExternalProcess = SingleNode; @@ -24,7 +24,7 @@ impl<'a> LocalDeploy<'a> for SingleProcessGraph { } } -impl<'a> ProcessSpec<'a, SingleProcessGraph> for () { +impl ProcessSpec<'_, SingleProcessGraph> for () { fn build(self, _id: usize, _name_hint: &str) -> SingleNode { SingleNode {} } @@ -56,7 +56,7 @@ impl Node for SingleNode { pub struct MultiGraph {} -impl<'a> LocalDeploy<'a> for MultiGraph { +impl LocalDeploy<'_> for MultiGraph { type Process = MultiNode; type Cluster = MultiNode; type ExternalProcess = MultiNode; @@ -76,7 +76,7 @@ impl<'a> LocalDeploy<'a> for MultiGraph { } } -impl<'a> ProcessSpec<'a, MultiGraph> for () { +impl ProcessSpec<'_, MultiGraph> for () { fn build(self, _id: usize, _name_hint: &str) -> MultiNode { MultiNode {} } diff --git a/hydroflow_plus/src/deploy/macro_runtime.rs b/hydroflow_plus/src/deploy/macro_runtime.rs index 2310c5743dc1..a565b20fb561 100644 --- a/hydroflow_plus/src/deploy/macro_runtime.rs +++ b/hydroflow_plus/src/deploy/macro_runtime.rs @@ -295,7 +295,7 @@ impl Node for DeployRuntimeCluster { } } -impl<'a> ProcessSpec<'a, DeployRuntime> for () { +impl ProcessSpec<'_, DeployRuntime> for () { fn build(self, _id: usize, _name_hint: &str) -> DeployRuntimeNode { DeployRuntimeNode { next_port: Rc::new(RefCell::new(0)), @@ -303,7 +303,7 @@ impl<'a> ProcessSpec<'a, DeployRuntime> for () { } } -impl<'cli> ClusterSpec<'cli, DeployRuntime> for () { +impl ClusterSpec<'_, DeployRuntime> for () { fn build(self, _id: usize, _name_hint: &str) -> DeployRuntimeCluster { DeployRuntimeCluster { next_port: Rc::new(RefCell::new(0)), @@ -311,7 +311,7 @@ impl<'cli> ClusterSpec<'cli, DeployRuntime> for () { } } -impl<'cli> ExternalSpec<'cli, DeployRuntime> for () { +impl ExternalSpec<'_, DeployRuntime> for () { fn build(self, _id: usize, _name_hint: &str) -> DeployRuntimeNode { panic!() } diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 3f3c5fbb109c..5acf5ac634bc 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -64,7 +64,7 @@ pub struct HfCompiled<'a, ID> { _phantom: PhantomData<&'a mut &'a ID>, } -impl<'a, ID> HfCompiled<'a, ID> { +impl HfCompiled<'_, ID> { pub fn hydroflow_ir(&self) -> &BTreeMap { &self.hydroflow_ir } diff --git a/hydroflow_plus/src/location.rs b/hydroflow_plus/src/location.rs index da9a5978dd8d..7e4a5e428bb5 100644 --- a/hydroflow_plus/src/location.rs +++ b/hydroflow_plus/src/location.rs @@ -284,7 +284,7 @@ pub struct ExternalProcess<'a, P> { pub(crate) _phantom: PhantomData<&'a &'a mut P>, } -impl<'a, P> Clone for ExternalProcess<'a, P> { +impl

Clone for ExternalProcess<'_, P> { fn clone(&self) -> Self { ExternalProcess { id: self.id, @@ -385,7 +385,7 @@ pub struct Process<'a, P> { pub(crate) _phantom: PhantomData<&'a &'a mut P>, } -impl<'a, P> Clone for Process<'a, P> { +impl

Clone for Process<'_, P> { fn clone(&self) -> Self { Process { id: self.id, @@ -519,7 +519,7 @@ impl<'a, C> Cluster<'a, C> { } } -impl<'a, C> Clone for Cluster<'a, C> { +impl Clone for Cluster<'_, C> { fn clone(&self) -> Self { Cluster { id: self.id, diff --git a/lattices/src/collections.rs b/lattices/src/collections.rs index 13494e6fe915..42bc8ec24cf8 100644 --- a/lattices/src/collections.rs +++ b/lattices/src/collections.rs @@ -84,7 +84,8 @@ impl Len for VecSet { } } impl CollectionRef for VecSet { - type ItemRef<'a> = &'a Self::Item + type ItemRef<'a> + = &'a Self::Item where Self: 'a; @@ -103,7 +104,8 @@ where } } impl CollectionMut for VecSet { - type ItemMut<'a> = &'a mut Self::Item + type ItemMut<'a> + = &'a mut Self::Item where Self: 'a; @@ -119,7 +121,8 @@ where } } impl Iter for VecSet { - type Iter<'a> = std::slice::Iter<'a, T> + type Iter<'a> + = std::slice::Iter<'a, T> where Self: 'a; @@ -128,7 +131,8 @@ impl Iter for VecSet { } } impl IterMut for VecSet { - type IterMut<'a> = std::slice::IterMut<'a, T> + type IterMut<'a> + = std::slice::IterMut<'a, T> where Self: 'a; @@ -176,7 +180,8 @@ impl Len for VecMap { } } impl CollectionRef for VecMap { - type ItemRef<'a> = &'a Self::Item + type ItemRef<'a> + = &'a Self::Item where Self: 'a; @@ -198,7 +203,8 @@ where } } impl CollectionMut for VecMap { - type ItemMut<'a> = &'a mut Self::Item + type ItemMut<'a> + = &'a mut Self::Item where Self: 'a; @@ -220,7 +226,8 @@ impl Keyed for VecMap { type Key = K; } impl KeyedRef for VecMap { - type KeyRef<'a> = &'a Self::Key + type KeyRef<'a> + = &'a Self::Key where Self: 'a; @@ -254,18 +261,20 @@ impl SimpleKeyedRef for VecMap { simple_keyed_ref!(); } impl MapIter for VecMap { - type Iter<'a> = std::iter::Zip, std::slice::Iter<'a, V>> - where - Self: 'a; + type Iter<'a> + = std::iter::Zip, std::slice::Iter<'a, V>> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { self.keys.iter().zip(self.vals.iter()) } } impl MapIterMut for VecMap { - type IterMut<'a> = std::iter::Zip, std::slice::IterMut<'a, V>> - where - Self: 'a; + type IterMut<'a> + = std::iter::Zip, std::slice::IterMut<'a, V>> + where + Self: 'a; fn iter_mut(&mut self) -> Self::IterMut<'_> { self.keys.iter().zip(self.vals.iter_mut()) @@ -307,7 +316,10 @@ impl Collection for EmptySet { } impl CollectionRef for EmptySet { - type ItemRef<'a> = &'a Self::Item where Self::Item: 'a; + type ItemRef<'a> + = &'a Self::Item + where + Self::Item: 'a; covariant_item_ref!(); } @@ -328,7 +340,10 @@ impl Len for EmptySet { } impl Iter for EmptySet { - type Iter<'a> = std::iter::Empty<&'a T> where T: 'a; + type Iter<'a> + = std::iter::Empty<&'a T> + where + T: 'a; fn iter(&self) -> Self::Iter<'_> { std::iter::empty() @@ -371,7 +386,8 @@ impl Len for SingletonSet { } } impl CollectionRef for SingletonSet { - type ItemRef<'a> = &'a Self::Item + type ItemRef<'a> + = &'a Self::Item where Self: 'a; @@ -390,7 +406,8 @@ where } } impl CollectionMut for SingletonSet { - type ItemMut<'a> = &'a mut T + type ItemMut<'a> + = &'a mut T where Self: 'a; @@ -406,16 +423,18 @@ where } } impl Iter for SingletonSet { - type Iter<'a> = std::iter::Once<&'a T> - where - Self: 'a; + type Iter<'a> + = std::iter::Once<&'a T> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { std::iter::once(&self.0) } } impl IterMut for SingletonSet { - type IterMut<'a> = std::iter::Once<&'a mut T> + type IterMut<'a> + = std::iter::Once<&'a mut T> where Self: 'a; @@ -457,7 +476,8 @@ impl Len for EmptyMap { } } impl CollectionRef for EmptyMap { - type ItemRef<'a> = &'a Self::Item + type ItemRef<'a> + = &'a Self::Item where Self: 'a; @@ -476,7 +496,8 @@ where } } impl CollectionMut for EmptyMap { - type ItemMut<'a> = &'a mut Self::Item + type ItemMut<'a> + = &'a mut Self::Item where Self: 'a; @@ -495,9 +516,10 @@ impl Keyed for EmptyMap { type Key = K; } impl KeyedRef for EmptyMap { - type KeyRef<'a> = &'a Self::Key - where - Self: 'a; + type KeyRef<'a> + = &'a Self::Key + where + Self: 'a; covariant_key_ref!(); } @@ -520,9 +542,10 @@ where } } impl Iter for EmptyMap { - type Iter<'a> = std::iter::Empty<&'a V> - where - Self: 'a; + type Iter<'a> + = std::iter::Empty<&'a V> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { std::iter::empty() @@ -532,18 +555,20 @@ impl SimpleKeyedRef for EmptyMap { simple_keyed_ref!(); } impl MapIter for EmptyMap { - type Iter<'a> = std::iter::Empty<(&'a K, &'a V)> - where - Self: 'a; + type Iter<'a> + = std::iter::Empty<(&'a K, &'a V)> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { std::iter::empty() } } impl MapIterMut for EmptyMap { - type IterMut<'a> = std::iter::Empty<(&'a K, &'a mut V)> - where - Self: 'a; + type IterMut<'a> + = std::iter::Empty<(&'a K, &'a mut V)> + where + Self: 'a; fn iter_mut(&mut self) -> Self::IterMut<'_> { std::iter::empty() @@ -576,7 +601,8 @@ impl Len for SingletonMap { } } impl CollectionRef for SingletonMap { - type ItemRef<'a> = &'a Self::Item + type ItemRef<'a> + = &'a Self::Item where Self: 'a; @@ -595,7 +621,8 @@ where } } impl CollectionMut for SingletonMap { - type ItemMut<'a> = &'a mut Self::Item + type ItemMut<'a> + = &'a mut Self::Item where Self: 'a; @@ -614,9 +641,10 @@ impl Keyed for SingletonMap { type Key = K; } impl KeyedRef for SingletonMap { - type KeyRef<'a> = &'a Self::Key - where - Self: 'a; + type KeyRef<'a> + = &'a Self::Key + where + Self: 'a; covariant_key_ref!(); } @@ -639,9 +667,10 @@ where } } impl Iter for SingletonMap { - type Iter<'a> = std::iter::Once<&'a V> - where - Self: 'a; + type Iter<'a> + = std::iter::Once<&'a V> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { std::iter::once(&self.1) @@ -651,18 +680,20 @@ impl SimpleKeyedRef for SingletonMap { simple_keyed_ref!(); } impl MapIter for SingletonMap { - type Iter<'a> = std::iter::Once<(&'a K, &'a V)> - where - Self: 'a; + type Iter<'a> + = std::iter::Once<(&'a K, &'a V)> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { std::iter::once((&self.0, &self.1)) } } impl MapIterMut for SingletonMap { - type IterMut<'a> = std::iter::Once<(&'a K, &'a mut V)> - where - Self: 'a; + type IterMut<'a> + = std::iter::Once<(&'a K, &'a mut V)> + where + Self: 'a; fn iter_mut(&mut self) -> Self::IterMut<'_> { std::iter::once((&self.0, &mut self.1)) @@ -716,7 +747,8 @@ impl Len for OptionSet { } } impl CollectionRef for OptionSet { - type ItemRef<'a> = &'a Self::Item + type ItemRef<'a> + = &'a Self::Item where Self: 'a; @@ -735,7 +767,8 @@ where } } impl CollectionMut for OptionSet { - type ItemMut<'a> = &'a mut T + type ItemMut<'a> + = &'a mut T where Self: 'a; @@ -751,16 +784,18 @@ where } } impl Iter for OptionSet { - type Iter<'a> = std::option::Iter<'a, T> - where - Self: 'a; + type Iter<'a> + = std::option::Iter<'a, T> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { self.0.iter() } } impl IterMut for OptionSet { - type IterMut<'a> = std::option::IterMut<'a, T> + type IterMut<'a> + = std::option::IterMut<'a, T> where Self: 'a; @@ -803,7 +838,8 @@ impl Len for OptionMap { } } impl CollectionRef for OptionMap { - type ItemRef<'a> = &'a Self::Item + type ItemRef<'a> + = &'a Self::Item where Self: 'a; @@ -825,7 +861,8 @@ where } } impl CollectionMut for OptionMap { - type ItemMut<'a> = &'a mut Self::Item + type ItemMut<'a> + = &'a mut Self::Item where Self: 'a; @@ -847,9 +884,10 @@ impl Keyed for OptionMap { type Key = K; } impl KeyedRef for OptionMap { - type KeyRef<'a> = &'a Self::Key - where - Self: 'a; + type KeyRef<'a> + = &'a Self::Key + where + Self: 'a; covariant_key_ref!(); } @@ -878,9 +916,10 @@ where } } impl Iter for OptionMap { - type Iter<'a> = std::option::IntoIter<&'a V> - where - Self: 'a; + type Iter<'a> + = std::option::IntoIter<&'a V> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { self.0.as_ref().map(|(_k, v)| v).into_iter() @@ -890,18 +929,20 @@ impl SimpleKeyedRef for OptionMap { simple_keyed_ref!(); } impl MapIter for OptionMap { - type Iter<'a> = std::option::IntoIter<(&'a K, &'a V)> - where - Self: 'a; + type Iter<'a> + = std::option::IntoIter<(&'a K, &'a V)> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { self.0.as_ref().map(|(k, v)| (k, v)).into_iter() } } impl MapIterMut for OptionMap { - type IterMut<'a> = std::option::IntoIter<(&'a K, &'a mut V)> - where - Self: 'a; + type IterMut<'a> + = std::option::IntoIter<(&'a K, &'a mut V)> + where + Self: 'a; fn iter_mut(&mut self) -> Self::IterMut<'_> { self.0.as_mut().map(|(k, v)| (&*k, v)).into_iter() @@ -945,7 +986,8 @@ impl Len for ArraySet { } } impl CollectionRef for ArraySet { - type ItemRef<'a> = &'a T + type ItemRef<'a> + = &'a T where Self: 'a; @@ -967,7 +1009,8 @@ where } } impl CollectionMut for ArraySet { - type ItemMut<'a> = &'a mut T + type ItemMut<'a> + = &'a mut T where Self: 'a; @@ -986,9 +1029,10 @@ where } } impl Iter for ArraySet { - type Iter<'a> = std::slice::Iter<'a, T> - where - Self: 'a; + type Iter<'a> + = std::slice::Iter<'a, T> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { self.0.iter() @@ -1035,7 +1079,8 @@ impl Len for ArrayMap { } } impl CollectionRef for ArrayMap { - type ItemRef<'a> = &'a Self::Item + type ItemRef<'a> + = &'a Self::Item where Self: 'a; @@ -1057,7 +1102,8 @@ where } } impl CollectionMut for ArrayMap { - type ItemMut<'a> = &'a mut Self::Item + type ItemMut<'a> + = &'a mut Self::Item where Self: 'a; @@ -1079,9 +1125,10 @@ impl Keyed for ArrayMap { type Key = K; } impl KeyedRef for ArrayMap { - type KeyRef<'a> = &'a Self::Key - where - Self: 'a; + type KeyRef<'a> + = &'a Self::Key + where + Self: 'a; covariant_key_ref!(); } @@ -1110,9 +1157,10 @@ where } } impl Iter for ArrayMap { - type Iter<'a> = std::slice::Iter<'a, V> - where - Self: 'a; + type Iter<'a> + = std::slice::Iter<'a, V> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { self.vals.iter() @@ -1122,18 +1170,20 @@ impl SimpleKeyedRef for ArrayMap { simple_keyed_ref!(); } impl MapIter for ArrayMap { - type Iter<'a> = std::iter::Zip, std::slice::Iter<'a, V>> - where - Self: 'a; + type Iter<'a> + = std::iter::Zip, std::slice::Iter<'a, V>> + where + Self: 'a; fn iter(&self) -> Self::Iter<'_> { self.keys.iter().zip(self.vals.iter()) } } impl MapIterMut for ArrayMap { - type IterMut<'a> = std::iter::Zip, std::slice::IterMut<'a, V>> - where - Self: 'a; + type IterMut<'a> + = std::iter::Zip, std::slice::IterMut<'a, V>> + where + Self: 'a; fn iter_mut(&mut self) -> Self::IterMut<'_> { self.keys.iter().zip(self.vals.iter_mut()) diff --git a/lattices/src/test.rs b/lattices/src/test.rs index 2b2068906c46..80306c7752ad 100644 --- a/lattices/src/test.rs +++ b/lattices/src/test.rs @@ -329,8 +329,8 @@ pub fn cartesian_power( (size, Some(size)) } } - impl<'a, T, const N: usize> ExactSizeIterator for CartesianPower<'a, T, N> {} - impl<'a, T, const N: usize> Clone for CartesianPower<'a, T, N> { + impl ExactSizeIterator for CartesianPower<'_, T, N> {} + impl Clone for CartesianPower<'_, T, N> { fn clone(&self) -> Self { Self { items: self.items, diff --git a/lattices/src/vec_union.rs b/lattices/src/vec_union.rs index 731f6009711f..e9ef26994348 100644 --- a/lattices/src/vec_union.rs +++ b/lattices/src/vec_union.rs @@ -98,13 +98,13 @@ where { fn eq(&self, other: &VecUnion) -> bool { if self.vec.len() != other.vec.len() { - return false; + false + } else { + self.vec + .iter() + .zip(other.vec.iter()) + .all(|(val_self, val_other)| val_self == val_other) } - return self - .vec - .iter() - .zip(other.vec.iter()) - .all(|(val_self, val_other)| val_self == val_other); } } diff --git a/stageleft/src/lib.rs b/stageleft/src/lib.rs index ff23a4c4963c..d0b40c64d1ee 100644 --- a/stageleft/src/lib.rs +++ b/stageleft/src/lib.rs @@ -102,7 +102,7 @@ pub struct BorrowBounds<'a> { _marker: PhantomData<&'a &'a mut ()>, } -impl<'a> QuotedContext for BorrowBounds<'a> { +impl QuotedContext for BorrowBounds<'_> { fn create() -> Self { BorrowBounds { _marker: PhantomData, @@ -242,9 +242,8 @@ impl< } impl< - 'a, T, - F: FnOnce(&mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T + 'a, + F: FnOnce(&mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T, > FreeVariable for F { fn to_tokens(self) -> (Option, Option) { diff --git a/variadics/src/lib.rs b/variadics/src/lib.rs index bd0de27a1a3f..ad08b9fe0a9b 100644 --- a/variadics/src/lib.rs +++ b/variadics/src/lib.rs @@ -209,7 +209,10 @@ where { const LEN: usize = 1 + Rest::LEN; - type Extend = (Item, Rest::Extend) where Suffix: VariadicExt; + type Extend + = (Item, Rest::Extend) + where + Suffix: VariadicExt; fn extend(self, suffix: Suffix) -> Self::Extend where Suffix: VariadicExt, @@ -232,7 +235,8 @@ where out2 } - type AsRefVar<'a> = (&'a Item, Rest::AsRefVar<'a>) + type AsRefVar<'a> + = (&'a Item, Rest::AsRefVar<'a>) where Self: 'a; fn as_ref_var(&self) -> Self::AsRefVar<'_> { @@ -240,7 +244,8 @@ where (item, rest.as_ref_var()) } - type AsMutVar<'a> = (&'a mut Item, Rest::AsMutVar<'a>) + type AsMutVar<'a> + = (&'a mut Item, Rest::AsMutVar<'a>) where Self: 'a; fn as_mut_var(&mut self) -> Self::AsMutVar<'_> { @@ -248,7 +253,8 @@ where (item, rest.as_mut_var()) } - type IterAnyRef<'a> = std::iter::Chain, Rest::IterAnyRef<'a>> + type IterAnyRef<'a> + = std::iter::Chain, Rest::IterAnyRef<'a>> where Self: 'static; fn iter_any_ref(&self) -> Self::IterAnyRef<'_> @@ -260,7 +266,8 @@ where std::iter::once(item).chain(rest.iter_any_ref()) } - type IterAnyMut<'a> = std::iter::Chain, Rest::IterAnyMut<'a>> + type IterAnyMut<'a> + = std::iter::Chain, Rest::IterAnyMut<'a>> where Self: 'static; fn iter_any_mut(&mut self) -> Self::IterAnyMut<'_> @@ -289,7 +296,10 @@ where impl VariadicExt for () { const LEN: usize = 0; - type Extend = Suffix where Suffix: VariadicExt; + type Extend + = Suffix + where + Suffix: VariadicExt; fn extend(self, suffix: Suffix) -> Self::Extend where Suffix: VariadicExt, @@ -307,7 +317,8 @@ impl VariadicExt for () { type AsMutVar<'a> = (); fn as_mut_var(&mut self) -> Self::AsMutVar<'_> {} - type IterAnyRef<'a> = std::iter::Empty<&'a dyn Any> + type IterAnyRef<'a> + = std::iter::Empty<&'a dyn Any> where Self: 'static; fn iter_any_ref(&self) -> Self::IterAnyRef<'_> @@ -317,7 +328,8 @@ impl VariadicExt for () { std::iter::empty() } - type IterAnyMut<'a> = std::iter::Empty<&'a mut dyn Any> + type IterAnyMut<'a> + = std::iter::Empty<&'a mut dyn Any> where Self: 'static; fn iter_any_mut(&mut self) -> Self::IterAnyMut<'_> @@ -820,7 +832,10 @@ where std::iter::zip(this, rest.into_zip()) } - type Drain<'a> = std::iter::Zip, Rest::Drain<'a>> where Self: 'a; + type Drain<'a> + = std::iter::Zip, Rest::Drain<'a>> + where + Self: 'a; fn drain(&mut self, range: R) -> Self::Drain<'_> where R: std::ops::RangeBounds + Clone, @@ -849,7 +864,10 @@ impl VecVariadic for var_type!() { std::iter::repeat(var_expr!()) } - type Drain<'a> = std::iter::Repeat where Self: 'a; + type Drain<'a> + = std::iter::Repeat + where + Self: 'a; fn drain(&mut self, _range: R) -> Self::Drain<'_> where R: std::ops::RangeBounds, diff --git a/variadics/tests/boxed_refed.rs b/variadics/tests/boxed_refed.rs index 17366b177dc5..8d7e44fc9f5c 100644 --- a/variadics/tests/boxed_refed.rs +++ b/variadics/tests/boxed_refed.rs @@ -29,7 +29,8 @@ impl Refed for (Item, Rest) where Rest: Refed, { - type Refed<'a> = (&'a Item, Rest::Refed<'a>) + type Refed<'a> + = (&'a Item, Rest::Refed<'a>) where Self: 'a; fn refed(&self) -> Self::Refed<'_> { From b96123369cec3d6d407af973f1896b2734fd92ef Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Mon, 28 Oct 2024 12:29:39 -0700 Subject: [PATCH 04/74] fix(hydroflow): cleanup temp tcp networking code, fix race condition fix #1458 (#1446) consolidate into one task to prevent races --- hydroflow/src/util/tcp.rs | 207 ++++++++++++++++++++++++-------------- 1 file changed, 130 insertions(+), 77 deletions(-) diff --git a/hydroflow/src/util/tcp.rs b/hydroflow/src/util/tcp.rs index 5f1cab36003b..9c50c3094160 100644 --- a/hydroflow/src/util/tcp.rs +++ b/hydroflow/src/util/tcp.rs @@ -1,16 +1,16 @@ #![cfg(not(target_arch = "wasm32"))] -use std::cell::RefCell; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::HashMap; +use std::fmt::Debug; use std::net::SocketAddr; -use std::pin::pin; -use std::rc::Rc; use futures::{SinkExt, StreamExt}; use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::{TcpListener, TcpSocket, TcpStream}; +use tokio::select; use tokio::task::spawn_local; +use tokio_stream::StreamMap; use tokio_util::codec::{ BytesCodec, Decoder, Encoder, FramedRead, FramedWrite, LengthDelimitedCodec, LinesCodec, }; @@ -74,69 +74,90 @@ pub type TcpFramedSink = Sender<(T, SocketAddr)>; pub type TcpFramedStream = Receiver::Item, SocketAddr), ::Error>>; +// TODO(mingwei): this temporary code should be replaced with a properly thought out networking system. /// Create a listening tcp socket, and then as new connections come in, receive their data and forward it to a queue. -pub async fn bind_tcp>( +pub async fn bind_tcp( endpoint: SocketAddr, codec: Codec, -) -> Result<(TcpFramedSink, TcpFramedStream, SocketAddr), std::io::Error> { +) -> Result<(TcpFramedSink, TcpFramedStream, SocketAddr), std::io::Error> +where + Item: 'static, + Codec: 'static + Clone + Decoder + Encoder, + >::Error: Debug, +{ let listener = TcpListener::bind(endpoint).await?; let bound_endpoint = listener.local_addr()?; - let (tx_egress, mut rx_egress) = unsync_channel(None); - let (tx_ingress, rx_ingress) = unsync_channel(None); - - let clients = Rc::new(RefCell::new(HashMap::new())); - - spawn_local({ - let clients = clients.clone(); - - async move { - while let Some((payload, addr)) = rx_egress.next().await { - let client = clients.borrow_mut().remove(&addr); - - if let Some(mut sender) = client { - let _ = SinkExt::send(&mut sender, payload).await; - clients.borrow_mut().insert(addr, sender); - } - } - } - }); + let (send_egress, mut recv_egress) = unsync_channel::<(Item, SocketAddr)>(None); + let (send_ingres, recv_ingres) = unsync_channel(None); spawn_local(async move { + let send_ingress = send_ingres; + // Map of `addr -> peers`, to send messages to. + let mut peers_send = HashMap::new(); + // `StreamMap` of `addr -> peers`, to receive messages from. Automatically removes streams + // when they disconnect. + let mut peers_recv = StreamMap::>::new(); + loop { - let (stream, peer_addr) = if let Ok((stream, _)) = listener.accept().await { - if let Ok(peer_addr) = stream.peer_addr() { - (stream, peer_addr) - } else { - continue; + // Calling methods in a loop, futures must be cancel-safe. + select! { + // `biased` means the cases will be prioritized in the order they are listed. + // First we accept any new connections + // This is not strictly neccessary, but lets us do our internal work (send outgoing + // messages) before accepting more work (receiving more messages, accepting new + // clients). + biased; + // Send outgoing messages. + msg_send = recv_egress.next() => { + let Some((payload, peer_addr)) = msg_send else { + // `None` if the send side has been dropped (no more send messages will ever come). + continue; + }; + let Some(stream) = peers_send.get_mut(&peer_addr) else { + tracing::warn!("Dropping message to non-connected peer: {}", peer_addr); + continue; + }; + if let Err(err) = SinkExt::send(stream, payload).await { + tracing::error!("IO or codec error sending message to peer {}, disconnecting: {:?}", peer_addr, err); + peers_send.remove(&peer_addr); // `Drop` disconnects. + }; } - } else { - continue; - }; - - let mut tx_ingress = tx_ingress.clone(); - - let (send, recv) = tcp_framed(stream, codec.clone()); - - // TODO: Using peer_addr here as the key is a little bit sketchy. - // It's possible that a client could send a message, disconnect, then another client connects from the same IP address (and the same src port), and then the response could be sent to that new client. - // This can be solved by using monotonically increasing IDs for each new client, but would break the similarity with the UDP versions of this function. - clients.borrow_mut().insert(peer_addr, send); - - spawn_local({ - let clients = clients.clone(); - async move { - let mapped = recv.map(|x| Ok(x.map(|x| (x, peer_addr)))); - let _ = tx_ingress.send_all(&mut pin!(mapped)).await; - - clients.borrow_mut().remove(&peer_addr); + // Receive incoming messages. + msg_recv = peers_recv.next(), if !peers_recv.is_empty() => { + // If `peers_recv` is empty then `next()` will immediately return `None` which + // would cause the loop to spin. + let Some((peer_addr, payload_result)) = msg_recv else { + continue; // => `peers_recv.is_empty()`. + }; + if let Err(err) = send_ingress.send(payload_result.map(|payload| (payload, peer_addr))).await { + tracing::error!("Error passing along received message: {:?}", err); + } } - }); + // Accept new clients. + new_peer = listener.accept() => { + let Ok((stream, _addr)) = new_peer else { + continue; + }; + let Ok(peer_addr) = stream.peer_addr() else { + continue; + }; + let (peer_send, peer_recv) = tcp_framed(stream, codec.clone()); + + // TODO: Using peer_addr here as the key is a little bit sketchy. + // It's possible that a peer could send a message, disconnect, then another peer connects from the + // same IP address (and the same src port), and then the response could be sent to that new client. + // This can be solved by using monotonically increasing IDs for each new peer, but would break the + // similarity with the UDP versions of this function. + peers_send.insert(peer_addr, peer_send); + peers_recv.insert(peer_addr, peer_recv); + } + } } }); - Ok((tx_egress, rx_ingress, bound_endpoint)) + Ok((send_egress, recv_ingres, bound_endpoint)) } /// The inverse of [`bind_tcp`]. @@ -144,37 +165,69 @@ pub async fn bind_tcp> /// When messages enqueued into the returned sender, tcp sockets will be created and connected as /// necessary to send out the requests. As the responses come back, they will be forwarded to the /// returned receiver. -pub fn connect_tcp>( - codec: Codec, -) -> (TcpFramedSink, TcpFramedStream) { - let (tx_egress, mut rx_egress) = unsync_channel(None); - let (tx_ingress, rx_ingress) = unsync_channel(None); +pub fn connect_tcp(codec: Codec) -> (TcpFramedSink, TcpFramedStream) +where + Item: 'static, + Codec: 'static + Clone + Decoder + Encoder, + >::Error: Debug, +{ + let (send_egress, mut recv_egress) = unsync_channel(None); + let (send_ingres, recv_ingres) = unsync_channel(None); spawn_local(async move { - let mut streams = HashMap::new(); + let send_ingres = send_ingres; + // Map of `addr -> peers`, to send messages to. + let mut peers_send = HashMap::new(); + // `StreamMap` of `addr -> peers`, to receive messages from. Automatically removes streams + // when they disconnect. + let mut peers_recv = StreamMap::new(); - while let Some((payload, addr)) = rx_egress.next().await { - let stream = match streams.entry(addr) { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => { - let socket = TcpSocket::new_v4().unwrap(); - let stream = socket.connect(addr).await.unwrap(); - - let (send, recv) = tcp_framed(stream, codec.clone()); - - let mut tx_ingress = tx_ingress.clone(); - spawn_local(async move { - let mapped = recv.map(|x| Ok(x.map(|x| (x, addr)))); - let _ = tx_ingress.send_all(&mut pin!(mapped)).await; - }); - - entry.insert(send) + loop { + // Calling methods in a loop, futures must be cancel-safe. + select! { + // `biased` means the cases will be prioritized in the order they are listed. + // This is not strictly neccessary, but lets us do our internal work (send outgoing + // messages) before accepting more work (receiving more messages). + biased; + // Send outgoing messages. + msg_send = recv_egress.next() => { + let Some((payload, peer_addr)) = msg_send else { + // `None` if the send side has been dropped (no more send messages will ever come). + continue; + }; + + let stream = match peers_send.entry(peer_addr) { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => { + let socket = TcpSocket::new_v4().unwrap(); + let stream = socket.connect(peer_addr).await.unwrap(); + + let (peer_send, peer_recv) = tcp_framed(stream, codec.clone()); + + peers_recv.insert(peer_addr, peer_recv); + entry.insert(peer_send) + } + }; + + if let Err(err) = stream.send(payload).await { + tracing::error!("IO or codec error sending message to peer {}, disconnecting: {:?}", peer_addr, err); + peers_send.remove(&peer_addr); // `Drop` disconnects. + } } - }; - - let _ = stream.send(payload).await; + // Receive incoming messages. + msg_recv = peers_recv.next(), if !peers_recv.is_empty() => { + // If `peers_recv` is empty then `next()` will immediately return `None` which + // would cause the loop to spin. + let Some((peer_addr, payload_result)) = msg_recv else { + continue; // => `peers_recv.is_empty()`. + }; + if let Err(err) = send_ingres.send(payload_result.map(|payload| (payload, peer_addr))).await { + tracing::error!("Error passing along received message: {:?}", err); + } + } + } } }); - (tx_egress, rx_ingress) + (send_egress, recv_ingres) } From f7e740fb2ba36d0fcf3fd196d60333552911e3a4 Mon Sep 17 00:00:00 2001 From: Joe Hellerstein Date: Mon, 28 Oct 2024 17:34:28 -0700 Subject: [PATCH 05/74] feat: generalized hash trie indexes for relational tuples (#1503) Generalized Hash Tries are part of the SIGMOD '23 FreeJoin [paper](https://dl.acm.org/doi/abs/10.1145/3589295) by Wang/Willsey/Suciu. They provide a compressed ("factorized") representation of relations. By operating in the factorized domain, join algorithms can defer cross-products and achieve asymptotically optimal performance. --------- Co-authored-by: Mingwei Samuel Co-authored-by: Andre Giron --- Cargo.lock | 16 + Cargo.toml | 1 + .../surface_examples__example_1_simplest.snap | 1 - ...phism__cartesian_product@graphvis_dot.snap | 1 - ...m__cartesian_product@graphvis_mermaid.snap | 1 - ...eton__fold_singleton@graphvis_mermaid.snap | 1 - ...ce_singleton__multi_tick@graphvis_dot.snap | 1 - ...ingleton__multi_tick@graphvis_mermaid.snap | 1 - hydroflow/tests/surface_lattice_bimorphism.rs | 52 + .../surface_lattice_generalized_hash_trie.rs | 73 ++ .../src/graph/ops/anti_join_multiset.rs | 5 +- lattices/Cargo.toml | 3 + lattices/src/ght/colt.rs | 306 ++++++ lattices/src/ght/lattice.rs | 439 ++++++++ lattices/src/ght/macros.rs | 117 +++ lattices/src/ght/mod.rs | 545 ++++++++++ lattices/src/ght/test.rs | 943 ++++++++++++++++++ lattices/src/lib.rs | 3 +- .../compile-fail/non_lattice_field.stderr | 12 +- variadics/src/variadic_collections.rs | 34 +- variadics_macro/CHANGELOG.md | 0 variadics_macro/Cargo.toml | 22 + variadics_macro/README.md | 17 + variadics_macro/src/lib.rs | 47 + 24 files changed, 2612 insertions(+), 29 deletions(-) create mode 100644 hydroflow/tests/surface_lattice_generalized_hash_trie.rs create mode 100644 lattices/src/ght/colt.rs create mode 100644 lattices/src/ght/lattice.rs create mode 100644 lattices/src/ght/macros.rs create mode 100644 lattices/src/ght/mod.rs create mode 100644 lattices/src/ght/test.rs create mode 100644 variadics_macro/CHANGELOG.md create mode 100644 variadics_macro/Cargo.toml create mode 100644 variadics_macro/README.md create mode 100644 variadics_macro/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index bc00ec71c754..a53092a9c76c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1599,9 +1599,12 @@ version = "0.5.7" dependencies = [ "cc-traits", "lattices_macro", + "ref-cast", "sealed", "serde", "trybuild", + "variadics", + "variadics_macro", ] [[package]] @@ -3452,6 +3455,19 @@ dependencies = [ "trybuild", ] +[[package]] +name = "variadics_macro" +version = "0.5.5" +dependencies = [ + "insta", + "prettyplease", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.75", + "variadics", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 668519191c00..8b9deaa71ad9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ members = [ "stageleft_tool", "topolotree", "variadics", + "variadics_macro", "website_playground", ] diff --git a/hydroflow/tests/snapshots/surface_examples__example_1_simplest.snap b/hydroflow/tests/snapshots/surface_examples__example_1_simplest.snap index fa30505c69cd..065e174f4cb3 100644 --- a/hydroflow/tests/snapshots/surface_examples__example_1_simplest.snap +++ b/hydroflow/tests/snapshots/surface_examples__example_1_simplest.snap @@ -12,4 +12,3 @@ Hello 6 Hello 7 Hello 8 Hello 9 - diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_dot.snap index e8a4194cc706..dae12d839db5 100644 --- a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_dot.snap +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_dot.snap @@ -81,4 +81,3 @@ digraph { } } } - diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_mermaid.snap index 9a8e0c4a1c46..7b6a31512589 100644 --- a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_mermaid.snap +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_mermaid.snap @@ -67,4 +67,3 @@ subgraph sg_4v1 ["sg_4v1 stratum 1"] 9v1 end end - diff --git a/hydroflow/tests/snapshots/surface_singleton__fold_singleton@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_singleton__fold_singleton@graphvis_mermaid.snap index cfc332878447..3775b3febceb 100644 --- a/hydroflow/tests/snapshots/surface_singleton__fold_singleton@graphvis_mermaid.snap +++ b/hydroflow/tests/snapshots/surface_singleton__fold_singleton@graphvis_mermaid.snap @@ -53,4 +53,3 @@ subgraph sg_3v1 ["sg_3v1 stratum 1"] 3v1 end end - diff --git a/hydroflow/tests/snapshots/surface_singleton__multi_tick@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_singleton__multi_tick@graphvis_dot.snap index 74d09d7f66a3..3a4356ee0bc7 100644 --- a/hydroflow/tests/snapshots/surface_singleton__multi_tick@graphvis_dot.snap +++ b/hydroflow/tests/snapshots/surface_singleton__multi_tick@graphvis_dot.snap @@ -61,4 +61,3 @@ digraph { } } } - diff --git a/hydroflow/tests/snapshots/surface_singleton__multi_tick@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_singleton__multi_tick@graphvis_mermaid.snap index 54cd4484ba3c..3a8ee979cc01 100644 --- a/hydroflow/tests/snapshots/surface_singleton__multi_tick@graphvis_mermaid.snap +++ b/hydroflow/tests/snapshots/surface_singleton__multi_tick@graphvis_mermaid.snap @@ -53,4 +53,3 @@ subgraph sg_2v1 ["sg_2v1 stratum 0"] 3v1 end end - diff --git a/hydroflow/tests/surface_lattice_bimorphism.rs b/hydroflow/tests/surface_lattice_bimorphism.rs index 401b0953018f..30ff17165576 100644 --- a/hydroflow/tests/surface_lattice_bimorphism.rs +++ b/hydroflow/tests/surface_lattice_bimorphism.rs @@ -2,9 +2,14 @@ use std::collections::{HashMap, HashSet}; use hydroflow::util::collect_ready; use hydroflow::{assert_graphvis_snapshots, hydroflow_syntax}; +use lattices::ght::lattice::{DeepJoinLatticeBimorphism, GhtBimorphism}; +use lattices::ght::GeneralizedHashTrieNode; use lattices::map_union::{KeyedBimorphism, MapUnionHashMap, MapUnionSingletonMap}; use lattices::set_union::{CartesianProductBimorphism, SetUnionHashSet, SetUnionSingletonSet}; +use lattices::GhtType; use multiplatform_test::multiplatform_test; +use variadics::variadic_collections::VariadicHashSet; +use variadics::CloneVariadic; #[multiplatform_test] pub fn test_cartesian_product() { @@ -134,3 +139,50 @@ pub fn test_cartesian_product_tick_state() { &*collect_ready::, _>(&mut out_recv) ); } + +#[test] +fn test_ght_join_bimorphism() { + type MyGhtATrie = GhtType!(u32, u64, u16 => &'static str: VariadicHashSet); + type MyGhtBTrie = GhtType!(u32, u64, u16 => &'static str: VariadicHashSet); + + type JoinSchema = variadics::var_type!(u32, u64, u16, &'static str, &'static str); + + type MyNodeBim = <(MyGhtATrie, MyGhtBTrie) as DeepJoinLatticeBimorphism< + VariadicHashSet, + >>::DeepJoinLatticeBimorphism; + type MyBim = GhtBimorphism; + + let mut hf = hydroflow_syntax! { + lhs = source_iter([ + var_expr!(123, 2, 5, "hello"), + var_expr!(50, 1, 1, "hi"), + var_expr!(5, 1, 7, "hi"), + var_expr!(5, 1, 7, "bye"), + ]) + -> map(|row| MyGhtATrie::new_from([row])) + -> state::<'tick, MyGhtATrie>(); + rhs = source_iter([ + var_expr!(5, 1, 8, "hi"), + var_expr!(5, 1, 7, "world"), + var_expr!(5, 1, 7, "folks"), + var_expr!(10, 1, 2, "hi"), + var_expr!(12, 10, 98, "bye"), + ]) + -> map(|row| MyGhtBTrie::new_from([row])) + -> state::<'tick, MyGhtBTrie>(); + + lhs[items] -> [0]my_join; + rhs[items] -> [1]my_join; + + + my_join = lattice_bimorphism(MyBim::default(), #lhs, #rhs) + -> lattice_reduce() + -> enumerate() + -> inspect(|x| println!("{:?} {:#?}", context.current_tick(), x)) + -> flat_map(|(_num, ght)| ght.recursive_iter().map(::clone_ref_var).collect::>()) + -> null(); + // -> for_each(|x| println!("{:#?}\n", x)); + }; + // hf.meta_graph().unwrap().open_mermaid(&Default::default()); + hf.run_available(); +} diff --git a/hydroflow/tests/surface_lattice_generalized_hash_trie.rs b/hydroflow/tests/surface_lattice_generalized_hash_trie.rs new file mode 100644 index 000000000000..ece19b084ab9 --- /dev/null +++ b/hydroflow/tests/surface_lattice_generalized_hash_trie.rs @@ -0,0 +1,73 @@ +use hydroflow::hydroflow_syntax; +use hydroflow::lattices::ght::lattice::{DeepJoinLatticeBimorphism, GhtBimorphism}; +use hydroflow::lattices::ght::GeneralizedHashTrieNode; +use hydroflow::lattices::GhtType; +use hydroflow::util::collect_ready; +use hydroflow::variadics::{var_expr, var_type}; +use variadics::variadic_collections::VariadicHashSet; // Import the Insert trait + +#[test] +fn test_basic() { + type MyGht = GhtType!(u16, u32 => u64: VariadicHashSet); + type FlatTup = var_type!(u16, u32, u64); + let input: Vec = vec![ + var_expr!(42, 314, 43770), + var_expr!(42, 315, 43770), + var_expr!(42, 314, 30619), + var_expr!(43, 10, 600), + ]; + let mut merged = MyGht::default(); + for i in input.clone() { + merged.insert(i); + } + println!("merged: {:?}", merged); + let mut df = hydroflow_syntax! { + source_iter(input) + -> map(|t| MyGht::new_from(vec![t])) + -> lattice_fold::<'static>(MyGht::default) + -> inspect(|t| println!("{:?}", t)) + -> assert(|x: &MyGht| x.eq(&merged)) + -> null(); + }; + df.run_available(); +} + +#[test] +fn test_join() { + type MyGht = GhtType!(u8 => u16: VariadicHashSet); + type ResultGht = GhtType!(u8 => u16, u16: VariadicHashSet); + let (out_send, out_recv) = hydroflow::util::unbounded_channel::<_>(); + + let r = vec![ + var_expr!(1, 10), + var_expr!(2, 20), + var_expr!(3, 30), + var_expr!(4, 40), + ]; + let s = vec![var_expr!(1, 10), var_expr!(5, 50)]; + + type MyNodeBim = <(MyGht, MyGht) as DeepJoinLatticeBimorphism< + VariadicHashSet, + >>::DeepJoinLatticeBimorphism; + type MyBim = GhtBimorphism; + + let mut df = hydroflow_syntax! { + R = source_iter(r) + -> map(|t| MyGht::new_from([t])) + -> state::(); + S = source_iter(s) + -> map(|t| MyGht::new_from([t])) + -> state::(); + R[items] -> [0]my_join; + S[items] -> [1]my_join; + my_join = lattice_bimorphism(MyBim::default(), #R, #S) + -> lattice_reduce() + -> for_each(|x| out_send.send(x).unwrap()); + }; + df.run_available(); + + assert_eq!( + &[ResultGht::new_from(vec![var_expr!(1, 10, 10),])], + &*collect_ready::, _>(out_recv) + ); +} diff --git a/hydroflow_lang/src/graph/ops/anti_join_multiset.rs b/hydroflow_lang/src/graph/ops/anti_join_multiset.rs index dbb18358b86c..cf9a1e9441d4 100644 --- a/hydroflow_lang/src/graph/ops/anti_join_multiset.rs +++ b/hydroflow_lang/src/graph/ops/anti_join_multiset.rs @@ -2,9 +2,8 @@ use quote::{quote_spanned, ToTokens}; use syn::parse_quote; use super::{ - DelayType, OpInstGenerics, OperatorCategory, OperatorConstraints, - OperatorInstance, OperatorWriteOutput, Persistence, PortIndexValue, WriteContextArgs, RANGE_0, - RANGE_1, + DelayType, OpInstGenerics, OperatorCategory, OperatorConstraints, OperatorInstance, + OperatorWriteOutput, Persistence, PortIndexValue, WriteContextArgs, RANGE_0, RANGE_1, }; use crate::diagnostic::{Diagnostic, Level}; diff --git a/lattices/Cargo.toml b/lattices/Cargo.toml index b4daacad7ee7..7684d2ca3cfb 100644 --- a/lattices/Cargo.toml +++ b/lattices/Cargo.toml @@ -19,6 +19,9 @@ cc-traits = "2.0.0" sealed = "0.5.0" serde = { version = "1.0.197", features = ["derive"], optional = true } lattices_macro = { path = "../lattices_macro", version = "^0.5.6" } +ref-cast = "1.0.23" +variadics = { path = "../variadics", version = "^0.0.6" } +variadics_macro = { path = "../variadics_macro", version = "^0.5.5" } [dev-dependencies] trybuild = "1.0.0" diff --git a/lattices/src/ght/colt.rs b/lattices/src/ght/colt.rs new file mode 100644 index 000000000000..b4b22239cd81 --- /dev/null +++ b/lattices/src/ght/colt.rs @@ -0,0 +1,306 @@ +//! COLT from Wang/Willsey/Suciu + +use std::hash::Hash; + +use variadics::variadic_collections::VariadicCollection; +use variadics::{var_expr, var_type, PartialEqVariadic, SplitBySuffix, VariadicExt}; + +use crate::ght::{GeneralizedHashTrieNode, GhtGet, GhtInner, GhtLeaf}; + +/// Data structure design for our COLT is unique. +/// +/// In the paper, the COLT is an unbalanced trie that "grows upward" from leaves lazily +/// on access via the `force` method. +/// Unfortunately, unbalanced tries break our types: a node's type to be defined via the +/// type of its children, recursively -- meaning all paths need to be the same type (and length)! +/// +/// To work around this, our COLT is a variadic *list* GHTs (a forest) of increasing height, +/// starting with a trie of height 0 and continuing until a trie of height |key| - 1. +/// Our `force` method does not add a node above a leaf L as in the paper. Instead +/// it `take`s L from the current trie and merges it into the next trie to the right which is 1 taller. +// +/// The following trait provides the behavior we need from the nodes in a COLT forest. Every +/// `ColtForestNode` is a `GeneralizedHashTrieNode` with some extra methods. +pub trait ColtForestNode: GeneralizedHashTrieNode { + /// result of `force`ing a node + type Force: GeneralizedHashTrieNode; + + /// Force the generation of a parent node, as in the Wang/Willsey/Suciu COLT structure, + /// to be merged into the next trie to the right. + fn force(self) -> Option; + + /// Force the generation of a parent node but retain ref to this node + fn force_drain(&mut self) -> Option; +} + +// Force only acts on leaves +impl ColtForestNode for GhtInner +where + Head: 'static + Hash + Eq + Clone, + Node: 'static + ColtForestNode, + ::Schema: + SplitBySuffix::SuffixSchema)>, +{ + type Force = Node; // where Node:GeneralizedHashTrieNode; + fn force(self) -> Option { + None + } + + fn force_drain(&mut self) -> Option { + None + } +} + +// Leaf case +impl ColtForestNode + for GhtLeaf +where + Head: 'static + Clone + Hash + Eq, + Rest: 'static + Clone + Hash + Eq + VariadicExt, + Schema: 'static + Hash + Eq + Clone + VariadicExt + PartialEqVariadic, + Rest: PartialEqVariadic, + Schema: SplitBySuffix, + Schema: SplitBySuffix, + >::Prefix: Eq + Hash + Clone, + >::Prefix: Eq + Hash + Clone, + Storage: VariadicCollection + Default + IntoIterator, + GhtLeaf: GeneralizedHashTrieNode, + GhtInner>: + GeneralizedHashTrieNode, +{ + type Force = GhtInner>; + fn force(mut self) -> Option { + let mut retval = Self::Force::default(); + self.forced = true; + for row in self.into_iter().unwrap() { + retval.insert(row); + } + Some(retval) + } + + fn force_drain(&mut self) -> Option>> { + let mut retval = Self::Force::default(); + self.forced = true; + for row in self.elements.drain() { + retval.insert(row); + } + Some(retval) + } +} + +/// Emulate the `get` and iter` functions for a single Ght node +/// [`GhtGet`] across a forest of ColtForestNodes. +/// +/// The "current" ColtGet node (corresponding to the "current" GhtGet node) at depth +/// d from the root is a variadic list of nodes, each at depth d in its their +/// respective trie in the forest, Tries of height d or smaller are omitted, +/// hence the first element in any ColtGet is a GhtLeaf. +pub trait ColtGet { + /// Schema variadic: the schema of the relation stored in this COLT. + /// This type is the same in all Tries and nodes of the COLT. + type Schema: VariadicExt + Eq + Hash + Clone; + /// The type of Storage + /// This type is the same in all Tries and nodes of the COLT + type Storage: VariadicCollection; + /// SuffixSchema variadic: the suffix of the schema *from this node of the trie + /// downward*. The first entry in this variadic is of type Head. + /// This type is the same in all Tries of the COLT (but changes as we traverse downward) + type SuffixSchema: VariadicExt + Eq + Hash + Clone; + /// The type of the first column in the SuffixSchema + /// This type is the same in all Tries of the COLT (but changes as we traverse downward) + type Head: Eq + Hash; + + /// Type returned by [`Self::get`]. + type Get; + + /// Following the spec in Wang/Willsey/Suciu, on an Inner node this retrieves the value + /// (child) associated with the given "head" key. It returns an `Option` containing a + /// reference to the value if found, or `None` if not found. + /// On a Leaf node, returns None. + fn get(self, head: &Self::Head) -> Self::Get; + + /// Iterator for the "head" keys (from inner nodes) or nothing (from leaf nodes). + fn iter(&self) -> impl Iterator; +} + +/// `ColtGet` without the first (head) trie. +pub trait ColtGetTail: ColtGet { + /// merge an inner node into the head of this tail of the forest + fn merge(&mut self, inner_to_merge: InnerToMerge); +} + +impl<'a, Rest, Schema, SuffixSchema, Storage> ColtGet for var_type!(&'a mut GhtLeaf, ...Rest) +where + Rest: ColtGetTail< + as ColtForestNode>::Force, + Storage = Storage, + >, + ::SuffixSchema: 'a, + GhtLeaf: ColtForestNode, + Schema: Clone + Hash + Eq + VariadicExt, + SuffixSchema: Clone + Hash + Eq + VariadicExt, + Storage: VariadicCollection, +{ + type Schema = Schema; + type Head = Rest::Head; + type SuffixSchema = SuffixSchema; + type Get = Rest::Get; + type Storage = Rest::Storage; + + fn get(self, head: &Self::Head) -> Self::Get { + let (first, mut rest) = self; + let forced = first.force_drain().unwrap(); + ColtGetTail::merge(&mut rest, forced); + Rest::get(rest, head) + } + + fn iter(&self) -> impl Iterator { + std::iter::empty() + } +} + +// we only merge in GhtInner> nodes, so this +// should never be called. +impl<'a, Rest, Schema, SuffixSchema, T, Storage> ColtGetTail for var_type!(&'a mut GhtLeaf, ...Rest) +where + Rest: ColtGetTail< + as ColtForestNode>::Force, + Storage = Storage, + >, + ::SuffixSchema: 'a, + GhtLeaf: ColtForestNode, + Schema: Clone + Hash + Eq + VariadicExt, + SuffixSchema: Clone + Hash + Eq + VariadicExt, + Storage: VariadicCollection, +{ + fn merge(&mut self, _inner_to_merge: T) { + panic!(); + } +} + +impl<'a, Head, Head2, Rest, Node> ColtGet for var_type!(&'a mut GhtInner>, ...Rest) +where + Rest: ColtGet, + Head: Eq + Hash + Clone, + Head2: Eq + Hash + Clone, + Node: GeneralizedHashTrieNode, + GhtInner>: GeneralizedHashTrieNode< + Head = Rest::Head, + SuffixSchema = Rest::SuffixSchema, + Schema = Rest::Schema, + Storage = Rest::Storage, + >, + GhtInner: GeneralizedHashTrieNode, +{ + type Schema = Rest::Schema; + type Head = Rest::Head; + type SuffixSchema = Rest::SuffixSchema; + type Get = var_type!(&'a mut GhtInner, ...Rest::Get); + type Storage = Rest::Storage; + + fn get(self, head: &Self::Head) -> Self::Get { + let (first, rest) = self; + // create a child entry here for this get, to absorb future forces + // TODO(mingwei): extra clone here if entry already exists. + let child = first.children.entry(head.clone()).or_default(); + var_expr!(child, ...Rest::get(rest, head)) + } + + fn iter(&self) -> impl Iterator { + self.0.children.keys().cloned().chain(Rest::iter(&self.1)) + } +} + +impl<'a, Head, Rest, Schema, ValType, Storage> ColtGet for var_type!(&'a mut GhtInner>, ...Rest) +where + Rest: ColtGet, + Head: Eq + Hash + Clone, + Schema: Eq + Hash + Clone + PartialEqVariadic, + ValType: Eq + Hash + Clone + PartialEqVariadic, + Storage: VariadicCollection, + GhtLeaf: GeneralizedHashTrieNode, + Schema: 'static + Eq + VariadicExt + Hash + Clone + SplitBySuffix + PartialEqVariadic, + >::Prefix: Eq + Hash + Clone, + GhtInner>: + GeneralizedHashTrieNode + GhtGet, + GhtInner>: + GeneralizedHashTrieNode, + GhtLeaf: + GeneralizedHashTrieNode + GhtGet, +{ + type Schema = Rest::Schema; + type Head = Rest::Head; + type SuffixSchema = Rest::SuffixSchema; + type Get = var_type!(&'a mut GhtLeaf, ...Rest::Get); + type Storage = Rest::Storage; + + fn get(self, head: &Self::Head) -> Self::Get { + let (first, rest) = self; + let child = first.children.entry(head.clone()).or_default(); + var_expr!(child, ...Rest::get(rest, head)) + } + + fn iter(&self) -> impl Iterator { + self.0.children.keys().cloned().chain(Rest::iter(&self.1)) + } +} + +impl<'a, Head, Rest, Schema, ValType, Storage> + ColtGetTail>> for var_type!(&'a mut GhtInner>, ...Rest) +where + Rest: ColtGet, + Head: Eq + Hash + Clone, + Schema: Eq + Hash + Clone + PartialEqVariadic, + ValType: Eq + Hash + Clone + PartialEqVariadic, + Storage: VariadicCollection, + var_type!(&'a mut GhtInner>, ...Rest): + ColtGet, + GhtLeaf: GeneralizedHashTrieNode, + Schema: 'static + Eq + VariadicExt + Hash + Clone + SplitBySuffix + PartialEqVariadic, + >::Prefix: Eq + Hash + Clone, + GhtInner>: + GeneralizedHashTrieNode + GhtGet, +{ + fn merge(&mut self, inner_to_merge: GhtInner>) { + let (head, _rest) = self; + // can't use Merge with COLT bc columnstore is not a lattice!! + head.merge_node(inner_to_merge); + } +} + +impl<'a, Head, Node> ColtGet for var_type!(&'a mut GhtInner) +where + GhtInner: GeneralizedHashTrieNode, + Head: Clone + Eq + Hash, + Node: GeneralizedHashTrieNode, +{ + type Schema = as GeneralizedHashTrieNode>::Schema; + type SuffixSchema = as GeneralizedHashTrieNode>::SuffixSchema; + type Head = Head; + type Get = var_type!(&'a mut Node); + type Storage = Node::Storage; + + fn get(self, head: &Self::Head) -> Self::Get { + let child = self.0.children.entry(head.clone()).or_default(); + var_expr!(child) + } + + fn iter(&self) -> impl Iterator { + self.0.children.keys().cloned() + } +} +impl ColtGetTail>> for var_type!(&mut GhtInner>) +where + GhtInner>: + GeneralizedHashTrieNode + GhtGet, + GhtLeaf: GeneralizedHashTrieNode, + Head: Clone + Eq + Hash, + Schema: Clone + Eq + Hash + VariadicExt, + Storage: VariadicCollection, +{ + fn merge(&mut self, inner_to_merge: GhtInner>) { + let (head, _rest) = self; + // can't use Merge with COLT bc columnstore is not a lattice!! + head.merge_node(inner_to_merge); + } +} diff --git a/lattices/src/ght/lattice.rs b/lattices/src/ght/lattice.rs new file mode 100644 index 000000000000..11b8a6aabdec --- /dev/null +++ b/lattices/src/ght/lattice.rs @@ -0,0 +1,439 @@ +//! Lattice traits for GHT + +use core::cmp::Ordering::{Equal, Greater, Less}; +use std::cmp::Ordering; +use std::collections::HashMap; +use std::hash::Hash; + +use variadics::variadic_collections::VariadicSet; +use variadics::{var_expr, var_type, CloneVariadic, PartialEqVariadic, SplitBySuffix, VariadicExt}; + +use crate::ght::{GeneralizedHashTrieNode, GhtGet, GhtInner, GhtLeaf}; +use crate::{IsBot, IsTop, LatticeBimorphism, LatticeOrd, Merge}; + +impl Merge> for GhtInner +where + Node: GeneralizedHashTrieNode + Merge, + Node::Storage: VariadicSet, // multiset is not a lattice! + Self: GeneralizedHashTrieNode, + Head: Hash + Eq + Clone, +{ + fn merge(&mut self, other: GhtInner) -> bool { + let mut changed = false; + + for (k, v) in other.children { + match self.children.entry(k) { + std::collections::hash_map::Entry::Occupied(mut occupied) => { + changed |= occupied.get_mut().merge_node(v); + } + std::collections::hash_map::Entry::Vacant(vacant) => { + vacant.insert(v); + changed = true; + } + } + } + changed + } +} + +impl Merge> + for GhtLeaf +where + Schema: Eq + Hash, + Storage: VariadicSet + Extend + IntoIterator, +{ + fn merge(&mut self, other: GhtLeaf) -> bool { + let old_len = self.elements.len(); + self.elements.extend(other.elements); + self.elements.len() > old_len + } +} + +impl PartialEq> for GhtInner +where + Head: Hash + Eq + 'static + Clone, + Node: GeneralizedHashTrieNode + 'static + PartialEq, + Node::Storage: VariadicSet, // multiset is not a lattice! + Node::Schema: SplitBySuffix, + GhtInner: GhtGet, + as GhtGet>::Get: PartialEq, +{ + fn eq(&self, other: &GhtInner) -> bool { + if self.children.len() != other.children.len() { + return false; + } + + for head in self.iter() { + let other_node = other.get(&head); + if other_node.is_none() { + return false; + } + let this_node = self.get(&head); + if this_node.is_none() { + return false; + } + if this_node.unwrap() != other_node.unwrap() { + return false; + } + } + true + } +} + +impl PartialOrd> for GhtInner +where + Head: Hash + Eq + 'static + Clone, + Node: 'static + GeneralizedHashTrieNode + PartialEq + PartialOrd, + Node::Storage: VariadicSet, // multiset is not a lattice! + Node::Schema: SplitBySuffix, +{ + fn partial_cmp(&self, other: &GhtInner) -> Option { + let mut self_any_greater = false; + let mut other_any_greater = false; + if self.children.is_empty() && other.children.is_empty() { + Some(Equal) + } else { + for k in self.children.keys().chain(other.children.keys()) { + match (self.children.get(k), other.children.get(k)) { + (Some(self_value), Some(other_value)) => { + match self_value.partial_cmp(other_value)? { + Greater => { + self_any_greater = true; + } + Less => { + other_any_greater = true; + } + Equal => {} + } + } + (Some(_), None) => { + self_any_greater = true; + } + (None, Some(_)) => { + other_any_greater = true; + } + (None, None) => unreachable!(), + } + } + match (self_any_greater, other_any_greater) { + (true, false) => Some(Greater), + (false, true) => Some(Less), + (false, false) => Some(Equal), + (true, true) => unreachable!(), + } + } + } +} + +impl PartialOrd> + for GhtLeaf +where + Schema: Eq + Hash + PartialEqVariadic, + SuffixSchema: Eq + Hash, + Storage: VariadicSet + PartialEq, +{ + fn partial_cmp(&self, other: &GhtLeaf) -> Option { + match self.elements.len().cmp(&other.elements.len()) { + Greater => { + if other.elements.iter().all(|tup| self.elements.contains(tup)) { + Some(Greater) + } else { + None + } + } + Equal => { + if self + .elements + .iter() + .all(|head| other.elements.contains(head)) + { + Some(Equal) + } else { + None + } + } + Less => { + if self + .elements + .iter() + .all(|head| other.elements.contains(head)) + { + Some(Less) + } else { + None + } + } + } + } +} + +impl LatticeOrd> for GhtInner +where + Self: PartialOrd>, + Head: Clone, + Node: GeneralizedHashTrieNode, + Node::Storage: VariadicSet, // multiset is not a lattice! +{ +} +impl LatticeOrd> + for GhtLeaf +where + Schema: Eq + Hash + PartialEqVariadic, + SuffixSchema: Eq + Hash, + Storage: VariadicSet + PartialEq, +{ +} + +impl IsBot for GhtInner +where + Head: Clone, + Node: GeneralizedHashTrieNode + IsBot, +{ + fn is_bot(&self) -> bool { + self.children.iter().all(|(_, v)| v.is_bot()) + } +} + +impl IsBot for GhtLeaf +where + Schema: Eq + Hash, + SuffixSchema: Eq + Hash, + Storage: VariadicSet, +{ + fn is_bot(&self) -> bool { + self.elements.is_empty() + } +} + +impl IsTop for GhtInner +where + Head: Clone, + Node: GeneralizedHashTrieNode, + Node::Storage: VariadicSet, // multiset is not a lattice! +{ + fn is_top(&self) -> bool { + false + } +} + +impl IsTop for GhtLeaf +where + Schema: Eq + Hash, + SuffixSchema: Eq + Hash, + Storage: VariadicSet, +{ + fn is_top(&self) -> bool { + false + } +} + +////////////////////////// +// BiMorphisms for GHT +// + +/// Bimorphism for the cartesian product of two GHT *subtries*. +/// +/// Output is a set of all possible pairs of +/// *suffixes* from the two subtries. If you use this at the root of a GHT, it's a full cross-product. +/// If you use this at an internal node, it provides a 'factorized' representation with only the suffix +/// cross-products expanded. +pub struct GhtCartesianProductBimorphism { + _phantom: std::marker::PhantomData GhtOut>, +} +impl Default for GhtCartesianProductBimorphism { + fn default() -> Self { + Self { + _phantom: Default::default(), + } + } +} +impl<'a, 'b, GhtA, GhtB, GhtOut> LatticeBimorphism<&'a GhtA, &'b GhtB> + for GhtCartesianProductBimorphism +where + GhtA: GeneralizedHashTrieNode, + GhtA::Storage: VariadicSet, // multiset is not a lattice! + GhtB: GeneralizedHashTrieNode, + GhtB::Storage: VariadicSet, // multiset is not a lattice! + GhtOut: FromIterator, + GhtA::SuffixSchema: CloneVariadic, + GhtB::SuffixSchema: CloneVariadic, +{ + type Output = GhtOut; + + fn call(&mut self, ght_a: &'a GhtA, ght_b: &'b GhtB) -> Self::Output { + ght_a.recursive_iter().flat_map(|a| { + let (_a_prefix, a_suffix) = >::split_by_suffix_ref(a); + ght_b + .recursive_iter() + .map(move |b| { + let (_b_prefix, b_suffix) = >::split_by_suffix_ref(b); + var_expr!(...::clone_ref_var(a_suffix), ...::clone_ref_var(b_suffix)) + }) + }).collect() + } +} + +/// Forms the cartesian product of the ValTypes only +/// Used on GhtLeaf nodes to implement DeepJoinLatticeBimorphism +pub struct GhtValTypeProductBimorphism { + _phantom: std::marker::PhantomData GhtOut>, +} +impl Default for GhtValTypeProductBimorphism { + fn default() -> Self { + Self { + _phantom: Default::default(), + } + } +} +impl<'a, 'b, GhtA, GhtB, GhtOut> LatticeBimorphism<&'a GhtA, &'b GhtB> + for GhtValTypeProductBimorphism +where + GhtA: GeneralizedHashTrieNode, + GhtA::Storage: VariadicSet, // multiset is not a lattice! + GhtB: GeneralizedHashTrieNode, + GhtB::Storage: VariadicSet, // multiset is not a lattice! + GhtOut: FromIterator, + GhtA::Schema: Eq + Hash + CloneVariadic, + GhtB::Schema: Eq + Hash + SplitBySuffix, + GhtB::ValType: CloneVariadic, +{ + type Output = GhtOut; + + fn call(&mut self, ght_a: &'a GhtA, ght_b: &'b GhtB) -> Self::Output { + ght_a.recursive_iter().flat_map(|a| { + ght_b + .recursive_iter() + .map(move |b| { + let (_prefix_b, suffix_b) + = >::split_by_suffix_ref(b); + var_expr!(...::clone_ref_var(a), ...::clone_ref_var(suffix_b)) + } + ) + }).collect() + } +} + +/// Composable bimorphism, wraps an existing morphism by partitioning it per key. +/// +/// For example, `GhtKeyedBimorphism<..., GhtCartesianProduct<...>>` is a join. +#[derive(Default)] +pub struct GhtBimorphism { + bimorphism: Bimorphism, + // _phantom: std::marker::PhantomData MapOut>, +} +impl GhtBimorphism { + /// Create a `KeyedBimorphism` using `bimorphism` for handling values. + pub fn new(bimorphism: Bimorphism) -> Self { + Self { + bimorphism, + // _phantom: std::marker::PhantomData, + } + } +} + +impl LatticeBimorphism for GhtBimorphism +where + GhtA: GeneralizedHashTrieNode, + GhtA::Storage: VariadicSet, // multiset is not a lattice! + GhtB: GeneralizedHashTrieNode, + GhtB::Storage: VariadicSet, // multiset is not a lattice! + GhtOut: GeneralizedHashTrieNode, // FromIterator, + for<'a, 'b> ValFunc: LatticeBimorphism<&'a GhtA, &'b GhtB, Output = GhtOut>, +{ + type Output = GhtOut; + + fn call(&mut self, ght_a: GhtA, ght_b: GhtB) -> Self::Output { + let node_bim = &mut self.bimorphism; // GhtNodeKeyedBimorphism::::new(self.bimorphism); + node_bim.call(&ght_a, &ght_b) + } +} + +#[derive(Default)] +/// bimorphism trait for equijoining Ght Nodes +pub struct GhtNodeKeyedBimorphism { + bimorphism: Bimorphism, +} +/// bimorphism implementation for equijoining Ght Nodes +impl GhtNodeKeyedBimorphism { + /// initialize bimorphism + pub fn new(bimorphism: Bimorphism) -> Self { + Self { bimorphism } + } +} +/// bimorphism implementation for equijoining Ght Nodes +impl<'a, 'b, Head, GhtA, GhtB, ValFunc> LatticeBimorphism<&'a GhtA, &'b GhtB> + for GhtNodeKeyedBimorphism +where + Head: Clone + Hash + Eq, + ValFunc: LatticeBimorphism<&'a GhtA::Get, &'b GhtB::Get>, + ValFunc::Output: GeneralizedHashTrieNode, + GhtA: GeneralizedHashTrieNode + GhtGet, + GhtB: GeneralizedHashTrieNode + GhtGet, + GhtA::Storage: VariadicSet, // multiset is not a lattice! + GhtB::Storage: VariadicSet, // multiset is not a lattice! + ::AsRefVar<'a>: CloneVariadic, + ::AsRefVar<'b>: CloneVariadic, +{ + type Output = GhtInner; // HashMap; // GhtOut; + + fn call(&mut self, ght_a: &'a GhtA, ght_b: &'b GhtB) -> Self::Output { + let mut children = HashMap::::new(); + // for head in ght_b.iter_keys() { + for head in ght_b.iter() { + if let Some(get_a) = ght_a.get(&head) { + let get_b = ght_b.get(&head).unwrap(); + let val = self.bimorphism.call(get_a, get_b); + children.insert(head.clone(), val); + } + } + GhtInner { children } + } +} + +/// bimorphism trait for equijoin on full tuple (keys in all GhtInner nodes) +pub trait DeepJoinLatticeBimorphism { + /// bimorphism type for equijoin on full tuple (keys in all GhtInner nodes) + type DeepJoinLatticeBimorphism; +} +/// bimorphism implementation for equijoin on full tuple (keys in all GhtInner nodes) +impl DeepJoinLatticeBimorphism + for (GhtInner, GhtInner) +where + Head: 'static + Hash + Eq + Clone, + NodeA: 'static + GeneralizedHashTrieNode, + NodeB: 'static + GeneralizedHashTrieNode, + NodeA::Storage: VariadicSet, // multiset is not a lattice! + NodeB::Storage: VariadicSet, // multiset is not a lattice! + (NodeA, NodeB): DeepJoinLatticeBimorphism, + Storage: VariadicSet, +{ + type DeepJoinLatticeBimorphism = GhtNodeKeyedBimorphism< + <(NodeA, NodeB) as DeepJoinLatticeBimorphism>::DeepJoinLatticeBimorphism, + >; +} +impl + DeepJoinLatticeBimorphism + for ( + GhtLeaf, + GhtLeaf, + ) +where + SchemaA: 'static + VariadicExt + Eq + Hash + SplitBySuffix, /* + AsRefVariadicPartialEq */ + ValTypeA: 'static + VariadicExt + Eq + Hash, // + AsRefVariadicPartialEq + SchemaB: 'static + VariadicExt + Eq + Hash + SplitBySuffix, /* + AsRefVariadicPartialEq */ + ValTypeB: 'static + VariadicExt + Eq + Hash, // + AsRefVariadicPartialEq + StorageA: VariadicSet, + StorageB: VariadicSet, + StorageOut: VariadicSet, + for<'x> SchemaA::AsRefVar<'x>: CloneVariadic, + for<'x> SchemaB::AsRefVar<'x>: CloneVariadic, + var_type!(...SchemaA, ...ValTypeB): Eq + Hash, +{ + type DeepJoinLatticeBimorphism = GhtValTypeProductBimorphism< + GhtLeaf< + var_type!(...SchemaA, ...ValTypeB), + var_type!(...ValTypeA, ...ValTypeB), + StorageOut, + >, + >; +} diff --git a/lattices/src/ght/macros.rs b/lattices/src/ght/macros.rs new file mode 100644 index 000000000000..174f124518ef --- /dev/null +++ b/lattices/src/ght/macros.rs @@ -0,0 +1,117 @@ +//! Macros for GHT +#[macro_export] +/// Internal macro for constructing a Ght struct with the given schema and storage type +/// +/// Should not be used directly, use `GhtType!` instead +macro_rules! GhtTypeWithSchema { + // Empty key & Val (Leaf) + (() => () => $( $schema:ty ),+ : $storage:ident) => ( + $crate::ght::GhtLeaf::<$( $schema ),*, () > + ); + + // Empty key (Leaf) + (() => $( $z:ty ),* => $schema:ty : $storage:ident) => ( + $crate::ght::GhtLeaf::<$schema, $crate::variadics::var_type!($( $z ),*), $crate::variadics::variadic_collections::$storage<$schema> > + ); + + // Singleton key & Empty val (Inner over Leaf) + ($a:ty => () => $schema:ty : $storage:ident) => ( + $crate::ght::GhtInner::<$a, $crate::ght::GhtLeaf::<$schema, (), $crate::variadics::variadic_collections::$storage<$schema> >> + ); + + // Singleton key (Inner over Leaf) + ($a:ty => $( $z:ty ),* => $schema:ty : $storage:ident) => ( + $crate::ght::GhtInner::<$a, $crate::ght::GhtLeaf::<$schema, $crate::variadics::var_type!($( $z ),*), $crate::variadics::variadic_collections::$storage<$schema> >> + ); + + // Recursive case with empty val + ($a:ty, $( $b:ty ),* => () => $schema:ty : $storage:ident) => ( + $crate::ght::GhtInner::<$a, $crate::GhtTypeWithSchema!($( $b ),* => () => $schema : $storage)> + ); + + // Recursive case + ($a:ty, $( $b:ty ),* => $( $z:ty ),* => $schema:ty : $storage:ident) => ( + $crate::ght::GhtInner::<$a, $crate::GhtTypeWithSchema!($( $b ),* => $( $z ),* => $schema : $storage)> + ); +} + +#[macro_export] +/// Public macro for constructing a Ght struct with the given schema and storage type +/// +/// # Example +/// ``` +/// use lattices::GhtType; +/// use variadics::variadic_collections::VariadicHashSet; +/// +/// // This generates a Ght struct with (u16, u32) as key, (u64) as val, and VariadicHashSet as storage +/// type MyHashGht = GhtType!(u16, u32 => u64: VariadicHashSet); +/// let my_ght = MyHashGht::default(); +/// +/// /// // This generates a Ght struct with (u16, u32) as key, () as val, and VariadicCountedHashSet as storage +/// type MyMultisetGht = GhtType!(u16, u32 => (): VariadicCountedHashSet); +/// let my_ght = MyMultisetGht::default(); +/// +/// // This generates a Ght struct with (u16, u32) as key, () as val, and VariadicColumnSet as storage +/// type MyColumnarMultisetGht = GhtType!(u16, u32 => (): VariadicColumnMultiset); +/// let my_ght = MyColumnarMultisetGht::default(); +/// ``` +macro_rules! GhtType { + // Empty key + (() => $( $z:ty ),*: $storage:ident) => ( + $crate::GhtTypeWithSchema!(() => $( $z ),* => $crate::variadics::var_type!($( $z ),*): $storage) + ); + + // Recursive case empty val + ($( $b:ty ),* => (): $storage:ident) => ( + $crate::GhtTypeWithSchema!($( $b ),* => () => $crate::variadics::var_type!($( $b ),*): $storage) + ); + + // Recursive case + ($( $b:ty ),* => $( $z:ty ),*: $storage:ident) => ( + $crate::GhtTypeWithSchema!($( $b ),* => $( $z ),* => $crate::variadics::var_type!($( $b ),*, $( $z ),*): $storage) + ); +} + +#[macro_export] +/// Construct a forest of Ghts (i.e. a ColtForest) with the given schema and storage type. +/// +/// # Example +/// ``` +/// use lattices::ColtType; +/// +/// type MyColt = ColtType!(u16, u32, u64); +/// ``` +macro_rules! ColtType { + // Base case: single type to empty + ($a:ty => ()) => { + $crate::variadics::var_type!($crate::GhtType!($a => (): VariadicColumnMultiset)) + }; + // Base case: single type to single type + ($a:ty => $c:ty) => { + ($crate::GhtType!($a => $c: VariadicColumnMultiset), $crate::ColtType!($a, $c => ())) + }; + // Recursive case: single type to multiple types + ($a:ty => $c:ty, $( $d:ty ),*) => { + ($crate::GhtType!($a => $c, $( $d ),*: VariadicColumnMultiset), $crate::ColtType!($a, $c => $( $d ),*)) + }; + // Base case: multiple types to empty + ($a:ty, $( $b:ty ),* => ()) => { + $crate::variadics::var_type!($crate::GhtType!($a, $( $b ),* => (): VariadicColumnMultiset)) + }; + // Base case: multiple types to single type + ($a:ty, $( $b:ty ),* => $c:ty) => { + ($crate::GhtType!($a, $( $b ),* => $c: VariadicColumnMultiset), $crate::ColtType!($a, $( $b ),*, $c => ())) + }; + // Recursive case: multiple types to multiple types + ($a:ty, $( $b:ty ),* => $c:ty, $( $d:ty ),*) => { + ($crate::GhtType!($a, $( $b ),* => $c, $( $d ),*: VariadicColumnMultiset), $crate::ColtType!($a, $( $b ),*, $c => $( $d ),*)) + }; + // General case: single type + ($a:ty) => { + ($crate::GhtType!(() => $a: VariadicColumnMultiset), $crate::ColtType!($a => ())) + }; + // General case: multiple types + ($a:ty, $( $b:ty ),*) => { + ($crate::GhtType!(() => $a, $( $b ),*: VariadicColumnMultiset), $crate::ColtType!($a => $( $b ),*)) + }; +} diff --git a/lattices/src/ght/mod.rs b/lattices/src/ght/mod.rs new file mode 100644 index 000000000000..9496a43cd1e6 --- /dev/null +++ b/lattices/src/ght/mod.rs @@ -0,0 +1,545 @@ +//! GHT from the Wang/Willsey/Suciu Freejoin work +use std::collections::HashMap; +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::PhantomData; + +use variadics::variadic_collections::VariadicCollection; +use variadics::{ + var_args, var_type, PartialEqVariadic, RefVariadic, Split, SplitBySuffix, VariadicExt, +}; + +pub mod colt; +pub mod lattice; +pub mod macros; +pub mod test; + +/// The GeneralizedHashTrieNode trait captures the properties of nodes in a Ght. +/// +/// The Ght, defined by Wang/Willsey/Suciu, is a hash-based trie for storing tuples. +/// It is parameterized by an ordered schema [`VariadicExt`] of the relation stored in the trie. +/// It is a tree of [`GhtInner`] nodes, with [`GhtLeaf`] nodes at the leaves. +/// The trie is keyed on a prefix of the schema [`Self::KeyType`], +/// and the remaining columns [`Self::ValType`] are stored in the leaf. +/// All leaf nodes use the same `[Self::Storage]` type to store the data. +pub trait GeneralizedHashTrieNode: Default { + // types that are the same in all nodes of the trie + /// Schema variadic: the schema of the relation stored in this trie. + type Schema: VariadicExt + Eq + Hash + Clone + SplitBySuffix; + /// The prefix of columns in [`Self::Schema`] that the trie is keyed on + type KeyType: VariadicExt + Eq + Hash + Clone; + /// The suffix of columns in [`Self::Schema`] that are not part of the trie keys + type ValType: VariadicExt + Eq + Hash + Clone; + /// The type that holds the data in the leaves + type Storage: VariadicCollection + + Default + + IntoIterator; + + // types that vary per node + /// SuffixSchema variadic: the suffix of [`Self::Schema`] from this node of the trie + /// downward. The first entry in this variadic is of type [`Self::Head`]. + type SuffixSchema: VariadicExt + Eq + Hash + Clone; + /// The first field in [`Self::SuffixSchema`], and the key for the next node in the trie. + type Head: Eq + Hash + Clone; + + /// Create a new Ght from the iterator. + fn new_from(input: impl IntoIterator) -> Self; + + /// Merge a matching Ght node into this node + fn merge_node(&mut self, other: Self) -> bool; + + /// Report the height of this node. This is the length of path from this node to a leaf - 1. + /// E.g. if we have GhtInner> the height is 2 + /// This is a static property of the type of this node, so simply invokes the static method. + fn height(&self) -> usize { + Self::HEIGHT + } + + /// The height of this node in the GhT. Leaf = 0. + const HEIGHT: usize; + + /// Inserts an item into the hash trie. + fn insert(&mut self, row: Self::Schema) -> bool; + + /// Returns `true` if the (entire) row is found below in the trie, `false` otherwise. + /// See [`GhtGet::get`] to look just for "head" keys in this node + fn contains<'a>(&'a self, row: ::AsRefVar<'a>) -> bool; + + /// Iterate through (entire) rows stored in this HashTrie. + fn recursive_iter(&self) -> impl Iterator::AsRefVar<'_>>; + + /// return the leaf below that contains this row, or `None` if not found. + fn find_containing_leaf( + &self, + row: ::AsRefVar<'_>, + ) -> Option<&'_ GhtLeaf>; + + /// into_iter for leaf elements, or None for inner nodes + fn into_iter(self) -> Option>; + + /// pull all the data out of this trie node but retain the reference + fn drain(&mut self) -> Option>; +} + +/// internal node of a HashTrie +#[derive(Debug, Clone)] +pub struct GhtInner +where + Head: Clone, + Node: GeneralizedHashTrieNode, +{ + pub(crate) children: HashMap, +} + +impl Default for GhtInner +where + Head: Clone, + Node: GeneralizedHashTrieNode, +{ + fn default() -> Self { + let children = Default::default(); + Self { children } + } +} + +impl GeneralizedHashTrieNode for GhtInner +where + Head: 'static + Hash + Eq + Clone, + Node: 'static + GeneralizedHashTrieNode, + Node::Schema: SplitBySuffix, +{ + type Schema = Node::Schema; + type KeyType = Node::KeyType; + type ValType = Node::ValType; + type Storage = Node::Storage; + type SuffixSchema = var_type!(Head, ...Node::SuffixSchema); + type Head = Head; + + fn new_from(input: impl IntoIterator) -> Self { + let mut retval: Self = Default::default(); + for row in input { + retval.insert(row); + } + retval + } + + fn merge_node(&mut self, other: Self) -> bool { + let mut changed = false; + + for (k, v) in other.children { + match self.children.entry(k) { + std::collections::hash_map::Entry::Occupied(mut occupied) => { + changed |= occupied.get_mut().merge_node(v) + } + std::collections::hash_map::Entry::Vacant(vacant) => { + vacant.insert(v); + changed = true + } + } + } + changed + } + + const HEIGHT: usize = Node::HEIGHT + 1; + + fn insert(&mut self, row: Self::Schema) -> bool { + let (_prefix, var_args!(head, ..._rest)) = + Self::Schema::split_by_suffix_ref(row.as_ref_var()); + self.children.entry(head.clone()).or_default().insert(row) + } + + fn contains<'a>(&'a self, row: ::AsRefVar<'a>) -> bool { + let (_prefix, var_args!(head, ..._rest)) = Self::Schema::split_by_suffix_ref(row); + if let Some(node) = self.children.get(head) { + node.contains(row) + } else { + false + } + } + + fn recursive_iter(&self) -> impl Iterator::AsRefVar<'_>> { + self.children + .iter() + .flat_map(|(_k, vs)| vs.recursive_iter()) + } + + fn find_containing_leaf( + &self, + row: ::AsRefVar<'_>, + ) -> Option<&'_ GhtLeaf> { + let (_prefix, var_args!(head, ..._rest)) = Self::Schema::split_by_suffix_ref(row); + self.children + .get(head) + .and_then(|child| child.find_containing_leaf(row)) + } + + fn into_iter(self) -> Option> { + None::>> + } + + fn drain(&mut self) -> Option> { + None::>> + } +} + +impl FromIterator for GhtInner +where + Head: 'static + Hash + Eq + Clone, + Node: 'static + GeneralizedHashTrieNode + Clone, + Node::Schema: SplitBySuffix, +{ + fn from_iter>(iter: Iter) -> Self { + let mut out = Self::default(); + for row in iter { + out.insert(row); + } + out + } +} + +/// leaf node of a HashTrie +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct GhtLeaf +where + Schema: Eq + Hash, + Storage: VariadicCollection, +{ + pub(crate) elements: Storage, + pub(crate) forced: bool, + /// defines ValType for the parents, recursively + pub(crate) _suffix_schema: PhantomData, +} +impl Default for GhtLeaf +where + Schema: Eq + Hash, + Storage: VariadicCollection + Default, +{ + fn default() -> Self { + let elements = Storage::default(); + Self { + elements, + forced: false, + _suffix_schema: PhantomData, + } + } +} + +impl GeneralizedHashTrieNode + for GhtLeaf +where + Schema: 'static + + Eq + + VariadicExt + + Hash + + Clone + + SplitBySuffix + + PartialEqVariadic, + ValHead: Clone + Eq + Hash, + var_type!(ValHead, ...ValRest): Clone + Eq + Hash + PartialEqVariadic, + >::Prefix: Eq + Hash + Clone, + Storage: VariadicCollection + Default + IntoIterator, +{ + type Schema = Schema; + type SuffixSchema = var_type!(ValHead, ...ValRest); + type ValType = var_type!(ValHead, ...ValRest); + type KeyType = >::Prefix; + type Head = ValHead; + type Storage = Storage; + + fn new_from(input: impl IntoIterator) -> Self { + let mut retval: Self = Default::default(); + for i in input { + retval.insert(i); + } + retval + } + + fn merge_node(&mut self, other: Self) -> bool { + let old_len = self.elements.len(); + self.elements.extend(other.elements); + self.elements.len() > old_len + } + + const HEIGHT: usize = 0; + + fn insert(&mut self, row: Self::Schema) -> bool { + self.elements.insert(row); + true + } + + fn contains<'a>(&'a self, row: ::AsRefVar<'a>) -> bool { + self.elements.iter().any(|r| Schema::eq_ref(r, row)) + } + + fn recursive_iter(&self) -> impl Iterator::AsRefVar<'_>> { + self.elements.iter() + } + + fn find_containing_leaf( + &self, + row: ::AsRefVar<'_>, + ) -> Option<&'_ GhtLeaf<::Schema, Self::ValType, Self::Storage>> + { + // TODO(mingwei): actually use the hash set as a hash set + if self + .elements + .iter() + .any(|x| ::eq_ref(row, x)) + { + Some(self) + } else { + None + } + } + + fn into_iter(self) -> Option> { + Some(self.elements.into_iter()) + } + + fn drain(&mut self) -> Option> { + Some(self.elements.drain()) + } +} + +impl GeneralizedHashTrieNode for GhtLeaf +where + Schema: 'static + Eq + VariadicExt + Hash + Clone + PartialEqVariadic, + Storage: VariadicCollection + Default + IntoIterator, +{ + type Schema = Schema; + type SuffixSchema = (); + type ValType = (); + type KeyType = Schema; + type Head = (); + type Storage = Storage; + + fn new_from(input: impl IntoIterator) -> Self { + let mut retval: Self = Default::default(); + for i in input { + retval.insert(i); + } + retval + } + + fn merge_node(&mut self, other: Self) -> bool { + let old_len = self.elements.len(); + self.elements.extend(other.elements); + self.elements.len() > old_len + } + + const HEIGHT: usize = 0; + + fn insert(&mut self, row: Self::Schema) -> bool { + self.elements.insert(row); + true + } + + fn contains<'a>(&'a self, row: ::AsRefVar<'a>) -> bool { + self.elements.iter().any(|r| Schema::eq_ref(r, row)) + } + + fn recursive_iter(&self) -> impl Iterator::AsRefVar<'_>> { + self.elements.iter() + } + + fn find_containing_leaf( + &self, + row: ::AsRefVar<'_>, + ) -> Option<&'_ GhtLeaf<::Schema, Self::ValType, Self::Storage>> + { + // TODO(mingwei): actually use the hash set as a hash set + if self + .elements + .iter() + .any(|x| ::eq_ref(row, x)) + { + Some(self) + } else { + None + } + } + + fn into_iter(self) -> Option> { + Some(self.elements.into_iter()) + } + + fn drain(&mut self) -> Option> { + Some(self.elements.drain()) + } +} + +impl FromIterator for GhtLeaf +where + Schema: Eq + Hash, + Storage: VariadicCollection + Default + FromIterator, +{ + fn from_iter>(iter: Iter) -> Self { + let elements = iter.into_iter().collect(); + Self { + elements, + forced: false, + _suffix_schema: PhantomData, + } + } +} + +/// A trait for the get and iter methods from Wang/Willsey/Suciu, which +/// work differently on leaves than internal nodes +pub trait GhtGet: GeneralizedHashTrieNode { + /// Type returned by [`Self::get`]. + type Get: GeneralizedHashTrieNode; + + /// On an Inner node, retrieves the value (child) associated with the given "head" key. + /// returns an `Option` containing a reference to the value if found, or `None` if not found. + /// On a Leaf node, returns None. + fn get<'a>(&'a self, head: &Self::Head) -> Option<&'a Self::Get>; + + /// get, but mutable output + fn get_mut<'a>(&'a mut self, head: &Self::Head) -> Option<&'a mut Self::Get>; + + /// Iterator for the "head" keys (from inner nodes) or nothing (from leaf nodes). + fn iter(&self) -> impl Iterator; + + /// Iterator for the tuples (from leaf nodes) or nothing (from inner nodes). + fn iter_tuples(&self) -> impl Iterator::AsRefVar<'_>>; +} + +impl GhtGet for GhtInner +where + Head: 'static + Eq + Hash + Clone, + Node: 'static + GeneralizedHashTrieNode, + Node::Schema: SplitBySuffix, +{ + /// Type returned by [`Self::get`]. + type Get = Node; + + /// On an Inner node, retrieves the value (child) associated with the given "head" key. + /// returns an `Option` containing a reference to the value if found, or `None` if not found. + /// On a Leaf node, returns None. + fn get<'a>(&'a self, head: &Self::Head) -> Option<&'a Self::Get> { + self.children.get(head) + } + + fn get_mut<'a>(&'a mut self, head: &Self::Head) -> Option<&'a mut Self::Get> { + self.children.get_mut(head) + } + + fn iter(&self) -> impl Iterator { + self.children.keys().cloned() + } + + fn iter_tuples(&self) -> impl Iterator::AsRefVar<'_>> { + std::iter::empty() + } +} + +impl GhtGet for GhtLeaf +where + Schema: 'static + Eq + Hash + Clone + PartialEqVariadic + SplitBySuffix, + ValType: Eq + Hash + Clone + PartialEqVariadic, + >::Prefix: Eq + Hash + Clone, + GhtLeaf: GeneralizedHashTrieNode, + Storage: VariadicCollection, +{ + /// Type returned by [`Self::get`]. + type Get = GhtLeaf; + + /// On an Inner node, retrieves the value (child) associated with the given "head" key. + /// returns an `Option` containing a reference to the value if found, or `None` if not found. + /// On a Leaf node, returns None. + fn get<'a>(&'a self, _head: &Self::Head) -> Option<&'a Self::Get> { + None + } + fn get_mut<'a>(&'a mut self, _head: &Self::Head) -> Option<&'a mut Self::Get> { + None + } + + fn iter(&self) -> impl Iterator { + std::iter::empty() + } + + fn iter_tuples(&self) -> impl Iterator::AsRefVar<'_>> { + self.elements.iter() + } +} + +/// A trait to iterate through the items in a Ght based on a prefix of the schema. +pub trait GhtPrefixIter { + /// the schema output + type Item: VariadicExt; + /// given a prefix, return an iterator through the items below + fn prefix_iter<'a>( + &'a self, + prefix: KeyPrefix, + ) -> impl Iterator::AsRefVar<'a>> + where + Self::Item: 'a; +} + +impl<'k, Head, Node, PrefixRest> GhtPrefixIter + for GhtInner +where + Head: Eq + Hash + Clone, + Node: GeneralizedHashTrieNode + GhtPrefixIter, +{ + type Item = >::Item; + fn prefix_iter<'a>( + &'a self, + prefix: var_type!(&'k Head, ...PrefixRest), + ) -> impl Iterator::AsRefVar<'a>> + where + Self::Item: 'a, + { + let var_args!(head, ...rest) = prefix; + self.children + .get(head) + .map(|node| node.prefix_iter(rest)) + .into_iter() + .flatten() + } +} +impl GhtPrefixIter for GhtInner +where + Self: GeneralizedHashTrieNode, + Head: Eq + Hash + Clone, + Node: GeneralizedHashTrieNode, +{ + type Item = ::Schema; + fn prefix_iter<'a>( + &'a self, + _prefix: var_type!(), + ) -> impl Iterator::AsRefVar<'a>> + where + Self::Item: 'a, + { + self.recursive_iter() + } +} + +impl GhtPrefixIter + for GhtLeaf +where + KeyPrefixRef: 'static + RefVariadic, + Schema: 'static + VariadicExt + Hash + Eq + SplitBySuffix, + ValType: VariadicExt, + ValType: Split, + KeyPrefixRef::UnRefVar: PartialEqVariadic, + Storage: 'static + VariadicCollection, +{ + type Item = Schema; + fn prefix_iter<'a>( + &'a self, + prefix: KeyPrefixRef, + ) -> impl Iterator::AsRefVar<'a>> + where + Self::Item: 'a, + { + self.elements.iter().filter(move |&row| { + let (_row_prefix, row_mid_suffix) = + >::split_by_suffix_ref(row); + let (row_mid, _row_suffix): (::AsRefVar<'_>, _) = + >::split_ref(row_mid_suffix); + ::eq_ref(prefix.unref_ref(), row_mid) + }) + } +} diff --git a/lattices/src/ght/test.rs b/lattices/src/ght/test.rs new file mode 100644 index 000000000000..e24238c2de34 --- /dev/null +++ b/lattices/src/ght/test.rs @@ -0,0 +1,943 @@ +//! Tests for the GHT code +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + #[test] + fn basic_test() { + use variadics::var_expr; + + use crate::ght::GeneralizedHashTrieNode; + use crate::GhtType; + + // Example usage + type MyTrie1 = GhtType!(u32, u32 => &'static str: VariadicCountedHashSet); + + fn ght_type() {} + ght_type::(); + + let htrie1 = MyTrie1::new_from(vec![var_expr!(42, 314, "hello")]); + assert!(htrie1.contains(var_expr!(&42, &314, &"hello"))); + assert_eq!(htrie1.recursive_iter().count(), 1); + + type MyTrie2 = GhtType!(u32 => u32: VariadicCountedHashSet); + let htrie2 = MyTrie2::new_from(vec![var_expr!(42, 314)]); + assert!(htrie2.contains(var_expr!(&42, &314))); + assert_eq!(htrie1.recursive_iter().count(), 1); + + type MyTrie3 = GhtType!(u32, u64, u16 => &'static str: VariadicCountedHashSet); + let htrie3 = MyTrie3::new_from(vec![ + var_expr!(123, 2, 5, "hello"), + var_expr!(50, 1, 1, "hi"), + var_expr!(5, 1, 7, "hi"), + var_expr!(5, 1, 7, "bye"), + ]); + assert!(htrie3.contains(var_expr!(&50, &1, &1, &"hi"))); + assert_eq!(htrie3.recursive_iter().count(), 4); + } + #[test] + fn test_ght_node_type_macro() { + use variadics::var_expr; + + use crate::ght::GeneralizedHashTrieNode; + use crate::GhtType; + + // 0 => 1 + type LilTrie = GhtType!(() => u32: VariadicCountedHashSet); + let _j = LilTrie::default(); + let _l = LilTrie::new_from(vec![var_expr!(1)]); + + // 0 => >1 + type LilTrie2 = GhtType!(() => u32, u64: VariadicCountedHashSet); + let _l = LilTrie2::default(); + let _l = LilTrie2::new_from(vec![var_expr!(1, 1)]); + + // 1 => 0 + type KeyNoValTrie = GhtType!(u32 => (): VariadicCountedHashSet); + let l = KeyNoValTrie::new_from(vec![var_expr!(1)]); + let _: KeyNoValTrie = l; + + // 1 => 1 + type SmallTrie = GhtType!(u32 => &'static str: VariadicCountedHashSet); + type SmallKeyedTrie = GhtType!(u32 => &'static str: VariadicCountedHashSet); + let l = SmallTrie::new_from(vec![var_expr!(1, "hello")]); + let _: SmallKeyedTrie = l; + + // 1 => >1 + type SmallKeyLongValTrie = GhtType!(u32 => u64, u16, &'static str: VariadicCountedHashSet); + let _x = SmallKeyLongValTrie::new_from(vec![var_expr!(1, 999, 222, "hello")]); + + // >1 => 0 + type LongKeyNoValTrie = GhtType!(u32, u64 => (): VariadicCountedHashSet); + let l = LongKeyNoValTrie::new_from(vec![var_expr!(1, 999)]); + let _: LongKeyNoValTrie = l; + + // >1 => 1 + type LongKeySmallValTrie = GhtType!(u32, u16 => &'static str: VariadicCountedHashSet); + type LongKeySmallValKeyedTrie = GhtType!(u32, u16 => &'static str: VariadicCountedHashSet); + let x = LongKeySmallValTrie::new_from(vec![var_expr!(1, 314, "hello")]); + let _: LongKeySmallValKeyedTrie = x; + let _ = LongKeySmallValTrie::new_from(vec![var_expr!(1, 314, "hello")]); + + // >1 => >1 + type LongKeyLongValTrie = GhtType!(u32, u64 => u16, &'static str: VariadicCountedHashSet); + let _x = LongKeyLongValTrie::new_from(vec![var_expr!(1, 999, 222, "hello")]); + } + + #[test] + fn test_insert() { + use variadics::var_expr; + + use crate::ght::GeneralizedHashTrieNode; + use crate::GhtType; + + type MyGht = GhtType!(u16, u32 => u64: VariadicCountedHashSet); + let mut htrie = MyGht::default(); + htrie.insert(var_expr!(42, 314, 43770)); + assert_eq!(htrie.recursive_iter().count(), 1); + assert_eq!(MyGht::HEIGHT, 2); + htrie.insert(var_expr!(42, 315, 43770)); + assert_eq!(htrie.recursive_iter().count(), 2); + htrie.insert(var_expr!(42, 314, 30619)); + assert_eq!(htrie.recursive_iter().count(), 3); + htrie.insert(var_expr!(43, 10, 600)); + assert_eq!(htrie.recursive_iter().count(), 4); + assert!(htrie.contains(var_expr!(&42, &314, &30619))); + assert!(htrie.contains(var_expr!(&42, &315, &43770))); + assert!(htrie.contains(var_expr!(&43, &10, &600))); + + type LongKeyLongValTrie = GhtType!(u32, u64 => u16, &'static str: VariadicCountedHashSet); + let mut htrie = LongKeyLongValTrie::new_from(vec![var_expr!(1, 999, 222, "hello")]); + htrie.insert(var_expr!(1, 999, 111, "bye")); + htrie.insert(var_expr!(1, 1000, 123, "cya")); + assert!(htrie.contains(var_expr!(&1, &999, &222, &"hello"))); + assert!(htrie.contains(var_expr!(&1, &999, &111, &"bye"))); + assert!(htrie.contains(var_expr!(&1, &1000, &123, &"cya"))); + } + + #[test] + fn test_scale() { + use variadics::var_expr; + + use crate::ght::GeneralizedHashTrieNode; + use crate::GhtType; + + type MyGht = GhtType!(bool, usize, &'static str => i32: VariadicCountedHashSet); + let mut htrie = MyGht::new_from(vec![var_expr!(true, 1, "hello", -5)]); + assert_eq!(htrie.recursive_iter().count(), 1); + for i in 1..1000000 { + htrie.insert(var_expr!(true, 1, "hello", i)); + } + assert_eq!(htrie.recursive_iter().count(), 1000000); + } + + #[test] + fn test_contains() { + use variadics::{var_expr, VariadicExt}; + + use crate::ght::GeneralizedHashTrieNode; + use crate::GhtType; + + type MyGht = GhtType!(u16, u32 => u64: VariadicCountedHashSet); + let htrie = MyGht::new_from(vec![var_expr!(42_u16, 314_u32, 43770_u64)]); + let x = var_expr!(&42, &314, &43770); + assert!(htrie.contains(x)); + assert!(htrie.contains(var_expr!(42, 314, 43770).as_ref_var())); + assert!(htrie.contains(var_expr!(&42, &314, &43770))); + assert!(!htrie.contains(var_expr!(42, 314, 30619).as_ref_var())); + assert!(!htrie.contains(var_expr!(&42, &315, &43770))); + assert!(!htrie.contains(var_expr!(&43, &314, &43770))); + } + + #[test] + fn test_get() { + use variadics::{var_expr, VariadicExt}; + + use crate::ght::{GeneralizedHashTrieNode, GhtGet}; + use crate::GhtType; + + type MyGht = GhtType!(u32, u32 => u32: VariadicCountedHashSet); + let ht_root = MyGht::new_from(vec![var_expr!(42, 314, 43770)]); + + let inner = ht_root.get(&42).unwrap(); + let t = inner.recursive_iter().next().unwrap(); + assert_eq!(t, var_expr!(&42, &314, &43770)); + + let leaf = inner.get(&314).unwrap(); + let t = leaf.recursive_iter().next().unwrap(); + assert_eq!(t, var_expr!(42, 314, 43770).as_ref_var()); + } + + #[test] + fn test_iter() { + use variadics::var_expr; + + use crate::ght::{GeneralizedHashTrieNode, GhtGet}; + use crate::GhtType; + type MyGht = GhtType!(u32, u32 => u32: VariadicCountedHashSet); + let ht_root = MyGht::new_from(vec![var_expr!(42, 314, 43770)]); + let inner_key = ht_root.iter().next().unwrap(); + let inner = ht_root.get(&inner_key).unwrap(); + let t = inner.recursive_iter().next().unwrap(); + assert_eq!(t, var_expr!(&42, &314, &43770)); + + let leaf_key = inner.iter().next().unwrap(); + let leaf = inner.get(&leaf_key).unwrap(); + // iter() on leaf should return None + let t = leaf.iter().next(); + assert!(t.is_none()); + } + + #[test] + fn test_recursive_iter() { + use variadics::{var_expr, var_type, VariadicExt}; + + use crate::ght::GeneralizedHashTrieNode; + use crate::GhtType; + + type MyGht = GhtType!(u32, u32 => u32: VariadicCountedHashSet); + type InputType = var_type!(u32, u32, u32); + type ResultType<'a> = var_type!(&'a u32, &'a u32, &'a u32); + let input: HashSet = HashSet::from_iter( + [ + (42, 314, 30619), + (42, 314, 43770), + (42, 315, 43770), + (43, 10, 600), + ] + .iter() + .map(|&(a, b, c)| var_expr!(a, b, c)), + ); + let htrie = MyGht::new_from(input.clone()); + let result = input.iter().map(|v| v.as_ref_var()).collect(); + let v: HashSet = htrie.recursive_iter().collect(); + assert_eq!(v, result); + } + + #[test] + fn test_prefix_iter_leaf() { + use variadics::variadic_collections::VariadicCountedHashSet; + use variadics::{var_expr, var_type}; + + use crate::ght::{GeneralizedHashTrieNode, GhtLeaf, GhtPrefixIter}; + + type InputType = var_type!(u8, u16, u32); + type ResultType<'a> = var_type!(&'a u8, &'a u16, &'a u32); + + let input: HashSet = HashSet::from_iter( + [ + (42, 314, 30619), + (42, 314, 43770), + (42, 315, 43770), + (43, 10, 600), + ] + .iter() + .map(|&(a, b, c)| var_expr!(a, b, c)), + ); + let leaf = + GhtLeaf::>::new_from( + input.clone(), + ); + // let key = var_expr!(42u8).as_ref_var(); + let key = (); // (var_expr!().as_ref_var();) + let v: HashSet = leaf.prefix_iter(key).collect(); + let result = input + .iter() + // .filter(|t: &&InputType| t.0 == 42) + .map(|t: &InputType| var_expr!(&t.0, &t.1 .0, &t.1 .1 .0)) + .collect(); + assert_eq!(v, result); + } + + #[test] + fn test_prefix_iter() { + use variadics::{var_expr, var_type, VariadicExt}; + + use crate::ght::{GeneralizedHashTrieNode, GhtPrefixIter}; + use crate::GhtType; + + type MyGht = GhtType!(u8, u16 => u32: VariadicCountedHashSet); + type InputType = var_type!(u8, u16, u32); + type ResultType<'a> = var_type!(&'a u8, &'a u16, &'a u32); + let input: HashSet = HashSet::from_iter( + [ + (42, 314, 30619), + (42, 314, 43770), + (42, 315, 43770), + (43, 10, 600), + ] + .iter() + .map(|&(a, b, c)| var_expr!(a, b, c)), + ); + let htrie = MyGht::new_from(input.clone()); + + let v: HashSet = htrie.prefix_iter(var_expr!(42, 315).as_ref_var()).collect(); + let result = HashSet::from_iter([var_expr!(&42, &315, &43770)].iter().copied()); + assert_eq!(v, result); + + let v: HashSet = htrie.prefix_iter(var_expr!(42u8).as_ref_var()).collect(); + let result = input + .iter() + .filter(|t: &&InputType| t.0 == 42) + .map(|t: &InputType| var_expr!(&t.0, &t.1 .0, &t.1 .1 .0)) + .collect(); + assert_eq!(v, result); + + for row in htrie.prefix_iter(var_expr!(42, 315, 43770).as_ref_var()) { + assert_eq!(row, var_expr!(&42, &315, &43770)); + } + } + + #[test] + fn test_prefix_iter_complex() { + use variadics::{var_expr, var_type, VariadicExt}; + + use crate::ght::{GeneralizedHashTrieNode, GhtPrefixIter}; + use crate::GhtType; + + type MyGht = GhtType!(bool, u32, &'static str => i32: VariadicCountedHashSet); + type InputType = var_type!(bool, u32, &'static str, i32); + type ResultType<'a> = var_type!(&'a bool, &'a u32, &'a &'static str, &'a i32); + let input: HashSet = HashSet::from_iter( + [ + (true, 1, "hello", -5), + (true, 1, "hi", -2), + (true, 1, "hi", -3), + (true, 1, "hi", -4), + (true, 1, "hi", -5), + (true, 2, "hello", 1), + (false, 10, "bye", 5), + ] + .iter() + .map(|&(a, b, c, d)| var_expr!(a, b, c, d)), + ); + + let htrie = MyGht::new_from(input.clone()); + + let v: HashSet = htrie + .prefix_iter(var_expr!(true, 1, "hi").as_ref_var()) + .collect(); + let result = input + .iter() + .filter(|t: &&InputType| t.0 && t.1.0 == 1 && t.1.1.0 == "hi") + //.map(|t: &InputType| (&t.0, &t.1 .0, (&t.1 .1 .0, (&t.1 .1 .1 .0, ())))) + .map(|t| t.as_ref_var()) + .collect(); + assert_eq!(v, result); + + let v: HashSet = htrie.prefix_iter(var_expr!(true).as_ref_var()).collect(); + let result = input + .iter() + .filter(|t: &&InputType| t.0) + .map(|t: &InputType| t.as_ref_var()) + .collect(); + assert_eq!(v, result); + } + + #[test] + fn test_merge() { + use variadics::{var_expr, var_type}; + + use crate::ght::GeneralizedHashTrieNode; + use crate::{GhtType, Merge}; + + type MyGht = GhtType!(u32, u64 => u16, &'static str: VariadicHashSet); + + let mut test_ght1 = MyGht::new_from(vec![var_expr!(42, 314, 10, "hello")]); + let test_ght2 = MyGht::new_from(vec![var_expr!(42, 314, 10, "hello")]); + + assert_eq!( + test_ght1 + .recursive_iter() + .collect::>() + .len(), + 1 + ); + test_ght1.merge(test_ght2.clone()); + // merge does not contain duplicate copy of the tuple + assert_eq!( + test_ght1 + .recursive_iter() + .collect::>() + .len(), + 1 + ); + assert!(!test_ght1.merge(test_ght2.clone())); + + let mut test_ght1 = MyGht::new_from(vec![var_expr!(42, 314, 10, "hello")]); + let mut test_ght2 = MyGht::new_from(vec![var_expr!(42, 314, 10, "hello")]); + test_ght1.merge(test_ght2.clone()); + + test_ght1.insert(var_expr!(42, 314, 20, "goodbye")); + test_ght2.insert(var_expr!(42, 314, 20, "again")); + + // change on merge + assert!(test_ght1.merge(test_ght2.clone())); + for k in test_ght2.recursive_iter() { + assert!(test_ght1.contains(k)) + } + } + + #[test] + fn test_node_lattice() { + use variadics::var_expr; + + use crate::ght::GeneralizedHashTrieNode; + use crate::{GhtType, NaiveLatticeOrd}; + + type MyGht = GhtType!(u32, u64 => u16, &'static str: VariadicHashSet); + type MyGhtNode = GhtType!(u32, u64 => u16, &'static str: VariadicHashSet); + + let mut test_vec: Vec = Vec::new(); + + let empty_ght = MyGht::new_from(vec![]); + let test_ght1 = MyGht::new_from(vec![var_expr!(42, 314, 10, "hello")]); + let mut test_ght2 = MyGht::new_from(vec![var_expr!(42, 314, 10, "hello")]); + test_ght2.insert(var_expr!(42, 314, 20, "again")); + let mut test_ght3 = test_ght2.clone(); + test_ght3.insert(var_expr!(42, 400, 1, "level 2")); + let mut test_ght4 = test_ght3.clone(); + test_ght4.insert(var_expr!(43, 1, 1, "level 1")); + + let test_vec_wrap = [empty_ght, test_ght1, test_ght2, test_ght3, test_ght4]; + + for ght in test_vec_wrap.iter().cloned() { + ght.naive_cmp(&ght.clone()); + test_vec.push(ght); + } + crate::test::check_all(&test_vec); + crate::test::check_all(&test_vec_wrap); + } + + #[test] + fn test_cartesian_bimorphism() { + use variadics::var_expr; + + use crate::ght::lattice::GhtCartesianProductBimorphism; + use crate::ght::GeneralizedHashTrieNode; + use crate::{GhtType, LatticeBimorphism}; + + type MyGhtA = GhtType!(u32, u64 => u16, &'static str: VariadicHashSet); + type MyGhtB = GhtType!(u32, u64, u16 => &'static str: VariadicHashSet); + + let mut ght_a = MyGhtA::default(); + let mut ght_b = MyGhtB::default(); + + ght_a.insert(var_expr!(123, 2, 5, "hello")); + ght_a.insert(var_expr!(50, 1, 1, "hi")); + ght_a.insert(var_expr!(5, 1, 7, "hi")); + ght_b.insert(var_expr!(5, 1, 8, "hi")); + ght_b.insert(var_expr!(10, 1, 2, "hi")); + ght_b.insert(var_expr!(12, 10, 98, "bye")); + + type MyGhtAb = GhtType!(u32, u64, u16, &'static str, u32, u64 => u16, &'static str: VariadicCountedHashSet); + + let mut bim = GhtCartesianProductBimorphism::::default(); + let ght_out = bim.call(&ght_a, &ght_b); + assert_eq!( + ght_out.recursive_iter().count(), + ght_a.recursive_iter().count() * ght_b.recursive_iter().count() + ); + } + + #[test] + fn test_join_bimorphism() { + use variadics::variadic_collections::{VariadicCountedHashSet, VariadicHashSet}; + use variadics::{var_expr, var_type}; + + use crate::ght::lattice::{ + DeepJoinLatticeBimorphism, GhtNodeKeyedBimorphism, GhtValTypeProductBimorphism, + }; + use crate::ght::{GeneralizedHashTrieNode, GhtInner, GhtLeaf}; + use crate::{GhtType, LatticeBimorphism}; + + type ResultSchemaType = var_type!(u32, u64, u16, &'static str, &'static str); + type ResultSchemaRefType<'a> = var_type!( + &'a u32, + &'a u64, + &'a u16, + &'a &'static str, + &'a &'static str + ); + type MyGhtATrie = GhtType!(u32, u64, u16 => &'static str: VariadicHashSet); + type MyGhtBTrie = GhtType!(u32, u64, u16 => &'static str: VariadicHashSet); + + let mut ght_a = MyGhtATrie::default(); + let mut ght_b = MyGhtBTrie::default(); + + ght_a.insert(var_expr!(123, 2, 5, "hello")); + ght_a.insert(var_expr!(50, 1, 1, "hi")); + ght_a.insert(var_expr!(5, 1, 7, "hi")); + + ght_b.insert(var_expr!(5, 1, 8, "hi")); + ght_b.insert(var_expr!(5, 1, 7, "world")); + ght_b.insert(var_expr!(10, 1, 2, "hi")); + ght_b.insert(var_expr!(12, 10, 98, "bye")); + + let result: HashSet = [var_expr!(&5, &1, &7, &"hi", &"world")] + .iter() + .copied() + .collect(); + { + // here we manually construct the proper bimorphism stack. + // note that the bottommost bimorphism is GhtValTypeProductBimorphism, + // which ensures that the Schema of the resulting output GhtLeaf and GhtInner + // nodes correctly includes the key columns, not just the cross-product of the values. + type MyGhtOut = GhtInner< + &'static str, + GhtLeaf< + ResultSchemaType, + var_type!(&'static str), + VariadicCountedHashSet, + >, + >; + // let mut bim = GhtNodeKeyedBimorphism::new(GhtNodeKeyedBimorphism::new( + // GhtNodeKeyedBimorphism::new(GhtValTypeProductBimorphism::::default()), + // )); + let mut bim = GhtNodeKeyedBimorphism::new(GhtNodeKeyedBimorphism::new( + GhtNodeKeyedBimorphism::new(GhtValTypeProductBimorphism::::default()), + )); + let out = bim.call(&ght_a, &ght_b); + let out: HashSet = out.recursive_iter().collect(); + assert_eq!(out, result.iter().copied().collect()); + } + { + // Here we use DeepJoinLatticeBimorphism as a more compact representation of the + // manual stack of bimorphisms above. This is the recommended approach. + type MyNodeBim<'a> = <(MyGhtATrie, MyGhtBTrie) as DeepJoinLatticeBimorphism< + VariadicHashSet, + >>::DeepJoinLatticeBimorphism; + let mut bim = ::default(); + let out = bim.call(&ght_a, &ght_b); + let out: HashSet = out.recursive_iter().collect(); + assert_eq!(out, result.iter().copied().collect()); + } + } + + #[test] + fn test_ght_with_tuple_macro() { + use variadics::{var_expr, VariadicExt}; + use variadics_macro::tuple; + + use crate::ght::GeneralizedHashTrieNode; + use crate::GhtType; + + type MyRoot = GhtType!(u16, u32 => u64: VariadicCountedHashSet); + + let mut trie1 = MyRoot::default(); + assert_eq!(3, <::Schema>::LEN); + trie1.insert(var_expr!(1, 2, 3)); + let t = trie1.recursive_iter().next().unwrap(); + let tup = tuple!(t, 3); + assert_eq!(tup, (&1, &2, &3)); + } + + #[test] + fn test_triangle_generic_join() { + use std::hash::{BuildHasherDefault, DefaultHasher}; + + use variadics::var_expr; + + use crate::ght::{GeneralizedHashTrieNode, GhtPrefixIter}; + use crate::GhtType; + + const MATCHES: u32 = 1000; + type MyGht = GhtType!(u32 => u32: VariadicCountedHashSet); + + let r_iter = (0..MATCHES) + .map(|i| (0, i)) + .chain((1..MATCHES).map(|i| (i, 0))); + + let s_iter = (0..MATCHES) + .map(|i| (0, i)) + .chain((1..MATCHES).map(|i| (i, 0))); + + let t_iter = (0..MATCHES) + .map(|i| (0, i)) + .chain((1..MATCHES).map(|i| (i, 0))); + + let rx_ght = MyGht::new_from(r_iter.clone().map(|(x, y)| var_expr!(x, y))); + let sb_ght = MyGht::new_from(s_iter.clone().map(|(y, b)| var_expr!(b, y))); + let tx_ght = MyGht::new_from(t_iter.clone().map(|(z, x)| var_expr!(x, z))); + + let r_x = r_iter + .clone() + .map(|(x, _y)| x) + .collect::>>(); + let t_x = s_iter + .clone() + .map(|(_z, x)| x) + .collect::>>(); + let x_inter = r_x.intersection(&t_x); + let len = x_inter.clone().count(); + if len > 1 { + assert_eq!(1000, len); + } + + let mut output: Vec<(u32, u32, u32)> = Vec::new(); + let mut x_iters = 0usize; + let mut y_iters = 0usize; + let mut z_iters = 0usize; + for a in x_inter { + x_iters += 1; + let r = rx_ght + .prefix_iter(var_expr!(a)) + .map(|(_x, (y, ()))| *y) + .collect::>>(); + let s_y = s_iter + .clone() + .map(|(y, _z)| y) + .collect::>>(); + let y_inter = r.intersection(&s_y); + let len = y_inter.clone().count(); + if len > 1 { + assert_eq!(1000, len); + } + for b in y_inter { + y_iters += 1; + let s = sb_ght + .prefix_iter(var_expr!(b)) + .map(|(_b, (z, ()))| *z) + .collect::>>(); + let t = tx_ght + .prefix_iter(var_expr!(a)) + .map(|(_x, (z, ()))| *z) + .collect::>>(); + let z_inter = s.intersection(&t); + let len = z_inter.clone().count(); + if len > 1 { + assert_eq!(1000, len); + } + for c in z_inter { + z_iters += 1; + output.push((*a, *b, *c)); + } + } + } + + assert_eq!(1000, x_iters); + assert_eq!(1999, y_iters); + assert_eq!(2998, z_iters); + assert_eq!(2998, output.len()); + } + + fn clover_setup( + matches: usize, + ) -> ( + impl Iterator, + impl Iterator, + impl Iterator, + ) { + let r_iter = (1..matches) + .map(|i| (1u32, i as u32)) + .chain((1..matches).map(|i| (2, i as u32))) + .chain([(0, 0)]); + + let s_iter = (1..matches) + .map(|i| (2u32, i as u32)) + .chain((1..matches).map(|i| (3, i as u32))) + .chain([(0, 0)]); + + let t_iter = (1..matches) + .map(|i| (3u32, i as u32)) + .chain((1..matches).map(|i| (1, i as u32))) + .chain([(0, 0)]); + (r_iter, s_iter, t_iter) + } + + #[test] + fn clover_generic_join() { + use variadics::var_expr; + + use crate::ght::{GeneralizedHashTrieNode, GhtGet}; + use crate::GhtType; + + const MATCHES: usize = 1000; + let (r_iter, s_iter, t_iter) = clover_setup(MATCHES); + + type MyGht = GhtType!(u32 => u32: VariadicCountedHashSet); + let rx_ght = MyGht::new_from(r_iter.map(|(x, a)| var_expr!(x, a))); + let sx_ght = MyGht::new_from(s_iter.map(|(x, b)| var_expr!(x, b))); + let tx_ght = MyGht::new_from(t_iter.map(|(x, c)| var_expr!(x, c))); + for x in rx_ght.iter() { + if let (Some(r), Some(s), Some(t)) = (rx_ght.get(&x), sx_ght.get(&x), tx_ght.get(&x)) { + // All unwraps succeeded, use `r`, `s`, `t` here + for a in r.iter() { + for b in s.iter() { + for c in t.iter() { + assert_eq!((x, a, b, c), (0, 0, 0, 0)); + } + } + } + } else { + // If any unwrap fails, continue to the next iteration + continue; + } + } + } + + #[test] + fn clover_factorized_join() { + use variadics::var_expr; + + use crate::ght::{GeneralizedHashTrieNode, GhtGet}; + use crate::GhtType; + + const MATCHES: usize = 1000; + let (r_iter, s_iter, t_iter) = clover_setup(MATCHES); + + type Ght1 = GhtType!(() => u32, u32: VariadicCountedHashSet); + type Ght2 = GhtType!(u32 => u32: VariadicCountedHashSet); + let rx_ght = Ght1::new_from(r_iter.map(|(x, a)| var_expr!(x, a))); + let sx_ght = Ght2::new_from(s_iter.map(|(x, b)| var_expr!(x, b))); + let tx_ght = Ght2::new_from(t_iter.map(|(x, c)| var_expr!(x, c))); + + for t in rx_ght.recursive_iter() { + let (x, (a, ())): (&u32, (&u32, _)) = t; + if let (Some(s), Some(t)) = (sx_ght.get(x), tx_ght.get(x)) { + // All unwraps succeeded, use `s`, `t` here + for b in s.iter() { + for c in t.iter() { + assert_eq!((x, a, b, c), (&0, &0, 0, 0)); + } + } + } else { + // If any unwrap fails, continue to the next iteration + continue; + } + } + } + + #[test] + fn test_force() { + use variadics::var_expr; + + use crate::ght::colt::ColtForestNode; + use crate::ght::GeneralizedHashTrieNode; + use crate::GhtType; + + type LeafType = GhtType!(() => u16, u32, u64: VariadicCountedHashSet); + let n = LeafType::new_from(vec![ + var_expr!(1, 1, 1), + var_expr!(1, 2, 2), + var_expr!(1, 3, 3), + var_expr!(2, 4, 4), + ]); + let out = n.force().unwrap(); + assert_eq!(out.height(), 1); + } + + #[test] + fn test_forest_macro() { + use crate::ColtType; + + type Forest4 = ColtType!(u8, u16, u32, u64); + let _f4 = Forest4::default(); + + type Forest3 = ColtType!(u8, u16, u32); + let _f3 = Forest3::default(); + + type Forest2 = ColtType!(u8, u16); + let _f2 = Forest2::default(); + + type Forest1 = ColtType!(u8); + let _f2 = Forest1::default(); + + type Forest01 = ColtType!(() => u16); + let _f01 = Forest01::default(); + + type Forest02 = ColtType!(() => u8, u16); + let _f02 = Forest02::default(); + + type Forest10 = ColtType!(u8 => ()); + let _f10 = Forest10::default(); + + type Forest11 = ColtType!(u8 => u16); + let _f11 = Forest11::default(); + + type Forest12 = ColtType!(u8 => u16, u32); + let _f12 = Forest12::default(); + + type Forest20 = ColtType!(u8, u16 => ()); + let _f20 = Forest20::default(); + + type Forest21 = ColtType!(u8, u16 => u32); + let _f21 = Forest21::default(); + + type Forest22 = ColtType!(u8, u16 => u32, u64); + let _f22 = Forest22::default(); + } + + #[test] + fn test_colt_little_get() { + use variadics::variadic_collections::VariadicCollection; + use variadics::{var_expr, VariadicExt}; + + use crate::ght::colt::ColtGet; + use crate::ght::GeneralizedHashTrieNode; + use crate::ColtType; + + type MyForest = ColtType!(u8); + + let mut forest = MyForest::default(); + + forest.0.insert(var_expr!(1)); + forest.0.insert(var_expr!(2)); + forest.0.insert(var_expr!(3)); + + assert_eq!(2, forest.len()); + assert_eq!(3, forest.0.elements.len()); + + let result = ColtGet::get(forest.as_mut_var(), &3); + assert_eq!(1, result.len()); + assert_eq!(0, forest.0.elements.len()); + assert!(forest.0.forced); + } + + #[test] + fn test_colt_get() { + use variadics::variadic_collections::VariadicCollection; + use variadics::{var_expr, VariadicExt}; + + use crate::ght::colt::ColtGet; + use crate::ght::{GeneralizedHashTrieNode, GhtGet}; + use crate::ColtType; + + type MyForest = ColtType!(u8, u16, u32, u64); + let mut forest = MyForest::default(); + forest.0.insert(var_expr!(1, 1, 1, 1)); + forest.0.insert(var_expr!(2, 2, 2, 2)); + forest.0.insert(var_expr!(3, 3, 3, 3)); + + let len = forest.len(); + assert_eq!(5, len); + { + let get_result = ColtGet::get(forest.as_mut_var(), &1); + assert_eq!(get_result.len(), len - 1); + assert_eq!(get_result.0.height(), 0); + let get_result2 = ColtGet::get(get_result, &1); + assert_eq!(get_result2.len(), len - 2); + let get_result3 = ColtGet::get(get_result2, &1); + assert_eq!(get_result3.len(), len - 3); + assert_eq!( + get_result3.0.elements.iter().next(), + Some(var_expr!(1, 1, 1, 1).as_ref_var()) + ); + assert_eq!(get_result3.1 .0.children.len(), 0); + } + { + let get_result = ColtGet::get(forest.as_mut_var(), &3); + assert_eq!(get_result.len(), len - 1); + let get_result2 = ColtGet::get(get_result, &3); + assert_eq!(get_result2.len(), len - 2); + assert_eq!( + get_result2.0.elements.iter().next(), + Some(var_expr!(3, 3, 3, 3).as_ref_var()) + ); + assert_eq!(get_result2.1 .0.children.len(), 0); + } + assert!(forest.0.forced); + assert_eq!(3, forest.1 .0.children.len()); // keys 1, 2 and 3 + assert_eq!(0, forest.1 .0.get(&1).unwrap().elements.len()); + assert_eq!(1, forest.1 .0.get(&2).unwrap().elements.len()); + assert_eq!(0, forest.1 .0.get(&3).unwrap().elements.len()); + assert_eq!(2, forest.1 .1 .0.children.len()); // keys 1 and 3 + assert_eq!( + 0, + forest + .1 + .1 + .0 + .get(&1) + .unwrap() + .get(&1) + .unwrap() + .elements + .len() + ); + assert!(forest.1 .1 .0.get(&2).is_none()); + assert_eq!( + 1, + forest + .1 + .1 + .0 + .get(&3) + .unwrap() + .get(&3) + .unwrap() + .elements + .len() + ); + assert_eq!( + 1, + forest + .1 + .1 + .1 + .0 + .get(&1) + .unwrap() + .get(&1) + .unwrap() + .get(&1) + .unwrap() + .elements + .len() + ); + } + + #[test] + fn test_colt_scale() { + use variadics::variadic_collections::VariadicCollection; + use variadics::{var_expr, VariadicExt}; + + use crate::ght::colt::ColtGet; + use crate::ght::{GeneralizedHashTrieNode, GhtPrefixIter}; + + type MyColt = crate::ColtType!(i32, bool, usize, &'static str); + let mut forest = MyColt::default(); + for i in 1..100000 { + forest.0.insert(var_expr!(i, true, 1, "hello")); + } + { + let result = forest.as_mut_var().get(&3); + assert_eq!(result.len(), 4); + } + // check: first Leaf trie is forced + assert!(forest.0.forced); + assert_eq!(forest.0.elements.len(), 0); + { + let result = forest.as_mut_var().get(&3); + let result2 = result.get(&true); + assert_eq!(result2.len(), 3); + } + { + // check: leaf below 3 in first non-empty trie is forced + let result = forest.as_mut_var().get(&3); + assert!(result.0.forced); + assert_eq!(result.0.elements.len(), 0); + } + // check: prefix (3, true) is now found in the third trie: forest.1.1.0 + assert!(forest + .1 + .1 + .0 + .prefix_iter(var_expr!(3, true).as_ref_var()) + .next() + .is_some()); + { + let result = forest.as_mut_var().get(&3); + let result2 = result.get(&true); + assert_eq!(result2.len(), 3); + let result3 = result2.get(&1); + assert_eq!(result3.len(), 2); + let result4 = result3.get(&"hello"); + assert_eq!(result4.0.elements.len(), 1); + assert_eq!( + result4.0.elements.iter().next(), + Some(var_expr!(3, true, 1, "hello").as_ref_var()) + ); + } + } +} diff --git a/lattices/src/lib.rs b/lattices/src/lib.rs index c8fc0804819e..bac63c543b16 100644 --- a/lattices/src/lib.rs +++ b/lattices/src/lib.rs @@ -3,14 +3,15 @@ use std::cmp::Ordering::{self, *}; -pub use cc_traits; use sealed::sealed; +pub use {cc_traits, variadics}; /// Module for definiting algebraic structures and properties. pub mod algebra; pub mod collections; mod conflict; mod dom_pair; +pub mod ght; pub mod map_union; pub mod map_union_with_tombstones; mod ord; diff --git a/lattices/tests/compile-fail/non_lattice_field.stderr b/lattices/tests/compile-fail/non_lattice_field.stderr index f2630b97f64e..7b8e66201e53 100644 --- a/lattices/tests/compile-fail/non_lattice_field.stderr +++ b/lattices/tests/compile-fail/non_lattice_field.stderr @@ -8,11 +8,11 @@ error[E0277]: the trait bound `String: Merge` is not satisfied `()` implements `Merge<()>` `Conflict` implements `Merge>` `DomPair` implements `Merge>` + `GhtInner` implements `Merge>` + `GhtLeaf` implements `Merge>` `MapUnion` implements `Merge>` `MapUnionWithTombstones` implements `Merge>` `Max` implements `Merge>` - `Min` implements `Merge>` - `NotALattice` implements `Merge` and $N others = help: see issue #48214 = note: this error originates in the derive macro `Lattice` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -31,11 +31,11 @@ error[E0277]: the trait bound `String: IsBot` is not satisfied () Conflict DomPair + GhtInner + GhtLeaf MapUnion MapUnionWithTombstones Max<()> - Max - Max and $N others = help: see issue #48214 = note: this error originates in the derive macro `Lattice` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -54,11 +54,11 @@ error[E0277]: the trait bound `String: IsTop` is not satisfied () Conflict DomPair + GhtInner + GhtLeaf MapUnion MapUnionWithTombstones Max<()> - Max - Max and $N others = help: see issue #48214 = note: this error originates in the derive macro `Lattice` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/variadics/src/variadic_collections.rs b/variadics/src/variadic_collections.rs index 1a1fa28b6de6..5dcc8fb923a4 100644 --- a/variadics/src/variadic_collections.rs +++ b/variadics/src/variadic_collections.rs @@ -6,7 +6,7 @@ use hashbrown::hash_table::{Entry, HashTable}; use crate::{PartialEqVariadic, VariadicExt, VecVariadic}; /// Trait for a set of Variadic Tuples -pub trait VariadicCollection { +pub trait VariadicCollection: Extend { /// The Schema (aka Variadic type) associated with tuples in this set type Schema: PartialEqVariadic; @@ -58,7 +58,7 @@ impl Default for VariadicHashSet { impl fmt::Debug for VariadicHashSet where - T: fmt::Debug + VariadicExt + PartialEqVariadic, + T: fmt::Debug + VariadicExt + PartialEqVariadic + Eq + Hash, for<'a> T::AsRefVar<'a>: Hash + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -82,7 +82,7 @@ where } impl VariadicCollection for VariadicHashSet where - T: VariadicExt + PartialEqVariadic, + T: VariadicExt + PartialEqVariadic + Eq + Hash, for<'a> T::AsRefVar<'a>: Hash, S: BuildHasher, { @@ -127,7 +127,7 @@ where } impl VariadicSet for VariadicHashSet where - T: VariadicExt + PartialEqVariadic, + T: VariadicExt + PartialEqVariadic + Eq + Hash, for<'a> T::AsRefVar<'a>: Hash, S: BuildHasher, { @@ -283,7 +283,7 @@ where impl VariadicCollection for VariadicCountedHashSet where - K: VariadicExt + PartialEqVariadic + Hash + Clone, + K: VariadicExt + PartialEqVariadic + Eq + Hash + Clone, for<'a> K::AsRefVar<'a>: Hash, S: BuildHasher, { @@ -333,7 +333,7 @@ where impl VariadicMultiset for VariadicCountedHashSet where - K: VariadicExt + PartialEqVariadic + Hash + Clone, + K: VariadicExt + PartialEqVariadic + Eq + Hash + Clone, for<'a> K::AsRefVar<'a>: Hash, S: BuildHasher, { @@ -356,6 +356,7 @@ where } /// Iterator helper for [`VariadicCountedHashSet::into_iter`]. +#[derive(Clone)] pub struct DuplicateCounted { iter: Iter, state: Option<(Item, usize)>, @@ -474,9 +475,10 @@ where /// Column storage for Variadic tuples of type Schema /// An alternative to VariadicHashMultiset +#[derive(Clone)] pub struct VariadicColumnMultiset where - Schema: VariadicExt, + Schema: VariadicExt + Eq + Hash, { columns: Schema::IntoVec, last_offset: usize, @@ -484,7 +486,7 @@ where impl VariadicColumnMultiset where - T: VariadicExt, + T: VariadicExt + Eq + Hash, { /// initialize an empty columnar multiset pub fn new() -> Self { @@ -497,7 +499,7 @@ where impl Default for VariadicColumnMultiset where - T: VariadicExt, + T: VariadicExt + Eq + Hash, { fn default() -> Self { Self::new() @@ -506,7 +508,8 @@ where impl VariadicCollection for VariadicColumnMultiset where - Schema: PartialEqVariadic, + Schema: PartialEqVariadic + Eq + Hash, + for<'a> ::AsRefVar<'a>: Hash, { type Schema = Schema; @@ -543,11 +546,16 @@ where } } -impl VariadicMultiset for VariadicColumnMultiset where Schema: PartialEqVariadic {} +impl VariadicMultiset for VariadicColumnMultiset +where + Schema: PartialEqVariadic + Eq + Hash, + for<'a> ::AsRefVar<'a>: Hash, +{ +} impl fmt::Debug for VariadicColumnMultiset where - T: fmt::Debug + VariadicExt + PartialEqVariadic, + T: fmt::Debug + VariadicExt + PartialEqVariadic + Eq + Hash, for<'a> T::AsRefVar<'a>: Hash + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -557,7 +565,7 @@ where impl IntoIterator for VariadicColumnMultiset where - Schema: PartialEqVariadic, + Schema: PartialEqVariadic + Eq + Hash, { type Item = Schema; type IntoIter = ::IntoZip; diff --git a/variadics_macro/CHANGELOG.md b/variadics_macro/CHANGELOG.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/variadics_macro/Cargo.toml b/variadics_macro/Cargo.toml new file mode 100644 index 000000000000..2c8ef8b0944c --- /dev/null +++ b/variadics_macro/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "variadics_macro" +publish = true +version = "0.5.5" +edition = "2021" +license = "Apache-2.0" +documentation = "https://docs.rs/variadics/" +description = "Procedural macros for the `variadics` crate." + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.63" +proc-macro-crate = "1.1.0" +quote = "1.0.0" +syn = { version = "2.0.0", features = [ "full", "parsing", "visit-mut" ] } +variadics = { path = "../variadics", version = "^0.0.6" } + +[dev-dependencies] +insta = "1.7.1" +prettyplease = "0.2.20" diff --git a/variadics_macro/README.md b/variadics_macro/README.md new file mode 100644 index 000000000000..5f2c0e520895 --- /dev/null +++ b/variadics_macro/README.md @@ -0,0 +1,17 @@ +## `tuple!` Macro + +Create a tuple from a Variadic type known at compile time. + +Example usage: +```rust +use variadics::var_expr; +use variadics_macro::tuple; + +let tup = var_expr!(1, 2, 3, "four"); +let a = tuple!(tup, 4); +assert_eq!(a, (1, 2, 3, "four")); + +let tup = var_expr!(1, 2, var_expr!(3)); +let b = tuple!(tup, 3); +assert_eq!(b, (1, 2, (3, ()))); +``` \ No newline at end of file diff --git a/variadics_macro/src/lib.rs b/variadics_macro/src/lib.rs new file mode 100644 index 000000000000..2583b3584268 --- /dev/null +++ b/variadics_macro/src/lib.rs @@ -0,0 +1,47 @@ +#![doc = include_str!("../README.md")] +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::Ident; +use quote::{format_ident, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::{parse_macro_input, LitInt}; + +struct InputLen { + input: Ident, + len: LitInt, +} + +impl Parse for InputLen { + fn parse(ts: ParseStream) -> syn::Result { + let input = ts.parse()?; + ts.parse::()?; + let len = ts.parse()?; + Ok(InputLen { input, len }) + } +} + +#[proc_macro] +pub fn tuple(ts: TokenStream) -> TokenStream { + let InputLen { input, len } = parse_macro_input!(ts as InputLen); + let len = len.base10_parse::().unwrap(); + let pattern = (0..len) + .rev() + .map(|i| format_ident!("x{}", i)) + .fold(quote! { () }, |rest, item| quote! { (#item, #rest) }); + let idents = (0..len).map(|i| format_ident!("x{}", i)); + let tuple = quote! { + ( #( #idents, )* ) + }; + + // Create the assignment statement + let expanded = quote! { + { + let #pattern = #input; + let retval = #tuple; + retval + } + }; + + expanded.into() +} From dff2a40669736014349cf12744d6a057a7992e11 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 30 Oct 2024 14:07:46 -0700 Subject: [PATCH 06/74] refactor(paxos): start splitting out leader election into a separate module (#1485) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1485). * #1493 * #1492 * #1489 * #1488 * #1487 * #1486 * __->__ #1485 --- hydroflow_plus/src/singleton.rs | 4 + hydroflow_plus_test/src/cluster/paxos.rs | 240 ++-- ...cluster__paxos_bench__tests__paxos_ir.snap | 1035 +++++++++-------- 3 files changed, 633 insertions(+), 646 deletions(-) diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 411404226967..54374e6c3f14 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -580,6 +580,10 @@ impl<'a, T, N: Location<'a>> Optional { .map(q!(|(d, _signal)| d)) } + pub fn then(self, value: Singleton) -> Optional { + value.continue_if(self) + } + pub fn continue_unless( self, other: Optional, diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 488be19c01e4..0667dae4e9d4 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -21,12 +21,12 @@ pub trait PaxosPayload: pub struct Ballot { // Note: Important that num comes before id, since Ord is defined lexicographically pub num: u32, - pub id: ClusterId, + pub proposer_id: ClusterId, } impl LeaderElected for Ballot { fn leader_id(&self) -> ClusterId { - self.id + self.proposer_id } } @@ -57,8 +57,8 @@ struct P2a

{ #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] struct P2b

{ + victory: bool, ballot: Ballot, - max_ballot: Ballot, slot: i32, value: P, } @@ -119,7 +119,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( let (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) = p_p1b( &proposers, - a_to_proposers_p1b.inspect(q!(|(_, p1b)| println!("Proposer received P1b: {:?}", p1b))), + a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), p_ballot_num.clone(), p_has_largest_ballot, f, @@ -152,7 +152,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( let p_to_clients_new_leader_elected = p_is_leader.clone() .continue_unless(p_next_slot) .cross_singleton(p_ballot_num) - .map(q!(move |(_is_leader, ballot_num)| Ballot { num: ballot_num, id: p_id})) // Only tell the clients once when leader election concludes + .map(q!(move |(_is_leader, ballot_num)| Ballot { num: ballot_num, proposer_id: p_id})) // Only tell the clients once when leader election concludes .all_ticks(); // End tell clients that leader election has completed let p_to_replicas = p_p2b(&proposers, a_to_proposers_p2b, f); @@ -197,8 +197,8 @@ fn acceptor<'a, P: PaxosPayload, R>( acceptors: &Cluster<'a, Acceptor>, f: usize, ) -> ( - Stream<(ClusterId, P1b

), Unbounded, NoTick, Cluster<'a, Proposer>>, - Stream<(ClusterId, P2b

), Unbounded, NoTick, Cluster<'a, Proposer>>, + Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, ) { // Get the latest checkpoint sequence per replica let a_checkpoint_largest_seqs = @@ -229,7 +229,7 @@ fn acceptor<'a, P: PaxosPayload, R>( // Create tuple with checkpoint number and dummy p2a ballot: Ballot { num: 0, - id: ClusterId::from_raw(0) + proposer_id: ClusterId::from_raw(0) }, slot: -1, value: Default::default() @@ -243,7 +243,7 @@ fn acceptor<'a, P: PaxosPayload, R>( .max() .unwrap_or(acceptors.singleton(q!(Ballot { num: 0, - id: ClusterId::from_raw(0) + proposer_id: ClusterId::from_raw(0) }))); let a_p2as_to_place_in_log = p_to_acceptors_p2a .clone() @@ -303,7 +303,7 @@ fn acceptor<'a, P: PaxosPayload, R>( .cross_singleton(a_max_ballot.clone().latest_tick()) .cross_singleton(a_log) .map(q!(|((p1a, max_ballot), (_prev_checkpoint, log))| ( - p1a.ballot.id, + p1a.ballot.proposer_id, P1b { ballot: p1a.ballot, max_ballot, @@ -311,33 +311,27 @@ fn acceptor<'a, P: PaxosPayload, R>( } ))) .all_ticks() - .send_bincode(proposers); + .send_bincode_interleaved(proposers); let a_to_proposers_p2b_new = p_to_acceptors_p2a .tick_batch() .cross_singleton(a_max_ballot.latest_tick()) .map(q!(|(p2a, max_ballot)| ( - p2a.ballot.id, + p2a.ballot.proposer_id, P2b { + victory: p2a.ballot == max_ballot, ballot: p2a.ballot, - max_ballot, slot: p2a.slot, value: p2a.value } ))) .all_ticks() - .send_bincode(proposers); + .send_bincode_interleaved(proposers); (a_to_proposers_p1b_new, a_to_proposers_p2b_new) } -#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_p2b<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - a_to_proposers_p2b: Stream< - (ClusterId, P2b

), - Unbounded, - NoTick, - Cluster<'a, Proposer>, - >, + a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, f: usize, ) -> Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>> { let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposers.tick_cycle(); @@ -348,62 +342,55 @@ fn p_p2b<'a, P: PaxosPayload>( .union(p_persisted_p2bs); let p_count_matching_p2bs = p_p2b .clone() - .filter_map(q!(|(sender, p2b)| if p2b.ballot == p2b.max_ballot { + .filter_map(q!(|p2b| if p2b.victory { // Only consider p2bs where max ballot = ballot, which means that no one preempted us - Some((p2b.slot, (sender, p2b))) + Some(((p2b.slot, p2b.ballot), p2b.value)) } else { None })) .fold_keyed( - q!(|| ( - 0, - P2b { - ballot: Ballot { - num: 0, - id: ClusterId::from_raw(0) - }, - max_ballot: Ballot { - num: 0, - id: ClusterId::from_raw(0) - }, - slot: 0, - value: Default::default() - } - )), - q!(|accum, (_sender, p2b)| { + q!(|| (0, Default::default())), + q!(|accum, value| { + // TODO(shadaj): why is sender unused? should we de-dup? accum.0 += 1; - accum.1 = p2b; + accum.1 = value; }), ); - let p_p2b_quorum_reached = p_count_matching_p2bs - .clone() - .filter(q!(move |(_slot, (count, _p2b))| *count > f)); + let p_p2b_quorum_reached = + p_count_matching_p2bs + .clone() + .filter_map(q!(move |((slot, _ballot), (count, value))| if count > f { + Some((slot, value)) + } else { + None + })); let p_to_replicas = p_p2b_quorum_reached .clone() .anti_join(p_broadcasted_p2b_slots) // Only tell the replicas about committed values once - .map(q!(|(_slot, (_count, p2b))| (p2b.slot, p2b.value))) + .map(q!(|(slot, value)| (slot, value))) .all_ticks(); let p_p2b_all_commit_slots = - p_count_matching_p2bs - .clone() - .filter_map(q!(move |(slot, (count, _p2b))| if count == 2 * f + 1 { + p_count_matching_p2bs.clone().filter_map(q!( + move |((slot, _ballot), (count, _p2b))| if count == 2 * f + 1 { Some(slot) } else { None - })); + } + )); // p_p2b_all_commit_slots.inspect(q!(|slot: i32| println!("Proposer slot all received: {:?}", slot))); let p_broadcasted_p2b_slots_new = p_p2b_quorum_reached .clone() - .map(q!(|(slot, (_count, _p2b))| slot)) + .map(q!(|(slot, _value)| slot)) .filter_not_in(p_p2b_all_commit_slots.clone()); // p_broadcasted_p2b_slots_new.inspect(q!(|slot: i32| println!("Proposer slot broadcasted: {:?}", slot))); p_broadcasted_p2b_slots_complete_cycle.complete_next_tick(p_broadcasted_p2b_slots_new); let p_persisted_p2bs_new = p_p2b .clone() - .map(q!(|(sender, p2b)| (p2b.slot, (sender, p2b)))) + .map(q!(|p2b| (p2b.slot, p2b))) .anti_join(p_p2b_all_commit_slots.clone()) - .map(q!(|(_slot, (sender, p2b))| (sender, p2b))); + .map(q!(|(_slot, p2b)| p2b)); + // TOOD: only persist if we are the leader // p_persisted_p2bs_new.inspect(q!(|(sender, p2b): (u32, P2b)| println!("Proposer persisting p2b: {:?}", p2b))); p_persisted_p2bs_complete_cycle.complete_next_tick(p_persisted_p2bs_new); p_to_replicas @@ -446,7 +433,7 @@ fn p_p2a<'a, P: PaxosPayload>( // .inspect(q!(|next| println!("{} p_indexed_payloads next slot: {}", context.current_tick(), next)))) .cross_singleton(p_ballot_num.clone()) // .inspect(q!(|ballot_num| println!("{} p_indexed_payloads ballot_num: {}", context.current_tick(), ballot_num)))) - .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { ballot: Ballot { num: ballot_num, id: p_id }, slot: next_slot + index as i32, value: payload })); + .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { ballot: Ballot { num: ballot_num, proposer_id: p_id }, slot: next_slot + index as i32, value: payload })); // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); let p_to_acceptors_p2a = p_log_to_try_commit .union(p_log_holes) @@ -489,12 +476,7 @@ fn p_p2a<'a, P: PaxosPayload>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_p1b<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - a_to_proposers_p1b: Stream< - (ClusterId, P1b

), - Unbounded, - NoTick, - Cluster<'a, Proposer>, - >, + a_to_proposers_p1b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, p_ballot_num: Singleton>, p_has_largest_ballot: Optional<(Ballot, u32), Bounded, Tick, Cluster<'a, Proposer>>, f: usize, @@ -506,30 +488,26 @@ fn p_p1b<'a, P: PaxosPayload>( ) { let p_id = proposers.self_id(); let p_relevant_p1bs = a_to_proposers_p1b - .clone() .tick_prefix() + // NOTE: because `p_ballot_num` grows monotonically across ticks, we could garbage gollect + // but we don't do that here since leader election is a rare event .cross_singleton(p_ballot_num.clone()) - .filter(q!(move |((_sender, p1b), ballot_num)| p1b.ballot - == Ballot { - num: *ballot_num, - id: p_id + .filter(q!(move |(p1b, ballot_num)| p1b.ballot.num == *ballot_num && p1b.ballot.proposer_id == p_id)) + .map(q!(|t| t.0)); + let p_received_quorum_of_p1bs = + p_relevant_p1bs + .clone() + .count() + .filter_map(q!(move |num_received| if num_received > f { + Some(true) + } else { + None })); - let p_received_quorum_of_p1bs = p_relevant_p1bs - .clone() - .map(q!(|((sender, _p1b), _ballot_num)| { sender })) - .unique() - .count() - .filter_map(q!(move |num_received| if num_received > f { - Some(true) - } else { - None - })); let p_is_leader = p_received_quorum_of_p1bs.continue_if(p_has_largest_ballot.clone()); let p_p1b_highest_entries_and_count = p_relevant_p1bs - .clone() - .flat_map(q!(|((_, p1b), _)| p1b.accepted.into_iter())) // Convert HashMap log back to stream - .fold_keyed(q!(|| (0, LogValue { ballot: Ballot { num: 0, id: ClusterId::from_raw(0) }, value: Default::default() })), q!(|curr_entry, new_entry| { + .flat_map(q!(|p1b| p1b.accepted.into_iter())) // Convert HashMap log back to stream + .fold_keyed(q!(|| (0, LogValue { ballot: Ballot { num: 0, proposer_id: ClusterId::from_raw(0) }, value: Default::default() })), q!(|curr_entry, new_entry| { let same_values = new_entry.value == curr_entry.1.value; let higher_ballot = new_entry.ballot > curr_entry.1.ballot; // Increment count if the values are the same @@ -554,7 +532,7 @@ fn p_p1b<'a, P: PaxosPayload>( Some(P2a { ballot: Ballot { num: ballot_num, - id: p_id, + proposer_id: p_id, }, slot, value: entry.value, @@ -578,7 +556,7 @@ fn p_p1b<'a, P: PaxosPayload>( .map(q!(move |(slot, ballot_num)| P2a { ballot: Ballot { num: ballot_num, - id: p_id + proposer_id: p_id }, slot, value: Default::default() @@ -587,36 +565,21 @@ fn p_p1b<'a, P: PaxosPayload>( } // Proposer logic to calculate the largest ballot received so far. -#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_max_ballot<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - a_to_proposers_p1b: Stream< - (ClusterId, P1b

), - Unbounded, - NoTick, - Cluster<'a, Proposer>, - >, - a_to_proposers_p2b: Stream< - (ClusterId, P2b

), - Unbounded, - NoTick, - Cluster<'a, Proposer>, - >, + a_to_proposers_p1b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, p_to_proposers_i_am_leader: Stream>, ) -> Singleton> { - let p_received_p1b_ballots = a_to_proposers_p1b - .clone() - .map(q!(|(_, p1b)| p1b.max_ballot)); - let p_received_p2b_ballots = a_to_proposers_p2b - .clone() - .map(q!(|(_, p2b)| p2b.max_ballot)); + let p_received_p1b_ballots = a_to_proposers_p1b.clone().map(q!(|p1b| p1b.max_ballot)); + let p_received_p2b_ballots = a_to_proposers_p2b.clone().map(q!(|p2b| p2b.ballot)); p_received_p1b_ballots .union(p_received_p2b_ballots) .union(p_to_proposers_i_am_leader) .max() .unwrap_or(proposers.singleton(q!(Ballot { num: 0, - id: ClusterId::from_raw(0) + proposer_id: ClusterId::from_raw(0) }))) } @@ -640,7 +603,7 @@ fn p_ballot_calc<'a>( if received_max_ballot > (Ballot { num: ballot_num, - id: p_id, + proposer_id: p_id, }) { received_max_ballot.num + 1 @@ -657,7 +620,7 @@ fn p_ballot_calc<'a>( move |(received_max_ballot, ballot_num)| *received_max_ballot <= Ballot { num: *ballot_num, - id: p_id + proposer_id: p_id } )); @@ -685,21 +648,55 @@ fn p_p1a<'a>( Stream>, ) { let p_id = proposers.self_id(); - let p_to_proposers_i_am_leader_new = p_ballot_num + let p_to_proposers_i_am_leader_new = p_is_leader .clone() + .then(p_ballot_num.clone()) + .latest() + .sample_every(q!(Duration::from_secs(i_am_leader_send_timeout))) + .map(q!(move |ballot_num| Ballot { + num: ballot_num, + proposer_id: p_id + })) + .broadcast_bincode_interleaved(proposers); + + let p_leader_expired = p_leader_expired( + p_to_proposers_i_am_leader, + p_is_leader, + i_am_leader_check_timeout, + ); + + let p_id = proposers.self_id(); + + // Add random delay depending on node ID so not everyone sends p1a at the same time + let p_to_acceptors_p1a = p_leader_expired + .then(p_ballot_num) .continue_if( proposers - .source_interval(q!(Duration::from_secs(i_am_leader_send_timeout))) + .source_interval_delayed( + q!(Duration::from_secs( + (p_id.raw_id * i_am_leader_check_timeout_delay_multiplier as u32).into() + )), + q!(Duration::from_secs(i_am_leader_check_timeout)), + ) .latest_tick(), ) - .continue_if(p_is_leader.clone()) - .map(q!(move |ballot_num| Ballot { - num: ballot_num, - id: p_id + .map(q!(move |ballot_num| P1a { + ballot: Ballot { + num: ballot_num, + proposer_id: p_id + } })) .all_ticks() - .broadcast_bincode_interleaved(proposers); + .inspect(q!(|_| println!("Proposer leader expired, sending P1a"))) + .broadcast_bincode_interleaved(acceptors); + (p_to_proposers_i_am_leader_new, p_to_acceptors_p1a) +} +fn p_leader_expired<'a>( + p_to_proposers_i_am_leader: Stream>, + p_is_leader: Optional>, + i_am_leader_check_timeout: u64, // How often to check if heartbeat expired +) -> Optional, Bounded, Tick, Cluster<'a, Proposer>> { let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold( q!(|| None), q!(|latest, _| { @@ -707,34 +704,17 @@ fn p_p1a<'a>( *latest = Some(Instant::now()); }), ); - // Add random delay depending on node ID so not everyone sends p1a at the same time - let p_leader_expired = proposers.source_interval_delayed(q!(Duration::from_secs((p_id.raw_id * i_am_leader_check_timeout_delay_multiplier as u32).into())), q!(Duration::from_secs(i_am_leader_check_timeout))) - .cross_singleton(p_latest_received_i_am_leader.clone()) + + p_latest_received_i_am_leader + .clone() .latest_tick() - // .inspect(q!(|v| println!("Proposer checking if leader expired"))) - // .continue_if(p_is_leader.clone().count().filter(q!(|c| *c == 0)).inspect(q!(|c| println!("Proposer is_leader count: {}", c)))) .continue_unless(p_is_leader) - .filter(q!(move |(_, latest_received_i_am_leader)| { + .filter(q!(move |latest_received_i_am_leader| { if let Some(latest_received_i_am_leader) = latest_received_i_am_leader { - (Instant::now().duration_since(*latest_received_i_am_leader)) > Duration::from_secs(i_am_leader_check_timeout) + (Instant::now().duration_since(*latest_received_i_am_leader)) + > Duration::from_secs(i_am_leader_check_timeout) } else { true } - })); - p_leader_expired - .clone() - .all_ticks() - .for_each(q!(|_| println!("Proposer leader expired"))); - - let p_to_acceptors_p1a = p_ballot_num - .continue_if(p_leader_expired) - .map(q!(move |ballot_num| P1a { - ballot: Ballot { - num: ballot_num, - id: p_id - } })) - .all_ticks() - .broadcast_bincode_interleaved(acceptors); - (p_to_proposers_i_am_leader_new, p_to_acceptors_p1a) } diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index f20193cd83b1..af57d72b873f 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -22,7 +22,7 @@ expression: built.ir() 2, ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , id : p_id , }) { received_max_ballot . num + 1 } else { ballot_num } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , proposer_id : p_id , }) { received_max_ballot . num + 1 } else { ballot_num } } }), input: CrossSingleton( Tee { inner: : Union( @@ -32,7 +32,7 @@ expression: built.ir() Union( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ , p1b) | p1b . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . max_ballot }), input: Tee { inner: : CycleSource { ident: Ident { @@ -45,7 +45,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ , p2b) | p2b . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . ballot }), input: Tee { inner: : CycleSource { ident: Ident { @@ -74,7 +74,7 @@ expression: built.ir() Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , id : ClusterId :: from_raw (0) } } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, ), location_kind: Cluster( 2, @@ -108,105 +108,6 @@ expression: built.ir() ), }, }, - ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < (tokio :: time :: Instant , core :: option :: Option < tokio :: time :: Instant >) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired") }), - input: Tee { - inner: : Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (tokio :: time :: Instant , core :: option :: Option < tokio :: time :: Instant >) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | (_ , latest_received_i_am_leader) | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((tokio :: time :: Instant , core :: option :: Option < tokio :: time :: Instant >) , ()) , (tokio :: time :: Instant , core :: option :: Option < tokio :: time :: Instant >) > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - CrossSingleton( - Source { - source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, - ), - location_kind: Cluster( - 2, - ), - }, - Tee { - inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), - input: Persist( - Tee { - inner: , - }, - ), - }, - }, - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Unique( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , u32) , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((sender , _p1b) , _ballot_num) | { sender } }), - input: Tee { - inner: : Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((_sender , p1b) , ballot_num) | p1b . ballot == Ballot { num : * ballot_num , id : p_id } }), - input: CrossSingleton( - Persist( - Tee { - inner: : Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ , p1b) | println ! ("Proposer received P1b: {:?}" , p1b) }), - input: Tee { - inner: , - }, - }, - }, - ), - Tee { - inner: , - }, - ), - }, - }, - }, - ), - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Tee { - inner: : Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , id : p_id } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - }, - }, - ), - }, - }, - }, - }, - }, - ), - }, - }, - }, - }, CycleSink { ident: Ident { sym: cycle_0, @@ -249,7 +150,7 @@ expression: built.ir() input: FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | Ballot { num : ballot_num , id : p_id } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | Ballot { num : ballot_num , proposer_id : p_id } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( @@ -260,22 +161,70 @@ expression: built.ir() inner: , }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Source { - source: Interval( - { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) }, - ), - location_kind: Cluster( - 2, - ), + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), + input: CrossSingleton( + Persist( + Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + input: Tee { + inner: , + }, + }, + ), + Tee { + inner: , + }, + ), + }, + }, + }, + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Tee { + inner: : Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, + }, + }, + ), + }, }, }, ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Tee { - inner: , + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Source { + source: Interval( + stageleft :: runtime_support :: type_hint :: < core :: time :: Duration > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) }), + ), + location_kind: Cluster( + 2, + ), }, }, ), @@ -295,7 +244,7 @@ expression: built.ir() input: DeferTick( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( @@ -307,18 +256,18 @@ expression: built.ir() input: CrossSingleton( Union( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , u32) , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((_ , p1b) , _) | p1b . accepted . into_iter () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { - inner: , + inner: , }, }, }, @@ -345,7 +294,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_4, }, @@ -364,15 +313,15 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads as i32 }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -420,10 +369,10 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: : Filter { + inner: : Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | num_payloads | * num_payloads > 0 }), input: Tee { - inner: , + inner: , }, }, }, @@ -432,7 +381,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -441,7 +390,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -451,7 +400,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -462,7 +411,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -474,7 +423,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -484,7 +433,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -505,18 +454,18 @@ expression: built.ir() input: DeferTick( Difference( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , (_count , _p2b)) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { - inner: : Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (i32 , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | (_slot , (count , _p2b)) | * count > f }), + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { - inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , P2b { ballot : Ballot { num : 0 , id : ClusterId :: from_raw (0) } , max_ballot : Ballot { num : 0 , id : ClusterId :: from_raw (0) } , slot : 0 , value : Default :: default () }) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , (_sender , p2b) | { accum . 0 += 1 ; accum . 1 = p2b ; } }), + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , core :: option :: Option < (i32 , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (sender , p2b) | if p2b . ballot == p2b . max_ballot { Some ((p2b . slot , (sender , p2b))) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . victory { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { - inner: : Union( + inner: : Union( Tee { inner: , }, @@ -537,10 +486,10 @@ expression: built.ir() }, }, Tee { - inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | (slot , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -556,16 +505,16 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , (sender , p2b)) | (sender , p2b) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), input: AntiJoin( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , (i32 , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (sender , p2b) | (p2b . slot , (sender , p2b)) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { - inner: , + inner: , }, }, Tee { - inner: , + inner: , }, ), }, @@ -589,355 +538,406 @@ expression: built.ir() location_kind: Cluster( 2, ), - input: Network { - from_location: Cluster( - 3, - ), - from_key: None, - to_location: Cluster( - 2, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", - ], - }, + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 3, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 2, ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >)) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , (_prev_checkpoint , log)) | (p1a . ballot . id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), - input: CrossSingleton( - CrossSingleton( - Tee { - inner: : Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 2, - ), - from_key: None, - to_location: Cluster( - 3, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1a) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& data) . unwrap () . into ()) }", - ], - }, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >)) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , (_prev_checkpoint , log)) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + input: CrossSingleton( + CrossSingleton( + Tee { + inner: : Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 2, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 3, ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , id : p_id } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (tokio :: time :: Instant , core :: option :: Option < tokio :: time :: Instant >) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Tee { - inner: , - }, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1a) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , proposer_id : p_id } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), + input: Persist( + Tee { + inner: , + }, + ), + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, + }, + }, + }, + ), + }, + }, + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Source { + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, + ), + location_kind: Cluster( + 2, + ), + }, + }, + ), }, - ), + }, }, }, }, }, }, }, - }, - Tee { - inner: : Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . ballot }), - input: Tee { - inner: , + Tee { + inner: : Union( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . ballot }), + input: Tee { + inner: , + }, }, - }, - ), - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , id : ClusterId :: from_raw (0) } } ; [e] }, - ), - location_kind: Cluster( - 3, ), }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, + ), + location_kind: Cluster( + 3, + ), + }, + ), ), - ), - }, - ), - Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , (new_checkpoint , p2a) | { if new_checkpoint != - 1 { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } else { if p2a . slot > * prev_checkpoint { match log . get (& p2a . slot) { None => { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } Some (prev_p2a) => { if p2a . ballot > prev_p2a . ballot { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } ; } } } }), - input: Persist( - Union( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some ((- 1 , p2a)) } else { None } }), - input: CrossSingleton( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 2, - ), - from_key: None, - to_location: Cluster( - 3, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", - ], - }, + }, + ), + Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , (new_checkpoint , p2a) | { if new_checkpoint != - 1 { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } else { if p2a . slot > * prev_checkpoint { match log . get (& p2a . slot) { None => { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } Some (prev_p2a) => { if p2a . ballot > prev_p2a . ballot { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } ; } } } }), + input: Persist( + Union( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some ((- 1 , p2a)) } else { None } }), + input: CrossSingleton( + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 2, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 3, ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , id : p_id , } , slot , value : entry . value , }) } else { None } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , id : p_id } , slot , value : Default :: default () } }), - input: CrossSingleton( - Difference( - FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), - input: Tee { - inner: , - }, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Union( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), + input: CrossSingleton( + Tee { + inner: , }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), - input: Tee { - inner: , + Tee { + inner: , + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), + input: CrossSingleton( + Difference( + FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), + input: Tee { + inner: , + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: , + }, }, + ), + Tee { + inner: , }, ), - Tee { - inner: , + }, + ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: stream :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, }, - ), + }, }, ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: stream :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : payload } }), + input: CrossSingleton( + CrossSingleton( + Enumerate( + Tee { inner: , }, - }, - }, - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , id : p_id } , slot : next_slot + index as i32 , value : payload } }), - input: CrossSingleton( - CrossSingleton( - Enumerate( + ), Tee { - inner: , + inner: , }, ), Tee { - inner: , + inner: , }, ), - Tee { - inner: , - }, - ), + }, + ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Tee { + inner: , + }, }, ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Tee { - inner: , - }, - }, - ), + }, }, }, }, }, - }, - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | (min_seq , P2a { ballot : Ballot { num : 0 , id : ClusterId :: from_raw (0) } , slot : - 1 , value : Default :: default () }) }), - input: Delta( - Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), + Tee { + inner: , + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | (min_seq , P2a { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , slot : - 1 , value : Default :: default () }) }), + input: Delta( + Union( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: : ReduceKeyed { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), - input: Persist( - Network { - from_location: Cluster( - 1, - ), - from_key: None, - to_location: Cluster( - 3, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , i32) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < i32 > (& data) . unwrap () . into ()) }", - ], - }, + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : ReduceKeyed { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), + input: Persist( + Network { + from_location: Cluster( + 1, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 3, ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 1, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , i32) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < i32 > (& data) . unwrap () . into ()) }", + ], + }, ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 1, + ), + }, }, }, - }, - ), + ), + }, }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, }, }, }, - }, - ), + ), + }, }, }, - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, - ), - location_kind: Cluster( - 3, - ), - }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, + ), + location_kind: Cluster( + 3, + ), + }, + ), ), ), - ), - }, + }, + ), ), - ), - }, - ), + }, + ), + }, }, }, }, @@ -948,46 +948,49 @@ expression: built.ir() location_kind: Cluster( 2, ), - input: Network { - from_location: Cluster( - 3, - ), - from_key: None, - to_location: Cluster( - 2, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", - ], - }, + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 3, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 2, ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + ], + }, + ), ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { victory : p2a . ballot == max_ballot , ballot : p2a . ballot , slot : p2a . slot , value : p2a . value }) }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, }, }, }, @@ -1005,10 +1008,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -1045,10 +1048,10 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (slot , data) | ReplicaPayload { seq : slot , key : data . key , value : data . value } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , (_count , p2b)) | (p2b . slot , p2b . value) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , value) | (slot , value) }), input: AntiJoin( Tee { - inner: , + inner: , }, CycleSource { ident: Ident { @@ -1077,12 +1080,12 @@ expression: built.ir() ), }, Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | - 1 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Union( CycleSource { @@ -1119,23 +1122,23 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . key , payload . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1156,7 +1159,7 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( @@ -1185,7 +1188,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1200,7 +1203,7 @@ expression: built.ir() 1, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1213,7 +1216,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1249,7 +1252,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . value . parse :: < u32 > () . unwrap ()) , payload) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1265,13 +1268,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1292,7 +1295,7 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_2, }, @@ -1306,9 +1309,9 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: : Delta( + inner: : Delta( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { @@ -1348,13 +1351,13 @@ expression: built.ir() input: FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , u32) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (_is_leader , ballot_num) | Ballot { num : ballot_num , id : p_id } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , u32) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (_is_leader , ballot_num) | Ballot { num : ballot_num , proposer_id : p_id } }), input: CrossSingleton( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -1364,7 +1367,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1389,10 +1392,10 @@ expression: built.ir() }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1405,7 +1408,7 @@ expression: built.ir() input: CrossSingleton( CrossSingleton( Tee { - inner: : Source { + inner: : Source { source: Interval( { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, ), @@ -1423,10 +1426,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1434,7 +1437,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: , + inner: , }, }, ), @@ -1456,7 +1459,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1467,7 +1470,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1479,7 +1482,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1499,17 +1502,17 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot . leader_id () , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot . leader_id () , ClientPayload { key , value : c_id . raw_id . to_string () }) }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, From 48c7fb730ace4cf45b4b1ab6120d69e6c777e805 Mon Sep 17 00:00:00 2001 From: Jameson Crate <86806051+Jameson-Crate@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:12:19 -0700 Subject: [PATCH 07/74] docs: update setup.md `--server-addr` -> `--address` (#1457) changed --server-addr flag to --address Co-authored-by: Mingwei Samuel --- docs/docs/hydroflow/quickstart/setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/hydroflow/quickstart/setup.md b/docs/docs/hydroflow/quickstart/setup.md index 027665899ba9..0c363510806a 100644 --- a/docs/docs/hydroflow/quickstart/setup.md +++ b/docs/docs/hydroflow/quickstart/setup.md @@ -99,7 +99,7 @@ Take note of the server's port number, and in a separate terminal, start a clien #shell-command-next-line cd #shell-command-next-line -cargo run -- --role client --server-addr 127.0.0.1: +cargo run -- --role client --address 127.0.0.1: Listening on 127.0.0.1: Connecting to server at 127.0.0.1: Client live! From 32e297094fed9908ca7bf77e7068bc0a6ea52eae Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Wed, 30 Oct 2024 14:12:30 -0700 Subject: [PATCH 08/74] refactor(hydroflow_lang): update topo-sort to detect cycles (#1512) --- .../src/graph/flat_to_partitioned.rs | 3 +- hydroflow_lang/src/graph/graph_algorithms.rs | 120 +++++++++++++++--- 2 files changed, 105 insertions(+), 18 deletions(-) diff --git a/hydroflow_lang/src/graph/flat_to_partitioned.rs b/hydroflow_lang/src/graph/flat_to_partitioned.rs index 4c732fe5b0aa..da192516c2d9 100644 --- a/hydroflow_lang/src/graph/flat_to_partitioned.rs +++ b/hydroflow_lang/src/graph/flat_to_partitioned.rs @@ -165,7 +165,8 @@ fn make_subgraph_collect( !matches!(pred, GraphNode::Handoff { .. }) }) }, - ); + ) + .expect("Subgraphs are in-out trees."); let mut grouped_nodes: SecondaryMap> = Default::default(); for node_id in topo_sort { diff --git a/hydroflow_lang/src/graph/graph_algorithms.rs b/hydroflow_lang/src/graph/graph_algorithms.rs index 96ec1e357df2..dfbaed243f39 100644 --- a/hydroflow_lang/src/graph/graph_algorithms.rs +++ b/hydroflow_lang/src/graph/graph_algorithms.rs @@ -23,16 +23,21 @@ where let topo_sort_order = { // Condensed each SCC into a single node for toposort. let mut condensed_preds: BTreeMap> = Default::default(); - for u in (nodes_fn)() { - condensed_preds - .entry(scc[&u]) - .or_default() - .extend((preds_fn)(u).into_iter().map(|v| scc[&v])); + for v in (nodes_fn)() { + let v = scc[&v]; + condensed_preds.entry(v).or_default().extend( + (preds_fn)(v) + .into_iter() + .map(|u| scc[&u]) + .filter(|&u| v != u), + ); } topo_sort((nodes_fn)(), |v| { condensed_preds.get(&v).into_iter().flatten().cloned() }) + .map_err(drop) + .expect("No cycles after SCC condensing.") }; topo_sort_order } @@ -40,13 +45,16 @@ where /// Topologically sorts a set of nodes. Returns a list where the order of `Id`s will agree with /// the order of any path through the graph. /// -/// This naturally requires a directed acyclic graph (DAG). +/// This succeeds if the input is a directed acyclic graph (DAG). +/// +/// If the input has a cycle, an `Err` will be returned containing the cycle. Each node in the +/// cycle will be listed exactly once. /// /// pub fn topo_sort( node_ids: NodeIds, mut preds_fn: PredsFn, -) -> Vec +) -> Result, Vec> where Id: Copy + Eq + Ord, NodeIds: IntoIterator, @@ -58,28 +66,49 @@ where fn pred_dfs_postorder( node_id: Id, preds_fn: &mut PredsFn, - marked: &mut BTreeSet, + marked: &mut BTreeMap, // `false` => temporary, `true` => permanent. order: &mut Vec, - ) where + ) -> Result<(), ()> + where Id: Copy + Eq + Ord, PredsFn: FnMut(Id) -> PredsIter, PredsIter: IntoIterator, { - if marked.insert(node_id) { - for next_pred in (preds_fn)(node_id) { - pred_dfs_postorder(next_pred, preds_fn, marked, order); + match marked.get(&node_id) { + Some(_permanent @ true) => Ok(()), + Some(_temporary @ false) => { + // Cycle found! + order.clear(); + order.push(node_id); + Err(()) + } + None => { + marked.insert(node_id, false); + for next_pred in (preds_fn)(node_id) { + pred_dfs_postorder(next_pred, preds_fn, marked, order).map_err(|()| { + if order.len() == 1 || order.first().unwrap() != order.last().unwrap() { + order.push(node_id); + } + })?; + } + order.push(node_id); + marked.insert(node_id, true); + Ok(()) } - order.push(node_id); - } else { - // TODO(mingwei): cycle found! } } for node_id in node_ids { - pred_dfs_postorder(node_id, &mut preds_fn, &mut marked, &mut order); + if pred_dfs_postorder(node_id, &mut preds_fn, &mut marked, &mut order).is_err() { + // Cycle found. + let end = order.last().unwrap(); + let beg = order.iter().position(|n| n == end).unwrap(); + order.drain(0..=beg); + return Err(order); + } } - order + Ok(order) } /// Finds the strongly connected components in the graph. A strongly connected component is a @@ -156,6 +185,8 @@ where #[cfg(test)] mod test { + use itertools::Itertools; + use super::*; #[test] @@ -179,6 +210,13 @@ mod test { .filter(move |&&(_, dst)| v == dst) .map(|&(src, _)| src) }); + assert!( + sort.is_ok(), + "Did not expect cycle: {:?}", + sort.unwrap_err() + ); + + let sort = sort.unwrap(); println!("{:?}", sort); let position: BTreeMap<_, _> = sort.iter().enumerate().map(|(i, &x)| (x, i)).collect(); @@ -187,6 +225,54 @@ mod test { } } + #[test] + pub fn test_toposort_cycle() { + // https://commons.wikimedia.org/wiki/File:Directed_graph,_cyclic.svg + // ┌────►C──────┐ + // │ │ + // │ ▼ + // A───────►B E ─────►F + // ▲ │ + // │ │ + // └─────D◄─────┘ + let edges = [ + ('A', 'B'), + ('B', 'C'), + ('C', 'E'), + ('D', 'B'), + ('E', 'F'), + ('E', 'D'), + ]; + let ids = edges + .iter() + .flat_map(|&(a, b)| [a, b]) + .collect::>(); + let cycle_rotations = BTreeSet::from_iter([ + ['B', 'C', 'E', 'D'], + ['C', 'E', 'D', 'B'], + ['E', 'D', 'B', 'C'], + ['D', 'B', 'C', 'E'], + ]); + + let permutations = ids.iter().copied().permutations(ids.len()); + for permutation in permutations { + let result = topo_sort(permutation.iter().copied(), |v| { + edges + .iter() + .filter(move |&&(_, dst)| v == dst) + .map(|&(src, _)| src) + }); + assert!(result.is_err()); + let cycle = result.unwrap_err(); + assert!( + cycle_rotations.contains(&*cycle), + "cycle: {:?}, vertex order: {:?}", + cycle, + permutation + ); + } + } + #[test] pub fn test_scc_kosaraju() { // https://commons.wikimedia.org/wiki/File:Scc-1.svg From 8b7b1c60fd33b78f9a4b0873bbbd150260ae2ad5 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 30 Oct 2024 15:18:33 -0700 Subject: [PATCH 09/74] refactor(paxos): complete split into leader election and sequencing phases (#1486) --- hydroflow_plus/src/cycle.rs | 21 +- hydroflow_plus/src/lib.rs | 2 +- hydroflow_plus/src/location.rs | 49 +- hydroflow_plus/src/singleton.rs | 36 +- hydroflow_plus/src/stream.rs | 15 +- hydroflow_plus_test/src/cluster/paxos.rs | 349 +++-- .../src/cluster/paxos_bench.rs | 4 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 1170 +++++++++-------- .../src/local/graph_reachability.rs | 2 +- stageleft_tool/src/lib.rs | 4 + 10 files changed, 930 insertions(+), 722 deletions(-) diff --git a/hydroflow_plus/src/cycle.rs b/hydroflow_plus/src/cycle.rs index d8a240e56548..4f2f010ecb2f 100644 --- a/hydroflow_plus/src/cycle.rs +++ b/hydroflow_plus/src/cycle.rs @@ -2,7 +2,11 @@ use std::marker::PhantomData; use crate::builder::FlowState; use crate::location::{Location, LocationId}; -use crate::{NoTick, Tick}; +use crate::Tick; + +pub trait DeferTick { + fn defer_tick(self) -> Self; +} pub trait CycleComplete<'a, T> { fn complete(self, ident: syn::Ident); @@ -25,25 +29,30 @@ pub trait CycleCollectionWithInitial<'a, T>: CycleComplete<'a, T> { ) -> Self; } -/// Represents a fixpoint cycle in the graph that will be fulfilled +/// Represents a forward reference in the graph that will be fulfilled /// by a stream that is not yet known. /// /// See [`crate::FlowBuilder`] for an explainer on the type parameters. -pub struct HfCycle<'a, T, S: CycleComplete<'a, T>> { +pub struct HfForwardRef<'a, T, S: CycleComplete<'a, T>> { pub(crate) ident: syn::Ident, pub(crate) _phantom: PhantomData<(&'a mut &'a (), T, S)>, } -impl<'a, S: CycleComplete<'a, NoTick>> HfCycle<'a, NoTick, S> { +impl<'a, T, S: CycleComplete<'a, T>> HfForwardRef<'a, T, S> { pub fn complete(self, stream: S) { let ident = self.ident; S::complete(stream, ident) } } -impl<'a, S: CycleComplete<'a, Tick>> HfCycle<'a, Tick, S> { +pub struct HfCycle<'a, S: CycleComplete<'a, Tick> + DeferTick> { + pub(crate) ident: syn::Ident, + pub(crate) _phantom: PhantomData<(&'a mut &'a (), S)>, +} + +impl<'a, S: CycleComplete<'a, Tick> + DeferTick> HfCycle<'a, S> { pub fn complete_next_tick(self, stream: S) { let ident = self.ident; - S::complete(stream, ident) + S::complete(stream.defer_tick(), ident) } } diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 5acf5ac634bc..8846d15223e6 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -31,7 +31,7 @@ pub mod deploy; pub use deploy::{ClusterSpec, Deploy, ProcessSpec}; pub mod cycle; -pub use cycle::HfCycle; +pub use cycle::HfForwardRef; pub mod builder; pub use builder::FlowBuilder; diff --git a/hydroflow_plus/src/location.rs b/hydroflow_plus/src/location.rs index 7e4a5e428bb5..9f5d9cb50aa6 100644 --- a/hydroflow_plus/src/location.rs +++ b/hydroflow_plus/src/location.rs @@ -12,9 +12,9 @@ use serde::{Deserialize, Serialize}; use stageleft::{q, quote_type, Quoted}; use super::builder::{ClusterIds, ClusterSelfId, FlowState}; -use crate::cycle::{CycleCollection, CycleCollectionWithInitial}; +use crate::cycle::{CycleCollection, CycleCollectionWithInitial, DeferTick, HfCycle}; use crate::ir::{HfPlusNode, HfPlusSource}; -use crate::{Bounded, HfCycle, NoTick, Optional, Singleton, Stream, Tick, Unbounded}; +use crate::{Bounded, HfForwardRef, NoTick, Optional, Singleton, Stream, Tick, Unbounded}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum LocationId { @@ -170,9 +170,9 @@ pub trait Location<'a> { .latest() } - fn tick_cycle>( + fn forward_ref>( &self, - ) -> (HfCycle<'a, Tick, S>, S) { + ) -> (HfForwardRef<'a, NoTick, S>, S) { let next_id = { let on_id = match self.id() { LocationId::Process(id) => id, @@ -191,7 +191,36 @@ pub trait Location<'a> { let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); ( - HfCycle { + HfForwardRef { + ident: ident.clone(), + _phantom: PhantomData, + }, + S::create_source(ident, self.flow_state().clone(), self.id()), + ) + } + + fn tick_forward_ref>( + &self, + ) -> (HfForwardRef<'a, Tick, S>, S) { + let next_id = { + let on_id = match self.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + HfForwardRef { ident: ident.clone(), _phantom: PhantomData, }, @@ -199,9 +228,9 @@ pub trait Location<'a> { ) } - fn cycle>( + fn tick_cycle + DeferTick>( &self, - ) -> (HfCycle<'a, NoTick, S>, S) { + ) -> (HfCycle<'a, S>, S) { let next_id = { let on_id = match self.id() { LocationId::Process(id) => id, @@ -228,10 +257,12 @@ pub trait Location<'a> { ) } - fn tick_cycle_with_initial>( + fn tick_cycle_with_initial< + S: CycleCollectionWithInitial<'a, Tick, Location = Self> + DeferTick, + >( &self, initial: S, - ) -> (HfCycle<'a, Tick, S>, S) { + ) -> (HfCycle<'a, S>, S) { let next_id = { let on_id = match self.id() { LocationId::Process(id) => id, diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 54374e6c3f14..9a3beaa036e9 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use stageleft::{q, IntoQuotedMut, Quoted}; use crate::builder::FlowState; -use crate::cycle::{CycleCollection, CycleCollectionWithInitial, CycleComplete}; +use crate::cycle::{CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick}; use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; use crate::location::{Location, LocationId}; use crate::stream::{Bounded, NoTick, Tick, Unbounded}; @@ -128,6 +128,12 @@ impl<'a, T, C, N: Location<'a>> From> } } +impl<'a, T, N: Location<'a>> DeferTick for Singleton { + fn defer_tick(self) -> Self { + Singleton::defer_tick(self) + } +} + impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Singleton { fn complete(self, ident: syn::Ident) { self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { @@ -138,6 +144,21 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Singleton> CycleCollection<'a, Tick> for Singleton { + type Location = N; + + fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { + Singleton::new( + l, + flow_state, + HfPlusNode::CycleSource { + ident, + location_kind: l, + }, + ) + } +} + impl<'a, T, N: Location<'a>> CycleCollectionWithInitial<'a, Tick> for Singleton { @@ -403,13 +424,18 @@ impl<'a, T, W, C, N: Location<'a>> Optional { } } +impl<'a, T, N: Location<'a>> DeferTick for Optional { + fn defer_tick(self) -> Self { + Optional::defer_tick(self) + } +} + impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Optional { fn complete(self, ident: syn::Ident) { - let me = self.defer_tick(); - me.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, - location_kind: me.location_kind, - input: Box::new(me.ir_node.into_inner()), + location_kind: self.location_kind, + input: Box::new(self.ir_node.into_inner()), }); } } diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 89b1d8476b2b..f374d5761bb9 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -14,7 +14,7 @@ use syn::parse_quote; use super::staging_util::get_this_crate; use crate::builder::{self, FlowState}; -use crate::cycle::{CycleCollection, CycleComplete}; +use crate::cycle::{CycleCollection, CycleComplete, DeferTick}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; use crate::location::{ CanSend, ExternalBincodeStream, ExternalBytesPort, ExternalProcess, Location, LocationId, @@ -54,13 +54,18 @@ pub struct Stream { _phantom: PhantomData<(T, N, W, C)>, } +impl<'a, T, N: Location<'a>> DeferTick for Stream { + fn defer_tick(self) -> Self { + Stream::defer_tick(self) + } +} + impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Stream { fn complete(self, ident: syn::Ident) { - let me = self.defer_tick(); - me.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, - location_kind: me.location_kind, - input: Box::new(me.ir_node.into_inner()), + location_kind: self.location_kind, + input: Box::new(self.ir_node.into_inner()), }); } } diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 0667dae4e9d4..7ebdc8160afb 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -96,56 +96,93 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( proposers .source_iter(q!(["Proposers say hello"])) .for_each(q!(|s| println!("{}", s))); - let p_id = proposers.self_id(); - let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader) = - proposers.cycle::>(); - let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b) = - proposers.cycle::>(); let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b) = - proposers.cycle::>(); - // a_to_proposers_p2b.clone().for_each(q!(|(_, p2b): (u32, P2b)| println!("Proposer received P2b: {:?}", p2b))); - // p_to_proposers_i_am_leader.clone().for_each(q!(|ballot: Ballot| println!("Proposer received I am leader: {:?}", ballot))); - // c_to_proposers.clone().for_each(q!(|payload: ClientPayload| println!("Client sent proposer payload: {:?}", payload))); + proposers.forward_ref::>(); + let (a_log_complete_cycle, a_log_forward_reference) = + acceptors.tick_forward_ref::>(); + + let (p_ballot_num, p_is_leader, p_max_slot, p_log_to_try_commit, p_log_holes, a_max_ballot) = + leader_election( + &proposers, + &acceptors, + f, + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + a_to_proposers_p2b, + a_log_forward_reference, + ); - let p_received_max_ballot = p_max_ballot( - &proposers, - a_to_proposers_p1b.clone(), - a_to_proposers_p2b.clone(), - p_to_proposers_i_am_leader.clone(), - ); - let (p_ballot_num, p_has_largest_ballot) = - p_ballot_calc(&proposers, p_received_max_ballot.latest_tick()); + let (p_to_clients_new_leader_elected, p_to_replicas, a_log, a_to_proposers_p2b_new) = + sequence_payload( + &proposers, + &acceptors, + c_to_proposers, + r_to_acceptors_checkpoint, + p_ballot_num, + p_is_leader, + p_max_slot, + p_log_to_try_commit, + p_log_holes, + f, + a_max_ballot, + ); - let (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) = p_p1b( - &proposers, - a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), - p_ballot_num.clone(), - p_has_largest_ballot, - f, - ); + a_log_complete_cycle.complete(a_log.clone()); + a_to_proposers_p2b_complete_cycle.complete(a_to_proposers_p2b_new); - let (p_to_proposers_i_am_leader_from_others, p_to_acceptors_p1a) = p_p1a( - p_ballot_num.clone(), - p_is_leader.clone(), - &proposers, - p_to_proposers_i_am_leader, - &acceptors, - i_am_leader_send_timeout, - i_am_leader_check_timeout, - i_am_leader_check_timeout_delay_multiplier, - ); - p_to_proposers_i_am_leader_complete_cycle.complete(p_to_proposers_i_am_leader_from_others); + ( + proposers, + acceptors, + p_to_clients_new_leader_elected, + p_to_replicas, + ) +} + +#[expect( + clippy::type_complexity, + clippy::too_many_arguments, + reason = "internal paxos code // TODO" +)] +fn sequence_payload<'a, P: PaxosPayload, R>( + proposers: &Cluster<'a, Proposer>, + acceptors: &Cluster<'a, Acceptor>, + c_to_proposers: Stream>, + r_to_acceptors_checkpoint: impl FnOnce( + &Cluster<'a, Acceptor>, + ) -> Stream< + (ClusterId, i32), + Unbounded, + NoTick, + Cluster<'a, Acceptor>, + >, + + p_ballot_num: Singleton>, + p_is_leader: Optional>, + p_max_slot: Optional>, + + p_log_to_try_commit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, + p_log_holes: Stream, Bounded, Tick, Cluster<'a, Proposer>>, + f: usize, + a_max_ballot: Singleton>, +) -> ( + Stream>, + Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, + Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, + Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, +) { + let p_id = proposers.self_id(); let (p_next_slot, p_to_acceptors_p2a) = p_p2a( - &proposers, + proposers, p_max_slot, c_to_proposers, p_ballot_num.clone(), p_log_to_try_commit, p_log_holes, p_is_leader.clone(), - &acceptors, + acceptors, ); // Tell clients that leader election has completed and they can begin sending messages @@ -154,38 +191,129 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( .cross_singleton(p_ballot_num) .map(q!(move |(_is_leader, ballot_num)| Ballot { num: ballot_num, proposer_id: p_id})) // Only tell the clients once when leader election concludes .all_ticks(); - // End tell clients that leader election has completed - let p_to_replicas = p_p2b(&proposers, a_to_proposers_p2b, f); // Acceptors. acceptors .source_iter(q!(["Acceptors say hello"])) .for_each(q!(|s| println!("{}", s))); - let r_to_acceptors_checkpoint = r_to_acceptors_checkpoint(&acceptors); + let r_to_acceptors_checkpoint = r_to_acceptors_checkpoint(acceptors); // p_to_acceptors_p2a.clone().for_each(q!(|p2a: P2a| println!("Acceptor received P2a: {:?}", p2a))); - let (a_to_proposers_p1b_new, a_to_proposers_p2b_new) = acceptor( - p_to_acceptors_p1a.inspect(q!(|p1a| println!("Acceptor received P1a: {:?}", p1a))), + let (a_log, a_to_proposers_p2b) = acceptor_p2( + a_max_ballot.clone(), p_to_acceptors_p2a, r_to_acceptors_checkpoint, - &proposers, - &acceptors, + proposers, + acceptors, f, ); - a_to_proposers_p1b_complete_cycle.complete(a_to_proposers_p1b_new); - a_to_proposers_p2b_complete_cycle.complete(a_to_proposers_p2b_new); + + // End tell clients that leader election has completed + let p_to_replicas = p_p2b(proposers, a_to_proposers_p2b.clone(), f); ( - proposers, - acceptors, p_to_clients_new_leader_elected, p_to_replicas, + a_log, + a_to_proposers_p2b, + ) +} + +#[expect( + clippy::type_complexity, + clippy::too_many_arguments, + reason = "internal paxos code // TODO" +)] +fn leader_election<'a, P: PaxosPayload>( + proposers: &Cluster<'a, Proposer>, + acceptors: &Cluster<'a, Acceptor>, + f: usize, + i_am_leader_send_timeout: u64, + i_am_leader_check_timeout: u64, + i_am_leader_check_timeout_delay_multiplier: usize, + a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + a_log: Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, +) -> ( + Singleton>, + Optional>, + Optional>, + Stream, Bounded, Tick, Cluster<'a, Proposer>>, + Stream, Bounded, Tick, Cluster<'a, Proposer>>, + Singleton>, +) { + let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b) = + proposers.forward_ref::>(); + let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader) = + proposers.forward_ref::>(); + // a_to_proposers_p2b.clone().for_each(q!(|(_, p2b): (u32, P2b)| println!("Proposer received P2b: {:?}", p2b))); + // p_to_proposers_i_am_leader.clone().for_each(q!(|ballot: Ballot| println!("Proposer received I am leader: {:?}", ballot))); + // c_to_proposers.clone().for_each(q!(|payload: ClientPayload| println!("Client sent proposer payload: {:?}", payload))); + + let p_received_max_ballot = p_max_ballot( + proposers, + a_to_proposers_p1b.clone(), + a_to_proposers_p2b, + p_to_proposers_i_am_leader.clone(), + ); + let (p_ballot_num, p_has_largest_ballot) = + p_ballot_calc(proposers, p_received_max_ballot.latest_tick()); + + let (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) = p_p1b( + proposers, + a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), + p_ballot_num.clone(), + p_has_largest_ballot, + f, + ); + + let (p_to_proposers_i_am_leader_from_others, p_to_acceptors_p1a) = p_p1a( + p_ballot_num.clone(), + p_is_leader.clone(), + proposers, + p_to_proposers_i_am_leader, + acceptors, + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + ); + p_to_proposers_i_am_leader_complete_cycle.complete(p_to_proposers_i_am_leader_from_others); + + let a_max_ballot = p_to_acceptors_p1a + .clone() + .inspect(q!(|p1a| println!("Acceptor received P1a: {:?}", p1a))) + .map(q!(|p1a| p1a.ballot)) + .max() + .unwrap_or(acceptors.singleton(q!(Ballot { + num: 0, + proposer_id: ClusterId::from_raw(0) + }))); + + a_to_proposers_p1b_complete_cycle.complete(acceptor_p1( + p_to_acceptors_p1a, + &a_max_ballot, + a_log, + proposers, + )); + + ( + p_ballot_num, + p_is_leader, + p_max_slot, + p_log_to_try_commit, + p_log_holes, + a_max_ballot, ) } +#[derive(Clone)] +enum CheckpointOrP2a

{ + Checkpoint(i32), + P2a(P2a

), +} + #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -fn acceptor<'a, P: PaxosPayload, R>( - p_to_acceptors_p1a: Stream>, +fn acceptor_p2<'a, P: PaxosPayload, R>( + a_max_ballot: Singleton>, p_to_acceptors_p2a: Stream, Unbounded, NoTick, Cluster<'a, Acceptor>>, r_to_acceptors_checkpoint: Stream< (ClusterId, i32), @@ -197,7 +325,7 @@ fn acceptor<'a, P: PaxosPayload, R>( acceptors: &Cluster<'a, Acceptor>, f: usize, ) -> ( - Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, ) { // Get the latest checkpoint sequence per replica @@ -223,35 +351,16 @@ fn acceptor<'a, P: PaxosPayload, R>( .min() .unwrap_or(acceptors.singleton(q!(-1)).latest_tick()) .delta() - .map(q!(|min_seq| ( - min_seq, - P2a { - // Create tuple with checkpoint number and dummy p2a - ballot: Ballot { - num: 0, - proposer_id: ClusterId::from_raw(0) - }, - slot: -1, - value: Default::default() - } - ))); + .map(q!(|min_seq| CheckpointOrP2a::Checkpoint(min_seq))); // .inspect(q!(|(min_seq, p2a): &(i32, P2a)| println!("Acceptor new checkpoint: {:?}", min_seq))); - let a_max_ballot = p_to_acceptors_p1a - .clone() - .map(q!(|p1a| p1a.ballot)) - .max() - .unwrap_or(acceptors.singleton(q!(Ballot { - num: 0, - proposer_id: ClusterId::from_raw(0) - }))); let a_p2as_to_place_in_log = p_to_acceptors_p2a .clone() .tick_batch() .cross_singleton(a_max_ballot.clone().latest_tick()) // Don't consider p2as if the current ballot is higher .filter_map(q!(|(p2a, max_ballot)| if p2a.ballot >= max_ballot { - Some((-1, p2a)) // Signal that this isn't a checkpoint with -1 + Some(CheckpointOrP2a::P2a(p2a)) } else { None } @@ -261,60 +370,39 @@ fn acceptor<'a, P: PaxosPayload, R>( .persist() .fold( q!(|| (-1, HashMap::new())), - q!(|(prev_checkpoint, log), (new_checkpoint, p2a)| { - if new_checkpoint != -1 { - // This is a checkpoint message. Delete all entries up to the checkpoint - for slot in *prev_checkpoint..new_checkpoint { - log.remove(&slot); + q!(|(prev_checkpoint, log), checkpoint_or_p2a| { + match checkpoint_or_p2a { + CheckpointOrP2a::Checkpoint(new_checkpoint) => { + // This is a checkpoint message. Delete all entries up to the checkpoint + for slot in *prev_checkpoint..new_checkpoint { + log.remove(&slot); + } + *prev_checkpoint = new_checkpoint; } - *prev_checkpoint = new_checkpoint; - } else { - // This is a regular p2a message. Insert it into the log if it is not checkpointed and has a higher ballot than what was there before - if p2a.slot > *prev_checkpoint { - match log.get(&p2a.slot) { - None => { - log.insert( - p2a.slot, - LogValue { - ballot: p2a.ballot, - value: p2a.value, - }, - ); - } - Some(prev_p2a) => { - if p2a.ballot > prev_p2a.ballot { - log.insert( - p2a.slot, - LogValue { - ballot: p2a.ballot, - value: p2a.value, - }, - ); - } - } - }; + CheckpointOrP2a::P2a(p2a) => { + // This is a regular p2a message. Insert it into the log if it is not checkpointed and has a higher ballot than what was there before + if p2a.slot > *prev_checkpoint + && log + .get(&p2a.slot) + .map(|prev_p2a: &LogValue<_>| p2a.ballot > prev_p2a.ballot) + .unwrap_or(true) + { + log.insert( + p2a.slot, + LogValue { + ballot: p2a.ballot, + value: p2a.value, + }, + ); + } } } }), ); - let a_to_proposers_p1b_new = p_to_acceptors_p1a - .tick_batch() - .cross_singleton(a_max_ballot.clone().latest_tick()) - .cross_singleton(a_log) - .map(q!(|((p1a, max_ballot), (_prev_checkpoint, log))| ( - p1a.ballot.proposer_id, - P1b { - ballot: p1a.ballot, - max_ballot, - accepted: log - } - ))) - .all_ticks() - .send_bincode_interleaved(proposers); let a_to_proposers_p2b_new = p_to_acceptors_p2a .tick_batch() - .cross_singleton(a_max_ballot.latest_tick()) + .cross_singleton(a_max_ballot.clone().latest_tick()) .map(q!(|(p2a, max_ballot)| ( p2a.ballot.proposer_id, P2b { @@ -326,7 +414,30 @@ fn acceptor<'a, P: PaxosPayload, R>( ))) .all_ticks() .send_bincode_interleaved(proposers); - (a_to_proposers_p1b_new, a_to_proposers_p2b_new) + (a_log, a_to_proposers_p2b_new) +} + +#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] +fn acceptor_p1<'a, P: PaxosPayload>( + p_to_acceptors_p1a: Stream>, + a_max_ballot: &Singleton>, + a_log: Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, + proposers: &Cluster<'a, Proposer>, +) -> Stream, Unbounded, NoTick, Cluster<'a, Proposer>> { + p_to_acceptors_p1a + .tick_batch() + .cross_singleton(a_max_ballot.clone().latest_tick()) + .cross_singleton(a_log) + .map(q!(|((p1a, max_ballot), (_prev_checkpoint, log))| ( + p1a.ballot.proposer_id, + P1b { + ballot: p1a.ballot, + max_ballot, + accepted: log + } + ))) + .all_ticks() + .send_bincode_interleaved(proposers) } fn p_p2b<'a, P: PaxosPayload>( diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 5b72192b093c..37cfc078305d 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -63,7 +63,7 @@ pub fn paxos_bench<'a>( let clients = flow.cluster::(); let replicas = flow.cluster::(); - let (c_to_proposers_complete_cycle, c_to_proposers) = clients.cycle(); + let (c_to_proposers_complete_cycle, c_to_proposers) = clients.forward_ref(); let (proposers, acceptors, p_to_clients_new_leader_elected, r_new_processed_payloads) = paxos_with_replica( @@ -115,7 +115,7 @@ fn paxos_with_replica<'a>( Stream<(ClusterId, ReplicaPayload), Unbounded, NoTick, Cluster<'a, Replica>>, ) { let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = - replicas.cycle::>(); + replicas.forward_ref::>(); let (proposers, acceptors, p_to_clients_new_leader_elected, p_to_replicas) = paxos_core( flow, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index af57d72b873f..adbac8d56634 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -21,96 +21,98 @@ expression: built.ir() location_kind: Cluster( 2, ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , proposer_id : p_id , }) { received_max_ballot . num + 1 } else { ballot_num } } }), - input: CrossSingleton( - Tee { - inner: : Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - Union( + input: DeferTick( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , proposer_id : p_id , }) { received_max_ballot . num + 1 } else { ballot_num } } }), + input: CrossSingleton( + Tee { + inner: : Union( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( Union( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . max_ballot }), - input: Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_1, + Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . max_ballot }), + input: Tee { + inner: : CycleSource { + ident: Ident { + sym: cycle_1, + }, + location_kind: Cluster( + 2, + ), }, - location_kind: Cluster( - 2, - ), }, }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . ballot }), - input: Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_2, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . ballot }), + input: Tee { + inner: : CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 2, + ), }, - location_kind: Cluster( - 2, - ), }, }, - }, - ), - Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_0, + ), + Tee { + inner: : CycleSource { + ident: Ident { + sym: cycle_2, + }, + location_kind: Cluster( + 2, + ), }, - location_kind: Cluster( - 2, - ), }, - }, - ), - ), - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, - ), - location_kind: Cluster( - 2, + ), ), }, - ), - ), - }, - Tee { - inner: : Union( - CycleSource { - ident: Ident { - sym: cycle_3, - }, - location_kind: Cluster( - 2, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, + ), + location_kind: Cluster( + 2, + ), + }, ), - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e] }, - ), + ), + }, + Tee { + inner: : Union( + CycleSource { + ident: Ident { + sym: cycle_3, + }, location_kind: Cluster( 2, ), }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e] }, + ), + location_kind: Cluster( + 2, + ), + }, + ), ), - ), - }, - ), - }, + }, + ), + }, + ), }, CycleSink { ident: Ident { - sym: cycle_0, + sym: cycle_2, }, location_kind: Cluster( 2, @@ -234,6 +236,195 @@ expression: built.ir() }, }, }, + CycleSink { + ident: Ident { + sym: cycle_1, + }, + location_kind: Cluster( + 2, + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 3, + ), + from_key: None, + to_location: Cluster( + 2, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >)) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , (_prev_checkpoint , log)) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + input: CrossSingleton( + CrossSingleton( + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 2, + ), + from_key: None, + to_location: Cluster( + 3, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1a) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , proposer_id : p_id } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), + input: Persist( + Tee { + inner: , + }, + ), + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, + }, + }, + }, + ), + }, + }, + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Source { + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, + ), + location_kind: Cluster( + 2, + ), + }, + }, + ), + }, + }, + }, + }, + }, + }, + }, + Tee { + inner: : Union( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . ballot }), + input: Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), + input: Tee { + inner: , + }, + }, + }, + ), + }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, + ), + location_kind: Cluster( + 3, + ), + }, + ), + ), + }, + ), + CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 3, + ), + }, + ), + }, + }, + }, + }, CycleSink { ident: Ident { sym: cycle_4, @@ -244,7 +435,7 @@ expression: built.ir() input: DeferTick( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( @@ -256,12 +447,12 @@ expression: built.ir() input: CrossSingleton( Union( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), input: FlatMap { @@ -294,7 +485,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_4, }, @@ -313,15 +504,15 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads as i32 }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -369,10 +560,10 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: : Filter { + inner: : Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | num_payloads | * num_payloads > 0 }), input: Tee { - inner: , + inner: , }, }, }, @@ -381,7 +572,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -390,7 +581,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -400,7 +591,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -433,7 +624,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -444,6 +635,17 @@ expression: built.ir() ), ), }, + ForEach { + f: stageleft :: runtime_support :: fn1_type_hint :: < & str , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | s | println ! ("{}" , s) }), + input: Source { + source: Iter( + { use crate :: __staged :: cluster :: paxos :: * ; ["Acceptors say hello"] }, + ), + location_kind: Cluster( + 3, + ), + }, + }, CycleSink { ident: Ident { sym: cycle_5, @@ -456,40 +658,204 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), input: FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . victory { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { - inner: : Union( + inner: : Union( Tee { - inner: , - }, - CycleSource { - ident: Ident { - sym: cycle_6, - }, - location_kind: Cluster( - 2, - ), - }, - ), - }, - }, - }, - }, - }, - }, - }, - Tee { - inner: : FilterMap { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 3, + ), + from_key: None, + to_location: Cluster( + 2, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { victory : p2a . ballot == max_ballot , ballot : p2a . ballot , slot : p2a . slot , value : p2a . value }) }), + input: CrossSingleton( + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 2, + ), + from_key: None, + to_location: Cluster( + 3, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Union( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), + input: CrossSingleton( + Difference( + FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), + input: Tee { + inner: , + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: , + }, + }, + ), + Tee { + inner: , + }, + ), + }, + ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: stream :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, + }, + }, + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : payload } }), + input: CrossSingleton( + CrossSingleton( + Enumerate( + Tee { + inner: , + }, + ), + Tee { + inner: , + }, + ), + Tee { + inner: , + }, + ), + }, + ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Tee { + inner: , + }, + }, + ), + }, + }, + }, + }, + }, + Tee { + inner: , + }, + ), + }, + }, + }, + }, + CycleSource { + ident: Ident { + sym: cycle_6, + }, + location_kind: Cluster( + 2, + ), + }, + ), + }, + }, + }, + }, + }, + }, + }, + Tee { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -510,194 +876,121 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { - inner: , + inner: , }, }, Tee { - inner: , + inner: , }, ), }, ), }, - ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < & str , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | s | println ! ("{}" , s) }), - input: Source { - source: Iter( - { use crate :: __staged :: cluster :: paxos :: * ; ["Acceptors say hello"] }, - ), - location_kind: Cluster( - 3, - ), - }, - }, CycleSink { ident: Ident { - sym: cycle_1, + sym: cycle_0, }, location_kind: Cluster( - 2, + 3, ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 3, - ), - from_key: None, - to_location: Cluster( - 2, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", - ], + input: Tee { + inner: : Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), + input: Persist( + Union( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), }, - ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >)) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , (_prev_checkpoint , log)) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), - input: CrossSingleton( - CrossSingleton( - Tee { - inner: : Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 2, - ), - from_key: None, - to_location: Cluster( - 3, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1a) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , proposer_id : p_id } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), - input: Persist( - Tee { - inner: , - }, - ), - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, - }, - }, - }, - ), - }, - }, - }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), + input: Delta( + Union( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : ReduceKeyed { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), + input: Persist( + Network { + from_location: Cluster( + 1, ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Source { - source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, + from_key: None, + to_location: Cluster( + 3, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , i32) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < i32 > (& data) . unwrap () . into ()) }", + ], + }, ), - location_kind: Cluster( - 2, + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", + ], + }, ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 1, + ), + }, }, }, ), }, }, - }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, + }, + }, + }, + ), }, }, }, - }, - }, - Tee { - inner: : Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . ballot }), - input: Tee { - inner: , - }, - }, - ), - }, Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, ), location_kind: Cluster( 3, @@ -705,293 +998,22 @@ expression: built.ir() }, ), ), - }, - ), - Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , (new_checkpoint , p2a) | { if new_checkpoint != - 1 { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } else { if p2a . slot > * prev_checkpoint { match log . get (& p2a . slot) { None => { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } Some (prev_p2a) => { if p2a . ballot > prev_p2a . ballot { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } ; } } } }), - input: Persist( - Union( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some ((- 1 , p2a)) } else { None } }), - input: CrossSingleton( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 2, - ), - from_key: None, - to_location: Cluster( - 3, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), - input: CrossSingleton( - Difference( - FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), - input: Tee { - inner: , - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), - input: Tee { - inner: , - }, - }, - ), - Tee { - inner: , - }, - ), - }, - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: stream :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, - }, - }, - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : payload } }), - input: CrossSingleton( - CrossSingleton( - Enumerate( - Tee { - inner: , - }, - ), - Tee { - inner: , - }, - ), - Tee { - inner: , - }, - ), - }, - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Tee { - inner: , - }, - }, - ), - }, - }, - }, - }, - }, - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | (min_seq , P2a { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , slot : - 1 , value : Default :: default () }) }), - input: Delta( - Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: : ReduceKeyed { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), - input: Persist( - Network { - from_location: Cluster( - 1, - ), - from_key: None, - to_location: Cluster( - 3, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , i32) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < i32 > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 1, - ), - }, - }, - }, - ), - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, - }, - }, - }, - ), - }, - }, - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, - ), - location_kind: Cluster( - 3, - ), - }, - ), - ), - ), - }, - ), ), }, ), - }, + ), }, }, }, CycleSink { ident: Ident { - sym: cycle_2, + sym: cycle_0, }, location_kind: Cluster( 2, ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 3, - ), - from_key: None, - to_location: Cluster( - 2, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { victory : p2a . ballot == max_ballot , ballot : p2a . ballot , slot : p2a . slot , value : p2a . value }) }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - }, + input: Tee { + inner: , }, }, CycleSink { @@ -1008,10 +1030,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -1051,7 +1073,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , value) | (slot , value) }), input: AntiJoin( Tee { - inner: , + inner: , }, CycleSource { ident: Ident { @@ -1080,12 +1102,12 @@ expression: built.ir() ), }, Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | - 1 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Union( CycleSource { @@ -1122,23 +1144,23 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . key , payload . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1159,7 +1181,7 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( @@ -1188,7 +1210,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1203,7 +1225,7 @@ expression: built.ir() 1, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1216,7 +1238,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1252,7 +1274,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . value . parse :: < u32 > () . unwrap ()) , payload) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1268,13 +1290,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1295,7 +1317,7 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_2, }, @@ -1309,9 +1331,9 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: : Delta( + inner: : Delta( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { @@ -1367,7 +1389,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1392,10 +1414,10 @@ expression: built.ir() }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1408,7 +1430,7 @@ expression: built.ir() input: CrossSingleton( CrossSingleton( Tee { - inner: : Source { + inner: : Source { source: Interval( { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, ), @@ -1426,10 +1448,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1437,7 +1459,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: , + inner: , }, }, ), @@ -1459,7 +1481,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1470,7 +1492,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1482,7 +1504,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1502,17 +1524,17 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot . leader_id () , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot . leader_id () , ClientPayload { key , value : c_id . raw_id . to_string () }) }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index c3a27f6c1d3f..97f48660e852 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -16,7 +16,7 @@ pub fn graph_reachability<'a>( let roots = process.source_stream(roots); let edges = process.source_stream(edges); - let (set_reached_cycle, reached_cycle) = process.cycle(); + let (set_reached_cycle, reached_cycle) = process.forward_ref(); let reached = roots.union(reached_cycle); let reachable = reached diff --git a/stageleft_tool/src/lib.rs b/stageleft_tool/src/lib.rs index 35bbff0f0f06..22cdb6ef0f34 100644 --- a/stageleft_tool/src/lib.rs +++ b/stageleft_tool/src/lib.rs @@ -125,6 +125,10 @@ impl VisitMut for GenFinalPubVistor { syn::visit_mut::visit_item_enum_mut(self, i); } + fn visit_variant_mut(&mut self, _i: &mut syn::Variant) { + // variant fields do not have visibility modifiers + } + fn visit_item_struct_mut(&mut self, i: &mut syn::ItemStruct) { i.vis = parse_quote!(pub); syn::visit_mut::visit_item_struct_mut(self, i); From 3064693a0488f32676910036b03f22e52a09e431 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 30 Oct 2024 18:28:17 -0700 Subject: [PATCH 10/74] refactor(paxos): rearrange code to reflect order of phases (#1487) --- hydroflow_plus_test/src/cluster/paxos.rs | 925 +++++++++--------- ...cluster__paxos_bench__tests__paxos_ir.snap | 688 ++++++------- 2 files changed, 814 insertions(+), 799 deletions(-) diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 7ebdc8160afb..94a8fb5cfa74 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -97,7 +97,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( .source_iter(q!(["Proposers say hello"])) .for_each(q!(|s| println!("{}", s))); - let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b) = + let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b_forward_reference) = proposers.forward_ref::>(); let (a_log_complete_cycle, a_log_forward_reference) = acceptors.tick_forward_ref::>(); @@ -110,11 +110,11 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( i_am_leader_send_timeout, i_am_leader_check_timeout, i_am_leader_check_timeout_delay_multiplier, - a_to_proposers_p2b, + a_to_proposers_p2b_forward_reference, a_log_forward_reference, ); - let (p_to_clients_new_leader_elected, p_to_replicas, a_log, a_to_proposers_p2b_new) = + let (p_to_clients_new_leader_elected, p_to_replicas, a_log, a_to_proposers_p2b) = sequence_payload( &proposers, &acceptors, @@ -130,7 +130,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( ); a_log_complete_cycle.complete(a_log.clone()); - a_to_proposers_p2b_complete_cycle.complete(a_to_proposers_p2b_new); + a_to_proposers_p2b_complete_cycle.complete(a_to_proposers_p2b); ( proposers, @@ -140,85 +140,6 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( ) } -#[expect( - clippy::type_complexity, - clippy::too_many_arguments, - reason = "internal paxos code // TODO" -)] -fn sequence_payload<'a, P: PaxosPayload, R>( - proposers: &Cluster<'a, Proposer>, - acceptors: &Cluster<'a, Acceptor>, - c_to_proposers: Stream>, - r_to_acceptors_checkpoint: impl FnOnce( - &Cluster<'a, Acceptor>, - ) -> Stream< - (ClusterId, i32), - Unbounded, - NoTick, - Cluster<'a, Acceptor>, - >, - - p_ballot_num: Singleton>, - p_is_leader: Optional>, - p_max_slot: Optional>, - - p_log_to_try_commit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, - p_log_holes: Stream, Bounded, Tick, Cluster<'a, Proposer>>, - f: usize, - - a_max_ballot: Singleton>, -) -> ( - Stream>, - Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, - Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, - Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, -) { - let p_id = proposers.self_id(); - let (p_next_slot, p_to_acceptors_p2a) = p_p2a( - proposers, - p_max_slot, - c_to_proposers, - p_ballot_num.clone(), - p_log_to_try_commit, - p_log_holes, - p_is_leader.clone(), - acceptors, - ); - - // Tell clients that leader election has completed and they can begin sending messages - let p_to_clients_new_leader_elected = p_is_leader.clone() - .continue_unless(p_next_slot) - .cross_singleton(p_ballot_num) - .map(q!(move |(_is_leader, ballot_num)| Ballot { num: ballot_num, proposer_id: p_id})) // Only tell the clients once when leader election concludes - .all_ticks(); - - // Acceptors. - acceptors - .source_iter(q!(["Acceptors say hello"])) - .for_each(q!(|s| println!("{}", s))); - let r_to_acceptors_checkpoint = r_to_acceptors_checkpoint(acceptors); - - // p_to_acceptors_p2a.clone().for_each(q!(|p2a: P2a| println!("Acceptor received P2a: {:?}", p2a))); - let (a_log, a_to_proposers_p2b) = acceptor_p2( - a_max_ballot.clone(), - p_to_acceptors_p2a, - r_to_acceptors_checkpoint, - proposers, - acceptors, - f, - ); - - // End tell clients that leader election has completed - let p_to_replicas = p_p2b(proposers, a_to_proposers_p2b.clone(), f); - - ( - p_to_clients_new_leader_elected, - p_to_replicas, - a_log, - a_to_proposers_p2b, - ) -} - #[expect( clippy::type_complexity, clippy::too_many_arguments, @@ -245,32 +166,25 @@ fn leader_election<'a, P: PaxosPayload>( proposers.forward_ref::>(); let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader) = proposers.forward_ref::>(); + let (p_is_leader_complete_cycle, p_is_leader) = + proposers.tick_forward_ref::>(); // a_to_proposers_p2b.clone().for_each(q!(|(_, p2b): (u32, P2b)| println!("Proposer received P2b: {:?}", p2b))); // p_to_proposers_i_am_leader.clone().for_each(q!(|ballot: Ballot| println!("Proposer received I am leader: {:?}", ballot))); // c_to_proposers.clone().for_each(q!(|payload: ClientPayload| println!("Client sent proposer payload: {:?}", payload))); let p_received_max_ballot = p_max_ballot( proposers, - a_to_proposers_p1b.clone(), + a_to_proposers_p1b, a_to_proposers_p2b, p_to_proposers_i_am_leader.clone(), ); let (p_ballot_num, p_has_largest_ballot) = p_ballot_calc(proposers, p_received_max_ballot.latest_tick()); - let (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) = p_p1b( - proposers, - a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), - p_ballot_num.clone(), - p_has_largest_ballot, - f, - ); - let (p_to_proposers_i_am_leader_from_others, p_to_acceptors_p1a) = p_p1a( p_ballot_num.clone(), p_is_leader.clone(), proposers, - p_to_proposers_i_am_leader, acceptors, i_am_leader_send_timeout, i_am_leader_check_timeout, @@ -288,12 +202,17 @@ fn leader_election<'a, P: PaxosPayload>( proposer_id: ClusterId::from_raw(0) }))); - a_to_proposers_p1b_complete_cycle.complete(acceptor_p1( - p_to_acceptors_p1a, - &a_max_ballot, - a_log, + let a_to_proposers_p1b = acceptor_p1(p_to_acceptors_p1a, &a_max_ballot, a_log, proposers); + a_to_proposers_p1b_complete_cycle.complete(a_to_proposers_p1b.clone()); + + let (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) = p_p1b( proposers, - )); + a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), + p_ballot_num.clone(), + p_has_largest_ballot, + f, + ); + p_is_leader_complete_cycle.complete(p_is_leader.clone()); ( p_ballot_num, @@ -305,282 +224,177 @@ fn leader_election<'a, P: PaxosPayload>( ) } -#[derive(Clone)] -enum CheckpointOrP2a

{ - Checkpoint(i32), - P2a(P2a

), +// Proposer logic to calculate the largest ballot received so far. +fn p_max_ballot<'a, P: PaxosPayload>( + proposers: &Cluster<'a, Proposer>, + a_to_proposers_p1b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + p_to_proposers_i_am_leader: Stream>, +) -> Singleton> { + let p_received_p1b_ballots = a_to_proposers_p1b.clone().map(q!(|p1b| p1b.max_ballot)); + let p_received_p2b_ballots = a_to_proposers_p2b.clone().map(q!(|p2b| p2b.ballot)); + p_received_p1b_ballots + .union(p_received_p2b_ballots) + .union(p_to_proposers_i_am_leader) + .max() + .unwrap_or(proposers.singleton(q!(Ballot { + num: 0, + proposer_id: ClusterId::from_raw(0) + }))) } +// Proposer logic to calculate the next ballot number. Expects p_received_max_ballot, the largest ballot received so far. Outputs streams: ballot_num, and has_largest_ballot, which only contains a value if we have the largest ballot. #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -fn acceptor_p2<'a, P: PaxosPayload, R>( - a_max_ballot: Singleton>, - p_to_acceptors_p2a: Stream, Unbounded, NoTick, Cluster<'a, Acceptor>>, - r_to_acceptors_checkpoint: Stream< - (ClusterId, i32), - Unbounded, - NoTick, - Cluster<'a, Acceptor>, - >, +fn p_ballot_calc<'a>( proposers: &Cluster<'a, Proposer>, - acceptors: &Cluster<'a, Acceptor>, - f: usize, + p_received_max_ballot: Singleton>, ) -> ( - Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, - Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + Singleton>, + Optional<(Ballot, u32), Bounded, Tick, Cluster<'a, Proposer>>, ) { - // Get the latest checkpoint sequence per replica - let a_checkpoint_largest_seqs = - r_to_acceptors_checkpoint - .tick_prefix() - .reduce_keyed(q!(|curr_seq, seq| { - if seq > *curr_seq { - *curr_seq = seq; - } - })); - let a_checkpoints_quorum_reached = a_checkpoint_largest_seqs.clone().count().filter_map(q!( - move |num_received| if num_received == f + 1 { - Some(true) - } else { - None - } - )); - // Find the smallest checkpoint seq that everyone agrees to, track whenever it changes - let a_new_checkpoint = a_checkpoint_largest_seqs - .continue_if(a_checkpoints_quorum_reached) - .map(q!(|(_sender, seq)| seq)) - .min() - .unwrap_or(acceptors.singleton(q!(-1)).latest_tick()) - .delta() - .map(q!(|min_seq| CheckpointOrP2a::Checkpoint(min_seq))); - // .inspect(q!(|(min_seq, p2a): &(i32, P2a)| println!("Acceptor new checkpoint: {:?}", min_seq))); + let p_id = proposers.self_id(); + let (p_ballot_num_complete_cycle, p_ballot_num) = + proposers.tick_cycle_with_initial(proposers.singleton(q!(0)).latest_tick()); - let a_p2as_to_place_in_log = p_to_acceptors_p2a + let p_new_ballot_num = p_received_max_ballot .clone() - .tick_batch() - .cross_singleton(a_max_ballot.clone().latest_tick()) // Don't consider p2as if the current ballot is higher - .filter_map(q!(|(p2a, max_ballot)| - if p2a.ballot >= max_ballot { - Some(CheckpointOrP2a::P2a(p2a)) + .cross_singleton(p_ballot_num.clone()) + .map(q!(move |(received_max_ballot, ballot_num)| { + if received_max_ballot + > (Ballot { + num: ballot_num, + proposer_id: p_id, + }) + { + received_max_ballot.num + 1 } else { - None + ballot_num } - )); - let a_log = a_p2as_to_place_in_log - .union(a_new_checkpoint.into_stream()) - .persist() - .fold( - q!(|| (-1, HashMap::new())), - q!(|(prev_checkpoint, log), checkpoint_or_p2a| { - match checkpoint_or_p2a { - CheckpointOrP2a::Checkpoint(new_checkpoint) => { - // This is a checkpoint message. Delete all entries up to the checkpoint - for slot in *prev_checkpoint..new_checkpoint { - log.remove(&slot); - } - *prev_checkpoint = new_checkpoint; - } - CheckpointOrP2a::P2a(p2a) => { - // This is a regular p2a message. Insert it into the log if it is not checkpointed and has a higher ballot than what was there before - if p2a.slot > *prev_checkpoint - && log - .get(&p2a.slot) - .map(|prev_p2a: &LogValue<_>| p2a.ballot > prev_p2a.ballot) - .unwrap_or(true) - { - log.insert( - p2a.slot, - LogValue { - ballot: p2a.ballot, - value: p2a.value, - }, - ); - } - } + })); + p_ballot_num_complete_cycle.complete_next_tick(p_new_ballot_num); + + let p_has_largest_ballot = p_received_max_ballot + .clone() + .cross_singleton(p_ballot_num.clone()) + .filter(q!( + move |(received_max_ballot, ballot_num)| *received_max_ballot + <= Ballot { + num: *ballot_num, + proposer_id: p_id } - }), - ); + )); - let a_to_proposers_p2b_new = p_to_acceptors_p2a - .tick_batch() - .cross_singleton(a_max_ballot.clone().latest_tick()) - .map(q!(|(p2a, max_ballot)| ( - p2a.ballot.proposer_id, - P2b { - victory: p2a.ballot == max_ballot, - ballot: p2a.ballot, - slot: p2a.slot, - value: p2a.value - } - ))) - .all_ticks() - .send_bincode_interleaved(proposers); - (a_log, a_to_proposers_p2b_new) + // End stable leader election + (p_ballot_num, p_has_largest_ballot) } -#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -fn acceptor_p1<'a, P: PaxosPayload>( - p_to_acceptors_p1a: Stream>, - a_max_ballot: &Singleton>, - a_log: Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, - proposers: &Cluster<'a, Proposer>, -) -> Stream, Unbounded, NoTick, Cluster<'a, Proposer>> { - p_to_acceptors_p1a - .tick_batch() - .cross_singleton(a_max_ballot.clone().latest_tick()) - .cross_singleton(a_log) - .map(q!(|((p1a, max_ballot), (_prev_checkpoint, log))| ( - p1a.ballot.proposer_id, - P1b { - ballot: p1a.ballot, - max_ballot, - accepted: log - } - ))) - .all_ticks() - .send_bincode_interleaved(proposers) -} +fn p_leader_expired<'a>( + p_to_proposers_i_am_leader: Stream>, + p_is_leader: Optional>, + i_am_leader_check_timeout: u64, // How often to check if heartbeat expired +) -> Optional, Bounded, Tick, Cluster<'a, Proposer>> { + let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold( + q!(|| None), + q!(|latest, _| { + // Note: May want to check received ballot against our own? + *latest = Some(Instant::now()); + }), + ); -fn p_p2b<'a, P: PaxosPayload>( - proposers: &Cluster<'a, Proposer>, - a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, - f: usize, -) -> Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>> { - let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposers.tick_cycle(); - let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposers.tick_cycle(); - let p_p2b = a_to_proposers_p2b - .clone() - .tick_batch() - .union(p_persisted_p2bs); - let p_count_matching_p2bs = p_p2b - .clone() - .filter_map(q!(|p2b| if p2b.victory { - // Only consider p2bs where max ballot = ballot, which means that no one preempted us - Some(((p2b.slot, p2b.ballot), p2b.value)) - } else { - None - })) - .fold_keyed( - q!(|| (0, Default::default())), - q!(|accum, value| { - // TODO(shadaj): why is sender unused? should we de-dup? - accum.0 += 1; - accum.1 = value; - }), - ); - let p_p2b_quorum_reached = - p_count_matching_p2bs - .clone() - .filter_map(q!(move |((slot, _ballot), (count, value))| if count > f { - Some((slot, value)) - } else { - None - })); - let p_to_replicas = p_p2b_quorum_reached + p_latest_received_i_am_leader .clone() - .anti_join(p_broadcasted_p2b_slots) // Only tell the replicas about committed values once - .map(q!(|(slot, value)| (slot, value))) - .all_ticks(); - - let p_p2b_all_commit_slots = - p_count_matching_p2bs.clone().filter_map(q!( - move |((slot, _ballot), (count, _p2b))| if count == 2 * f + 1 { - Some(slot) + .latest_tick() + .continue_unless(p_is_leader) + .filter(q!(move |latest_received_i_am_leader| { + if let Some(latest_received_i_am_leader) = latest_received_i_am_leader { + (Instant::now().duration_since(*latest_received_i_am_leader)) + > Duration::from_secs(i_am_leader_check_timeout) } else { - None + true } - )); - // p_p2b_all_commit_slots.inspect(q!(|slot: i32| println!("Proposer slot all received: {:?}", slot))); - let p_broadcasted_p2b_slots_new = p_p2b_quorum_reached - .clone() - .map(q!(|(slot, _value)| slot)) - .filter_not_in(p_p2b_all_commit_slots.clone()); - // p_broadcasted_p2b_slots_new.inspect(q!(|slot: i32| println!("Proposer slot broadcasted: {:?}", slot))); - p_broadcasted_p2b_slots_complete_cycle.complete_next_tick(p_broadcasted_p2b_slots_new); - let p_persisted_p2bs_new = p_p2b - .clone() - .map(q!(|p2b| (p2b.slot, p2b))) - .anti_join(p_p2b_all_commit_slots.clone()) - .map(q!(|(_slot, p2b)| p2b)); - // TOOD: only persist if we are the leader - // p_persisted_p2bs_new.inspect(q!(|(sender, p2b): (u32, P2b)| println!("Proposer persisting p2b: {:?}", p2b))); - p_persisted_p2bs_complete_cycle.complete_next_tick(p_persisted_p2bs_new); - p_to_replicas + })) } -// Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. -#[expect( - clippy::type_complexity, - clippy::too_many_arguments, - reason = "internal paxos code // TODO" -)] -fn p_p2a<'a, P: PaxosPayload>( - proposers: &Cluster<'a, Proposer>, - p_max_slot: Optional>, - c_to_proposers: Stream>, +// Proposer logic to send "I am leader" messages periodically to other proposers, or send p1a to acceptors if other leaders expired. +#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] +fn p_p1a<'a>( p_ballot_num: Singleton>, - p_log_to_try_commit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, - p_log_holes: Stream, Bounded, Tick, Cluster<'a, Proposer>>, p_is_leader: Optional>, + proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, + i_am_leader_send_timeout: u64, // How often to heartbeat + i_am_leader_check_timeout: u64, // How often to check if heartbeat expired + i_am_leader_check_timeout_delay_multiplier: usize, /* Initial delay, multiplied by proposer pid, to stagger proposers checking for timeouts */ ) -> ( - Optional>, - Stream, Unbounded, NoTick, Cluster<'a, Acceptor>>, + Stream>, + Stream>, ) { let p_id = proposers.self_id(); - let (p_next_slot_complete_cycle, p_next_slot) = - proposers.tick_cycle::>(); - let p_next_slot_after_reconciling_p1bs = p_max_slot - .unwrap_or(proposers.singleton(q!(-1)).latest_tick()) - // .inspect(q!(|max_slot| println!("{} p_max_slot: {:?}", context.current_tick(), max_slot))) - .continue_unless(p_next_slot.clone()) - .map(q!(|max_slot| max_slot + 1)); - - // Send p2as - let p_indexed_payloads = c_to_proposers + let p_to_proposers_i_am_leader = p_is_leader .clone() - .tick_batch() - .enumerate() - .cross_singleton(p_next_slot.clone()) - // .inspect(q!(|next| println!("{} p_indexed_payloads next slot: {}", context.current_tick(), next)))) - .cross_singleton(p_ballot_num.clone()) - // .inspect(q!(|ballot_num| println!("{} p_indexed_payloads ballot_num: {}", context.current_tick(), ballot_num)))) - .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { ballot: Ballot { num: ballot_num, proposer_id: p_id }, slot: next_slot + index as i32, value: payload })); - // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); - let p_to_acceptors_p2a = p_log_to_try_commit - .union(p_log_holes) - .continue_unless(p_next_slot.clone()) // Only resend p1b stuff once. Once it's resent, next_slot will exist. - .union(p_indexed_payloads) - .continue_if(p_is_leader.clone()) + .then(p_ballot_num.clone()) + .latest() + .sample_every(q!(Duration::from_secs(i_am_leader_send_timeout))) + .map(q!(move |ballot_num| Ballot { + num: ballot_num, + proposer_id: p_id + })) + .broadcast_bincode_interleaved(proposers); + + let p_leader_expired = p_leader_expired( + p_to_proposers_i_am_leader.clone(), + p_is_leader, + i_am_leader_check_timeout, + ); + + let p_id = proposers.self_id(); + + // Add random delay depending on node ID so not everyone sends p1a at the same time + let p_to_acceptors_p1a = p_leader_expired + .then(p_ballot_num) + .continue_if( + proposers + .source_interval_delayed( + q!(Duration::from_secs( + (p_id.raw_id * i_am_leader_check_timeout_delay_multiplier as u32).into() + )), + q!(Duration::from_secs(i_am_leader_check_timeout)), + ) + .latest_tick(), + ) + .map(q!(move |ballot_num| P1a { + ballot: Ballot { + num: ballot_num, + proposer_id: p_id + } + })) .all_ticks() + .inspect(q!(|_| println!("Proposer leader expired, sending P1a"))) .broadcast_bincode_interleaved(acceptors); + (p_to_proposers_i_am_leader, p_to_acceptors_p1a) +} - let p_num_payloads = c_to_proposers.clone().tick_batch().count(); - let p_exists_payloads = p_num_payloads - .clone() - .filter(q!(|num_payloads| *num_payloads > 0)); - let p_next_slot_after_sending_payloads = p_num_payloads - .continue_if(p_exists_payloads.clone()) - .clone() - .cross_singleton(p_next_slot.clone()) - .map(q!( - |(num_payloads, next_slot)| next_slot + num_payloads as i32 - )); - let p_next_slot_if_no_payloads = p_next_slot.clone().continue_unless(p_exists_payloads); - let p_new_next_slot_calculated = p_next_slot_after_reconciling_p1bs - // .inspect(q!(|slot| println!("{} p_new_next_slot_after_reconciling_p1bs: {:?}", context.current_tick(), slot))) - .union(p_next_slot_after_sending_payloads) - // .inspect(q!(|slot| println!("{} p_next_slot_after_sending_payloads: {:?}", context.current_tick(), slot)))) - .union(p_next_slot_if_no_payloads) - // .inspect(q!(|slot| println!("{} p_next_slot_if_no_payloads: {:?}", context.current_tick(), slot)))) - .continue_if(p_is_leader.clone()); - let p_new_next_slot_default = p_is_leader // Default next slot to 0 if there haven't been any payloads at all - .clone() - .continue_unless(p_new_next_slot_calculated.clone()) - .map(q!(|_| 0)); - // .inspect(q!(|slot| println!("{} p_new_next_slot_default: {:?}", context.current_tick(), slot))); - let p_new_next_slot = p_new_next_slot_calculated.union(p_new_next_slot_default); - p_next_slot_complete_cycle.complete_next_tick(p_new_next_slot); - (p_next_slot, p_to_acceptors_p2a) +#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] +fn acceptor_p1<'a, P: PaxosPayload>( + p_to_acceptors_p1a: Stream>, + a_max_ballot: &Singleton>, + a_log: Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, + proposers: &Cluster<'a, Proposer>, +) -> Stream, Unbounded, NoTick, Cluster<'a, Proposer>> { + p_to_acceptors_p1a + .tick_batch() + .cross_singleton(a_max_ballot.clone().latest_tick()) + .cross_singleton(a_log) + .map(q!(|((p1a, max_ballot), (_prev_checkpoint, log))| ( + p1a.ballot.proposer_id, + P1b { + ballot: p1a.ballot, + max_ballot, + accepted: log + } + ))) + .all_ticks() + .send_bincode_interleaved(proposers) } // Proposer logic for processing p1bs, determining if the proposer is now the leader, which uncommitted messages to commit, what the maximum slot is in the p1bs, and which no-ops to commit to fill log holes. @@ -675,157 +489,336 @@ fn p_p1b<'a, P: PaxosPayload>( (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) } -// Proposer logic to calculate the largest ballot received so far. -fn p_max_ballot<'a, P: PaxosPayload>( +#[expect( + clippy::type_complexity, + clippy::too_many_arguments, + reason = "internal paxos code // TODO" +)] +fn sequence_payload<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, - a_to_proposers_p1b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, - a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, - p_to_proposers_i_am_leader: Stream>, -) -> Singleton> { - let p_received_p1b_ballots = a_to_proposers_p1b.clone().map(q!(|p1b| p1b.max_ballot)); - let p_received_p2b_ballots = a_to_proposers_p2b.clone().map(q!(|p2b| p2b.ballot)); - p_received_p1b_ballots - .union(p_received_p2b_ballots) - .union(p_to_proposers_i_am_leader) - .max() - .unwrap_or(proposers.singleton(q!(Ballot { - num: 0, - proposer_id: ClusterId::from_raw(0) - }))) -} + acceptors: &Cluster<'a, Acceptor>, + c_to_proposers: Stream>, + r_to_acceptors_checkpoint: impl FnOnce( + &Cluster<'a, Acceptor>, + ) -> Stream< + (ClusterId, i32), + Unbounded, + NoTick, + Cluster<'a, Acceptor>, + >, -// Proposer logic to calculate the next ballot number. Expects p_received_max_ballot, the largest ballot received so far. Outputs streams: ballot_num, and has_largest_ballot, which only contains a value if we have the largest ballot. -#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -fn p_ballot_calc<'a>( - proposers: &Cluster<'a, Proposer>, - p_received_max_ballot: Singleton>, + p_ballot_num: Singleton>, + p_is_leader: Optional>, + p_max_slot: Optional>, + + p_log_to_try_commit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, + p_log_holes: Stream, Bounded, Tick, Cluster<'a, Proposer>>, + f: usize, + + a_max_ballot: Singleton>, ) -> ( - Singleton>, - Optional<(Ballot, u32), Bounded, Tick, Cluster<'a, Proposer>>, + Stream>, + Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, + Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, + Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, ) { let p_id = proposers.self_id(); - let (p_ballot_num_complete_cycle, p_ballot_num) = - proposers.tick_cycle_with_initial(proposers.singleton(q!(0)).latest_tick()); + let (p_next_slot, p_to_acceptors_p2a) = p_p2a( + proposers, + p_max_slot, + c_to_proposers, + p_ballot_num.clone(), + p_log_to_try_commit, + p_log_holes, + p_is_leader.clone(), + acceptors, + ); - let p_new_ballot_num = p_received_max_ballot - .clone() - .cross_singleton(p_ballot_num.clone()) - .map(q!(move |(received_max_ballot, ballot_num)| { - if received_max_ballot - > (Ballot { - num: ballot_num, - proposer_id: p_id, - }) - { - received_max_ballot.num + 1 - } else { - ballot_num - } - })); - p_ballot_num_complete_cycle.complete_next_tick(p_new_ballot_num); + // Tell clients that leader election has completed and they can begin sending messages + let p_to_clients_new_leader_elected = p_is_leader.clone() + .continue_unless(p_next_slot) + .cross_singleton(p_ballot_num) + .map(q!(move |(_is_leader, ballot_num)| Ballot { num: ballot_num, proposer_id: p_id})) // Only tell the clients once when leader election concludes + .all_ticks(); - let p_has_largest_ballot = p_received_max_ballot - .clone() - .cross_singleton(p_ballot_num.clone()) - .filter(q!( - move |(received_max_ballot, ballot_num)| *received_max_ballot - <= Ballot { - num: *ballot_num, - proposer_id: p_id - } - )); + // Acceptors. + acceptors + .source_iter(q!(["Acceptors say hello"])) + .for_each(q!(|s| println!("{}", s))); + let r_to_acceptors_checkpoint = r_to_acceptors_checkpoint(acceptors); - // End stable leader election - (p_ballot_num, p_has_largest_ballot) + // p_to_acceptors_p2a.clone().for_each(q!(|p2a: P2a| println!("Acceptor received P2a: {:?}", p2a))); + let (a_log, a_to_proposers_p2b) = acceptor_p2( + a_max_ballot.clone(), + p_to_acceptors_p2a, + r_to_acceptors_checkpoint, + proposers, + acceptors, + f, + ); + + // End tell clients that leader election has completed + let p_to_replicas = p_p2b(proposers, a_to_proposers_p2b.clone(), f); + + ( + p_to_clients_new_leader_elected, + p_to_replicas, + a_log, + a_to_proposers_p2b, + ) } -// Proposer logic to send "I am leader" messages periodically to other proposers, or send p1a to acceptors if other leaders expired. +#[derive(Clone)] +enum CheckpointOrP2a

{ + Checkpoint(i32), + P2a(P2a

), +} + +// Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. #[expect( - clippy::too_many_arguments, clippy::type_complexity, + clippy::too_many_arguments, reason = "internal paxos code // TODO" )] -fn p_p1a<'a>( +fn p_p2a<'a, P: PaxosPayload>( + proposers: &Cluster<'a, Proposer>, + p_max_slot: Optional>, + c_to_proposers: Stream>, p_ballot_num: Singleton>, + p_log_to_try_commit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, + p_log_holes: Stream, Bounded, Tick, Cluster<'a, Proposer>>, p_is_leader: Optional>, - proposers: &Cluster<'a, Proposer>, - p_to_proposers_i_am_leader: Stream>, acceptors: &Cluster<'a, Acceptor>, - i_am_leader_send_timeout: u64, // How often to heartbeat - i_am_leader_check_timeout: u64, // How often to check if heartbeat expired - i_am_leader_check_timeout_delay_multiplier: usize, /* Initial delay, multiplied by proposer pid, to stagger proposers checking for timeouts */ ) -> ( - Stream>, - Stream>, + Optional>, + Stream, Unbounded, NoTick, Cluster<'a, Acceptor>>, ) { let p_id = proposers.self_id(); - let p_to_proposers_i_am_leader_new = p_is_leader + let (p_next_slot_complete_cycle, p_next_slot) = + proposers.tick_cycle::>(); + let p_next_slot_after_reconciling_p1bs = p_max_slot + .unwrap_or(proposers.singleton(q!(-1)).latest_tick()) + // .inspect(q!(|max_slot| println!("{} p_max_slot: {:?}", context.current_tick(), max_slot))) + .continue_unless(p_next_slot.clone()) + .map(q!(|max_slot| max_slot + 1)); + + // Send p2as + let p_indexed_payloads = c_to_proposers .clone() - .then(p_ballot_num.clone()) - .latest() - .sample_every(q!(Duration::from_secs(i_am_leader_send_timeout))) - .map(q!(move |ballot_num| Ballot { - num: ballot_num, - proposer_id: p_id - })) - .broadcast_bincode_interleaved(proposers); + .tick_batch() + .enumerate() + .cross_singleton(p_next_slot.clone()) + // .inspect(q!(|next| println!("{} p_indexed_payloads next slot: {}", context.current_tick(), next)))) + .cross_singleton(p_ballot_num.clone()) + // .inspect(q!(|ballot_num| println!("{} p_indexed_payloads ballot_num: {}", context.current_tick(), ballot_num)))) + .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { ballot: Ballot { num: ballot_num, proposer_id: p_id }, slot: next_slot + index as i32, value: payload })); + // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); + let p_to_acceptors_p2a = p_log_to_try_commit + .union(p_log_holes) + .continue_unless(p_next_slot.clone()) // Only resend p1b stuff once. Once it's resent, next_slot will exist. + .union(p_indexed_payloads) + .continue_if(p_is_leader.clone()) + .all_ticks() + .broadcast_bincode_interleaved(acceptors); - let p_leader_expired = p_leader_expired( - p_to_proposers_i_am_leader, - p_is_leader, - i_am_leader_check_timeout, - ); + let p_num_payloads = c_to_proposers.clone().tick_batch().count(); + let p_exists_payloads = p_num_payloads + .clone() + .filter(q!(|num_payloads| *num_payloads > 0)); + let p_next_slot_after_sending_payloads = p_num_payloads + .continue_if(p_exists_payloads.clone()) + .clone() + .cross_singleton(p_next_slot.clone()) + .map(q!( + |(num_payloads, next_slot)| next_slot + num_payloads as i32 + )); + let p_next_slot_if_no_payloads = p_next_slot.clone().continue_unless(p_exists_payloads); + let p_new_next_slot_calculated = p_next_slot_after_reconciling_p1bs + // .inspect(q!(|slot| println!("{} p_new_next_slot_after_reconciling_p1bs: {:?}", context.current_tick(), slot))) + .union(p_next_slot_after_sending_payloads) + // .inspect(q!(|slot| println!("{} p_next_slot_after_sending_payloads: {:?}", context.current_tick(), slot)))) + .union(p_next_slot_if_no_payloads) + // .inspect(q!(|slot| println!("{} p_next_slot_if_no_payloads: {:?}", context.current_tick(), slot)))) + .continue_if(p_is_leader.clone()); + let p_new_next_slot_default = p_is_leader // Default next slot to 0 if there haven't been any payloads at all + .clone() + .continue_unless(p_new_next_slot_calculated.clone()) + .map(q!(|_| 0)); + // .inspect(q!(|slot| println!("{} p_new_next_slot_default: {:?}", context.current_tick(), slot))); + let p_new_next_slot = p_new_next_slot_calculated.union(p_new_next_slot_default); + p_next_slot_complete_cycle.complete_next_tick(p_new_next_slot); + (p_next_slot, p_to_acceptors_p2a) +} - let p_id = proposers.self_id(); +#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] +fn acceptor_p2<'a, P: PaxosPayload, R>( + a_max_ballot: Singleton>, + p_to_acceptors_p2a: Stream, Unbounded, NoTick, Cluster<'a, Acceptor>>, + r_to_acceptors_checkpoint: Stream< + (ClusterId, i32), + Unbounded, + NoTick, + Cluster<'a, Acceptor>, + >, + proposers: &Cluster<'a, Proposer>, + acceptors: &Cluster<'a, Acceptor>, + f: usize, +) -> ( + Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, + Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, +) { + // Get the latest checkpoint sequence per replica + let a_checkpoint_largest_seqs = + r_to_acceptors_checkpoint + .tick_prefix() + .reduce_keyed(q!(|curr_seq, seq| { + if seq > *curr_seq { + *curr_seq = seq; + } + })); + let a_checkpoints_quorum_reached = a_checkpoint_largest_seqs.clone().count().filter_map(q!( + move |num_received| if num_received == f + 1 { + Some(true) + } else { + None + } + )); + // Find the smallest checkpoint seq that everyone agrees to, track whenever it changes + let a_new_checkpoint = a_checkpoint_largest_seqs + .continue_if(a_checkpoints_quorum_reached) + .map(q!(|(_sender, seq)| seq)) + .min() + .unwrap_or(acceptors.singleton(q!(-1)).latest_tick()) + .delta() + .map(q!(|min_seq| CheckpointOrP2a::Checkpoint(min_seq))); + // .inspect(q!(|(min_seq, p2a): &(i32, P2a)| println!("Acceptor new checkpoint: {:?}", min_seq))); - // Add random delay depending on node ID so not everyone sends p1a at the same time - let p_to_acceptors_p1a = p_leader_expired - .then(p_ballot_num) - .continue_if( - proposers - .source_interval_delayed( - q!(Duration::from_secs( - (p_id.raw_id * i_am_leader_check_timeout_delay_multiplier as u32).into() - )), - q!(Duration::from_secs(i_am_leader_check_timeout)), - ) - .latest_tick(), - ) - .map(q!(move |ballot_num| P1a { - ballot: Ballot { - num: ballot_num, - proposer_id: p_id + let a_p2as_to_place_in_log = p_to_acceptors_p2a + .clone() + .tick_batch() + .cross_singleton(a_max_ballot.clone().latest_tick()) // Don't consider p2as if the current ballot is higher + .filter_map(q!(|(p2a, max_ballot)| + if p2a.ballot >= max_ballot { + Some(CheckpointOrP2a::P2a(p2a)) + } else { + None } - })) + )); + let a_log = a_p2as_to_place_in_log + .union(a_new_checkpoint.into_stream()) + .persist() + .fold( + q!(|| (-1, HashMap::new())), + q!(|(prev_checkpoint, log), checkpoint_or_p2a| { + match checkpoint_or_p2a { + CheckpointOrP2a::Checkpoint(new_checkpoint) => { + // This is a checkpoint message. Delete all entries up to the checkpoint + for slot in *prev_checkpoint..new_checkpoint { + log.remove(&slot); + } + *prev_checkpoint = new_checkpoint; + } + CheckpointOrP2a::P2a(p2a) => { + // This is a regular p2a message. Insert it into the log if it is not checkpointed and has a higher ballot than what was there before + if p2a.slot > *prev_checkpoint + && log + .get(&p2a.slot) + .map(|prev_p2a: &LogValue<_>| p2a.ballot > prev_p2a.ballot) + .unwrap_or(true) + { + log.insert( + p2a.slot, + LogValue { + ballot: p2a.ballot, + value: p2a.value, + }, + ); + } + } + } + }), + ); + + let a_to_proposers_p2b = p_to_acceptors_p2a + .tick_batch() + .cross_singleton(a_max_ballot.clone().latest_tick()) + .map(q!(|(p2a, max_ballot)| ( + p2a.ballot.proposer_id, + P2b { + victory: p2a.ballot == max_ballot, + ballot: p2a.ballot, + slot: p2a.slot, + value: p2a.value + } + ))) .all_ticks() - .inspect(q!(|_| println!("Proposer leader expired, sending P1a"))) - .broadcast_bincode_interleaved(acceptors); - (p_to_proposers_i_am_leader_new, p_to_acceptors_p1a) + .send_bincode_interleaved(proposers); + (a_log, a_to_proposers_p2b) } -fn p_leader_expired<'a>( - p_to_proposers_i_am_leader: Stream>, - p_is_leader: Optional>, - i_am_leader_check_timeout: u64, // How often to check if heartbeat expired -) -> Optional, Bounded, Tick, Cluster<'a, Proposer>> { - let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold( - q!(|| None), - q!(|latest, _| { - // Note: May want to check received ballot against our own? - *latest = Some(Instant::now()); - }), - ); - - p_latest_received_i_am_leader +fn p_p2b<'a, P: PaxosPayload>( + proposers: &Cluster<'a, Proposer>, + a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + f: usize, +) -> Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>> { + let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposers.tick_cycle(); + let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposers.tick_cycle(); + let p_p2b = a_to_proposers_p2b .clone() - .latest_tick() - .continue_unless(p_is_leader) - .filter(q!(move |latest_received_i_am_leader| { - if let Some(latest_received_i_am_leader) = latest_received_i_am_leader { - (Instant::now().duration_since(*latest_received_i_am_leader)) - > Duration::from_secs(i_am_leader_check_timeout) + .tick_batch() + .union(p_persisted_p2bs); + let p_count_matching_p2bs = p_p2b + .clone() + .filter_map(q!(|p2b| if p2b.victory { + // Only consider p2bs where max ballot = ballot, which means that no one preempted us + Some(((p2b.slot, p2b.ballot), p2b.value)) + } else { + None + })) + .fold_keyed( + q!(|| (0, Default::default())), + q!(|accum, value| { + // TODO(shadaj): why is sender unused? should we de-dup? + accum.0 += 1; + accum.1 = value; + }), + ); + let p_p2b_quorum_reached = + p_count_matching_p2bs + .clone() + .filter_map(q!(move |((slot, _ballot), (count, value))| if count > f { + Some((slot, value)) } else { - true + None + })); + let p_to_replicas = p_p2b_quorum_reached + .clone() + .anti_join(p_broadcasted_p2b_slots) // Only tell the replicas about committed values once + .map(q!(|(slot, value)| (slot, value))) + .all_ticks(); + + let p_p2b_all_commit_slots = + p_count_matching_p2bs.clone().filter_map(q!( + move |((slot, _ballot), (count, _p2b))| if count == 2 * f + 1 { + Some(slot) + } else { + None } - })) + )); + // p_p2b_all_commit_slots.inspect(q!(|slot: i32| println!("Proposer slot all received: {:?}", slot))); + let p_broadcasted_p2b_slots_new = p_p2b_quorum_reached + .clone() + .map(q!(|(slot, _value)| slot)) + .filter_not_in(p_p2b_all_commit_slots.clone()); + // p_broadcasted_p2b_slots_new.inspect(q!(|slot: i32| println!("Proposer slot broadcasted: {:?}", slot))); + p_broadcasted_p2b_slots_complete_cycle.complete_next_tick(p_broadcasted_p2b_slots_new); + let p_persisted_p2bs_new = p_p2b + .clone() + .map(q!(|p2b| (p2b.slot, p2b))) + .anti_join(p_p2b_all_commit_slots.clone()) + .map(q!(|(_slot, p2b)| p2b)); + // TOOD: only persist if we are the leader + // p_persisted_p2bs_new.inspect(q!(|(sender, p2b): (u32, P2b)| println!("Proposer persisting p2b: {:?}", p2b))); + p_persisted_p2bs_complete_cycle.complete_next_tick(p_persisted_p2bs_new); + p_to_replicas } diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index adbac8d56634..5614a8d634b0 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -16,7 +16,7 @@ expression: built.ir() }, CycleSink { ident: Ident { - sym: cycle_3, + sym: cycle_4, }, location_kind: Cluster( 2, @@ -88,7 +88,7 @@ expression: built.ir() inner: : Union( CycleSource { ident: Ident { - sym: cycle_3, + sym: cycle_4, }, location_kind: Cluster( 2, @@ -117,119 +117,80 @@ expression: built.ir() location_kind: Cluster( 2, ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 2, - ), - from_key: None, - to_location: Cluster( - 2, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: Ballot) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& data) . unwrap () . into ()) }", - ], - }, + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 2, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 2, ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | Ballot { num : ballot_num , proposer_id : p_id } }), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: Ballot) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), - input: CrossSingleton( - Persist( - Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), - input: Tee { - inner: , - }, - }, - ), - Tee { - inner: , - }, - ), - }, - }, - }, - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Tee { - inner: : Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - }, + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | Ballot { num : ballot_num , proposer_id : p_id } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Tee { + inner: : CycleSource { + ident: Ident { + sym: cycle_3, }, - ), + location_kind: Cluster( + 2, + ), + }, }, }, - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Source { - source: Interval( - stageleft :: runtime_support :: type_hint :: < core :: time :: Duration > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) }), - ), - location_kind: Cluster( - 2, ), }, - }, - ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Source { + source: Interval( + stageleft :: runtime_support :: type_hint :: < core :: time :: Duration > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) }), + ), + location_kind: Cluster( + 2, + ), + }, + }, + ), + }, }, }, }, @@ -243,191 +204,252 @@ expression: built.ir() location_kind: Cluster( 2, ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 3, - ), - from_key: None, - to_location: Cluster( - 2, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", - ], - }, + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 3, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 2, ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >)) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , (_prev_checkpoint , log)) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), - input: CrossSingleton( - CrossSingleton( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 2, - ), - from_key: None, - to_location: Cluster( - 3, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1a) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& data) . unwrap () . into ()) }", - ], - }, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >)) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , (_prev_checkpoint , log)) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + input: CrossSingleton( + CrossSingleton( + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 2, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 3, ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , proposer_id : p_id } } }), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1a) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), - input: Persist( - Tee { - inner: , - }, - ), + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , proposer_id : p_id } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), + input: Persist( + Tee { + inner: , + }, + ), + }, }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, }, }, }, - }, - ), + ), + }, }, }, - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Source { - source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, - ), - location_kind: Cluster( - 2, ), }, - }, - ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Source { + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, + ), + location_kind: Cluster( + 2, + ), + }, + }, + ), + }, }, }, }, }, }, }, - }, - Tee { - inner: : Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . ballot }), - input: Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), - input: Tee { - inner: , + Tee { + inner: : Union( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . ballot }), + input: Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), + input: Tee { + inner: , + }, }, }, - }, - ), - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, - ), - location_kind: Cluster( - 3, ), }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, + ), + location_kind: Cluster( + 3, + ), + }, + ), ), + }, + ), + CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 3, ), }, ), - CycleSource { - ident: Ident { - sym: cycle_0, + }, + }, + }, + }, + }, + CycleSink { + ident: Ident { + sym: cycle_3, + }, + location_kind: Cluster( + 2, + ), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), + input: CrossSingleton( + Persist( + Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + input: Tee { + inner: , + }, + }, + ), + Tee { + inner: , + }, + ), + }, + }, }, - location_kind: Cluster( - 3, - ), }, - ), - }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Tee { + inner: : Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, + }, + }, + ), }, }, }, CycleSink { ident: Ident { - sym: cycle_4, + sym: cycle_5, }, location_kind: Cluster( 2, @@ -435,7 +457,7 @@ expression: built.ir() input: DeferTick( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( @@ -447,18 +469,18 @@ expression: built.ir() input: CrossSingleton( Union( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), input: FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { - inner: , + inner: , }, }, }, @@ -485,9 +507,9 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { - sym: cycle_4, + sym: cycle_5, }, location_kind: Cluster( 2, @@ -504,15 +526,15 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads as i32 }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -560,10 +582,10 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: : Filter { + inner: : Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | num_payloads | * num_payloads > 0 }), input: Tee { - inner: , + inner: , }, }, }, @@ -572,7 +594,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -581,7 +603,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -591,7 +613,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -602,7 +624,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -614,7 +636,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -624,7 +646,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -648,7 +670,7 @@ expression: built.ir() }, CycleSink { ident: Ident { - sym: cycle_5, + sym: cycle_6, }, location_kind: Cluster( 2, @@ -658,18 +680,18 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), input: FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . victory { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { - inner: : Union( + inner: : Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -705,7 +727,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { victory : p2a . ballot == max_ballot , ballot : p2a . ballot , slot : p2a . slot , value : p2a . value }) }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -751,7 +773,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { inner: , @@ -765,13 +787,13 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: , + inner: , }, }, ), @@ -789,7 +811,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -802,11 +824,11 @@ expression: built.ir() CrossSingleton( Enumerate( Tee { - inner: , + inner: , }, ), Tee { - inner: , + inner: , }, ), Tee { @@ -818,7 +840,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -837,7 +859,7 @@ expression: built.ir() }, CycleSource { ident: Ident { - sym: cycle_6, + sym: cycle_7, }, location_kind: Cluster( 2, @@ -852,10 +874,10 @@ expression: built.ir() }, }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -864,7 +886,7 @@ expression: built.ir() }, CycleSink { ident: Ident { - sym: cycle_6, + sym: cycle_7, }, location_kind: Cluster( 2, @@ -876,11 +898,11 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { - inner: , + inner: , }, }, Tee { - inner: , + inner: , }, ), }, @@ -894,7 +916,7 @@ expression: built.ir() 3, ), input: Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), input: Persist( @@ -903,7 +925,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { inner: , @@ -922,7 +944,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : ReduceKeyed { + inner: : ReduceKeyed { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), input: Persist( Network { @@ -978,7 +1000,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1013,7 +1035,7 @@ expression: built.ir() 2, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1030,10 +1052,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -1073,11 +1095,11 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , value) | (slot , value) }), input: AntiJoin( Tee { - inner: , + inner: , }, CycleSource { ident: Ident { - sym: cycle_5, + sym: cycle_6, }, location_kind: Cluster( 2, @@ -1102,12 +1124,12 @@ expression: built.ir() ), }, Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | - 1 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Union( CycleSource { @@ -1144,23 +1166,23 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . key , payload . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1181,7 +1203,7 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( @@ -1210,7 +1232,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1225,7 +1247,7 @@ expression: built.ir() 1, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1238,7 +1260,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1274,7 +1296,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . value . parse :: < u32 > () . unwrap ()) , payload) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1290,13 +1312,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1317,7 +1339,7 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_2, }, @@ -1331,9 +1353,9 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: : Delta( + inner: : Delta( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { @@ -1379,7 +1401,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -1389,7 +1411,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1414,10 +1436,10 @@ expression: built.ir() }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1430,7 +1452,7 @@ expression: built.ir() input: CrossSingleton( CrossSingleton( Tee { - inner: : Source { + inner: : Source { source: Interval( { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, ), @@ -1448,10 +1470,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1459,7 +1481,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: , + inner: , }, }, ), @@ -1481,7 +1503,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1492,7 +1514,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1504,7 +1526,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1524,17 +1546,17 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot . leader_id () , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot . leader_id () , ClientPayload { key , value : c_id . raw_id . to_string () }) }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, From b6e5426d57b510debbe9a819d31c546734e75cba Mon Sep 17 00:00:00 2001 From: Ice cola <90940182+IceCoooola@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:19:32 -0700 Subject: [PATCH 11/74] chore: remove unexpected file (#1511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git push --set-upstream origin clean Co-authored-by: “Dee --- hydroflow_plus_test/src/cluster/pbft.rs | 390 ------------------------ 1 file changed, 390 deletions(-) delete mode 100644 hydroflow_plus_test/src/cluster/pbft.rs diff --git a/hydroflow_plus_test/src/cluster/pbft.rs b/hydroflow_plus_test/src/cluster/pbft.rs deleted file mode 100644 index ce128330fa8b..000000000000 --- a/hydroflow_plus_test/src/cluster/pbft.rs +++ /dev/null @@ -1,390 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::time::{Duration, SystemTime}; - -use hydroflow_plus::*; -use serde::{Deserialize, Serialize}; -use stageleft::*; -use tokio::time::Instant; - -pub struct Client {} -pub struct Replica {} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] -pub struct Request { - pub client_id: u32, - pub operation: String, - pub timestamp: u64, -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] -pub struct Reply { - pub view: u32, - pub timestamp: u64, - pub client_id: u32, - pub replica_id: u32, - pub result: String, -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] -pub struct PrePrepare { - pub view: u32, - pub sequence_number: u64, - pub request: Request, - pub digest: String, -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] -pub struct Prepare { - pub view: u32, - pub sequence_number: u64, - pub digest: String, - pub replica_id: u32, -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] -pub struct Commit { - pub view: u32, - pub sequence_number: u64, - pub digest: String, - pub replica_id: u32, -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] -pub struct ViewChange { - pub view: u32, - pub replica_id: u32, - pub last_sequence_number: u64, - pub P: Vec, -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] -pub struct NewView { - pub view: u32, - pub V: Vec, - pub O: Vec, -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] -pub struct Prepared { - pub view: u32, - pub sequence_number: u64, - pub digest: String, -} - -pub fn pbft<'a>( - flow: &FlowBuilder<'a>, - num_clients_per_node: usize, - f: usize, - throughput_window_size: usize, - view_timeout: u64, -) -> ( - Cluster, - Cluster, -) { - let clients = flow.cluster::(); - let replicas = flow.cluster::(); - - let client_id = flow.cluster_self_id(&clients); - let replica_id = flow.cluster_self_id(&replicas); - - let num_replicas = 3 * f + 1; - - // Define the current view number, starting from 0 - let (current_view_cycle, current_view_stream) = flow.tick_cycle_with_initial( - &replicas, - flow.singleton(&replicas, q!(0u32)).latest_tick(), - ); - let current_view = current_view_stream.latest(); - - // Each replica determines if it's the primary (leader) for the current view - let is_primary = current_view - .map(q!(move |view| (*view % (num_replicas as u32)) == replica_id)) - .latest_tick(); - - // Client sends requests - let client_requests = flow.source_iter(&clients, q!([ - Request { - client_id, - operation: "operation1".to_string(), - timestamp: 1, - }, - Request { - client_id, - operation: "operation2".to_string(), - timestamp: 2, - }, - Request { - client_id, - operation: "operation3".to_string(), - timestamp: 3, - }, - ])); - - // Clients broadcast requests to all replicas - let requests_to_replicas = client_requests.broadcast_bincode(&replicas); - - // Each replica filters requests intended for the current primary - let requests_to_primary = requests_to_replicas - .cross_singleton(current_view.clone()) - .filter(q!(move |(request, view)| { - (*view % (num_replicas as u32)) == replica_id - })) - .map(q!(|(request, _)| request.clone())) - .continue_if(is_primary.clone()); - - // The primary processes the requests and creates PrePrepare messages - let (sequence_number_cycle, sequence_number_stream) = flow.tick_cycle_with_initial( - &replicas, - flow.singleton(&replicas, q!(0u64)).latest_tick(), - ); - let sequence_number = sequence_number_stream.latest(); - - let pre_prepares = requests_to_primary - .enumerate() - .cross_singleton(current_view.clone()) - .cross_singleton(sequence_number.clone()) - .map(q!(move |(((index, request), view), seq_num)| PrePrepare { - view: *view, - sequence_number: *seq_num + index as u64 + 1, - request: request.clone(), - digest: format!("{:?}", request), - })) - .persist(); - - // Update the sequence number for the next batch - let next_sequence_number = sequence_number - .cross_singleton(pre_prepares.clone().count()) - .map(q!(|(seq_num, count)| seq_num + count as u64)) - .defer_tick(); - sequence_number_cycle.complete(next_sequence_number.into()); - - // The primary broadcasts PrePrepare messages to all replicas - let pre_prepares_broadcast = pre_prepares.clone().broadcast_bincode(&replicas); - - // Replicas receive PrePrepare messages - let received_pre_prepares = pre_prepares_broadcast.tick_batch(); - - // Replicas verify PrePrepare messages and create Prepare messages - let prepares = received_pre_prepares - .cross_singleton(replica_id.clone()) - .map(q!(|pre_prepare, replica_id| Prepare { - view: pre_prepare.view, - sequence_number: pre_prepare.sequence_number, - digest: pre_prepare.digest.clone(), - replica_id, - })) - .broadcast_bincode(&replicas); - - // Replicas receive Prepare messages - let received_prepares = prepares.tick_batch(); - - // Collect Prepare messages - let prepare_counts = received_prepares - .map(q!(|prepare| ((prepare.view, prepare.sequence_number, prepare.digest.clone()), prepare.replica_id))) - .fold_keyed( - q!(|| HashSet::new()), - q!(|set, replica_id| { set.insert(replica_id); }), - ); - - // Check if enough Prepare messages have been collected to move to the Commit phase - let prepared_certificates = prepare_counts - .filter(q!(move |(_, set)| set.len() >= 2 * f)) - .map(q!(|(key, _)| Prepared { - view: key.0, - sequence_number: key.1, - digest: key.2.clone(), - })) - .persist(); - - // Replicas create Commit messages and broadcast them - let commits = prepared_certificates - .cross_singleton(replica_id.clone()) - .map(q!(|cert, replica_id| Commit { - view: cert.view, - sequence_number: cert.sequence_number, - digest: cert.digest.clone(), - replica_id, - })) - .broadcast_bincode(&replicas); - - // Replicas receive Commit messages - let received_commits = commits.tick_batch(); - - // Collect Commit messages - let commit_counts = received_commits - .map(q!(|commit| ((commit.view, commit.sequence_number, commit.digest.clone()), commit.replica_id))) - .fold_keyed( - q!(|| HashSet::new()), - q!(|set, replica_id| { set.insert(replica_id); }), - ); - - // Replicas execute requests after receiving enough Commit messages - let executed_requests = commit_counts - .filter(q!(move |(_, set)| set.len() >= 2 * f + 1)) - .map(q!(|(key, _)| { - println!("Replica {} executed request at view {}, seq {}", replica_id, key.0, key.1); - (key.1, key.2.clone()) - })) - .persist(); - - // Maintain the highest sequence number executed - let highest_sequence_number_stream = executed_requests - .map(q!(|(seq_num, _)| seq_num)) - .max() - .unwrap_or(flow.singleton(&replicas, q!(0u64)).latest_tick()); - - let highest_sequence_number = highest_sequence_number_stream.latest_tick(); - - // View change mechanism - - // Define a timeout to detect primary failure - let timeout_interval = flow - .source_interval(&replicas, q!(Duration::from_secs(view_timeout))) - .latest_tick(); - - // Replicas send ViewChange messages upon timeout - let view_changes = timeout_interval - .cross_singleton(current_view.clone()) - .cross_singleton(replica_id.clone()) - .cross_singleton(highest_sequence_number.clone()) - .cross_singleton(prepared_certificates.clone().collect()) - .map(q!(move |(((((), view), replica_id), last_seq_num), prepared)| ViewChange { - view: *view + 1, - replica_id, - last_sequence_number: *last_seq_num, - P: prepared.clone(), - })) - .broadcast_bincode(&replicas); - - // Replicas receive ViewChange messages - let received_view_changes = view_changes.tick_batch(); - - let view_change_counts = received_view_changes - .map(q!(|vc| (vc.view, vc))) - .fold_keyed( - q!(|| Vec::new()), - q!(|vec, vc| { vec.push(vc); }), - ); - - // Check if enough ViewChange messages have been collected to form a NewView - let new_views = view_change_counts - .filter(q!(move |(_, vec)| vec.len() >= 2 * f + 1)) - .map(q!(|(view, vcs)| NewView { - view, - V: vcs.clone(), - O: vec![], // This should be constructed based on V and P - })) - .broadcast_bincode(&replicas); - - // Replicas receive NewView messages and update the current view - let received_new_views = new_views.tick_batch(); - - let updated_view = received_new_views - .map(q!(|nv| nv.view)) - .latest_tick(); - - // Update the current view - current_view_cycle.complete(updated_view.into()); - - // Each replica determines if it is the new primary after view change - let new_is_primary = updated_view - .map(q!(move |view| (*view % (num_replicas as u32)) == replica_id)) - .latest_tick(); - - // The new primary processes any pending requests from ViewChange messages and generates new PrePrepare messages - // For simplicity, we'll assume there are no pending requests in this example - // In practice, you would reconstruct O based on the V and P collections in the NewView message - - // Benchmark code similar to Paxos implementation - - // Track throughput - - // Define a timer for statistics output - let stats_interval = flow.source_interval(&clients, q!(Duration::from_secs(1))); - - // Collect the number of executed requests - let executed_requests_count = executed_requests - .count() - .continue_unless(stats_interval.clone().latest_tick()) - .map(q!(|count| (count, false))); - - let reset_throughput = stats_interval - .clone() - .latest_tick() - .map(q!(|_| (0usize, true))) - .defer_tick(); - - let throughput = executed_requests_count - .union(reset_throughput) - .all_ticks() - .fold( - q!(|| 0usize), - q!(|(total, (count, reset))| { - if reset { - *total = 0; - } else { - *total += count; - } - }), - ); - - // Output throughput - stats_interval - .cross_singleton(throughput.clone()) - .tick_samples() - .for_each(q!(move |(_, total)| { - println!("Throughput: {} requests/s", total); - })); - - // Latency tracking - - // Create cycles to track request send and execution times - let (request_times_cycle, request_times_stream) = flow.cycle(&clients); - let request_times = request_times_stream.latest_tick(); - - // When clients send requests, record the timestamp - let client_request_times = client_requests - .map(q!(move |_| (client_id, SystemTime::now()))) - .persist(); - - request_times_cycle.complete(client_request_times.into()); - - // When replicas execute requests, record the timestamp - let execution_times = executed_requests - .map(q!(move |(seq_num, digest)| (seq_num, digest, SystemTime::now()))) - .broadcast_bincode(&clients); - - // Clients receive execution times and calculate latency - let latencies = execution_times - .cross_singleton(request_times.clone()) - .map(q!(|((seq_num, digest, exec_time), (client_id, send_time))| { - let latency = exec_time.duration_since(send_time).unwrap_or(Duration::ZERO); - latency.as_millis() - })) - .collect() - .latest_tick(); - - // Calculate average latency over the window - let average_latency = latencies - .map(q!(move |latencies_vec| { - if latencies_vec.is_empty() { - 0 - } else { - let sum: u128 = latencies_vec.iter().sum(); - (sum / latencies_vec.len() as u128) as usize - } - })); - - // Output latency - stats_interval - .cross_singleton(average_latency) - .tick_samples() - .for_each(q!(move |(_, avg_latency)| { - println!("Average Latency: {} ms", avg_latency); - })); - - ( - clients, - replicas, - ) -} From 275a0edf1fb8eba467728c24edf3a984c8eaca75 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 31 Oct 2024 11:57:04 -0700 Subject: [PATCH 12/74] fix(paxos): be more careful about which parts of proposer and acceptor have to be maintained atomically (#1488) --- hydroflow_plus/src/location.rs | 10 ++ hydroflow_plus_test/src/cluster/paxos.rs | 87 ++++----- ...cluster__paxos_bench__tests__paxos_ir.snap | 170 +++++++++--------- 3 files changed, 141 insertions(+), 126 deletions(-) diff --git a/hydroflow_plus/src/location.rs b/hydroflow_plus/src/location.rs index 9f5d9cb50aa6..66b6aee9292c 100644 --- a/hydroflow_plus/src/location.rs +++ b/hydroflow_plus/src/location.rs @@ -115,6 +115,16 @@ pub trait Location<'a> { ) } + fn singleton_each_tick( + &self, + e: impl Quoted<'a, T>, + ) -> Singleton + where + Self: Sized, + { + self.singleton(e).latest_tick() + } + fn singleton_first_tick( &self, e: impl Quoted<'a, T>, diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 94a8fb5cfa74..4d60b35e6834 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -160,7 +160,7 @@ fn leader_election<'a, P: PaxosPayload>( Optional>, Stream, Bounded, Tick, Cluster<'a, Proposer>>, Stream, Bounded, Tick, Cluster<'a, Proposer>>, - Singleton>, + Singleton>, ) { let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b) = proposers.forward_ref::>(); @@ -192,17 +192,8 @@ fn leader_election<'a, P: PaxosPayload>( ); p_to_proposers_i_am_leader_complete_cycle.complete(p_to_proposers_i_am_leader_from_others); - let a_max_ballot = p_to_acceptors_p1a - .clone() - .inspect(q!(|p1a| println!("Acceptor received P1a: {:?}", p1a))) - .map(q!(|p1a| p1a.ballot)) - .max() - .unwrap_or(acceptors.singleton(q!(Ballot { - num: 0, - proposer_id: ClusterId::from_raw(0) - }))); - - let a_to_proposers_p1b = acceptor_p1(p_to_acceptors_p1a, &a_max_ballot, a_log, proposers); + let (a_max_ballot, a_to_proposers_p1b) = + acceptor_p1(acceptors, p_to_acceptors_p1a, a_log, proposers); a_to_proposers_p1b_complete_cycle.complete(a_to_proposers_p1b.clone()); let (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) = p_p1b( @@ -254,7 +245,7 @@ fn p_ballot_calc<'a>( ) { let p_id = proposers.self_id(); let (p_ballot_num_complete_cycle, p_ballot_num) = - proposers.tick_cycle_with_initial(proposers.singleton(q!(0)).latest_tick()); + proposers.tick_cycle_with_initial(proposers.singleton_each_tick(q!(0))); let p_new_ballot_num = p_received_max_ballot .clone() @@ -302,7 +293,6 @@ fn p_leader_expired<'a>( ); p_latest_received_i_am_leader - .clone() .latest_tick() .continue_unless(p_is_leader) .filter(q!(move |latest_received_i_am_leader| { @@ -376,25 +366,42 @@ fn p_p1a<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p1<'a, P: PaxosPayload>( + acceptors: &Cluster<'a, Acceptor>, p_to_acceptors_p1a: Stream>, - a_max_ballot: &Singleton>, a_log: Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, proposers: &Cluster<'a, Proposer>, -) -> Stream, Unbounded, NoTick, Cluster<'a, Proposer>> { - p_to_acceptors_p1a - .tick_batch() - .cross_singleton(a_max_ballot.clone().latest_tick()) - .cross_singleton(a_log) - .map(q!(|((p1a, max_ballot), (_prev_checkpoint, log))| ( - p1a.ballot.proposer_id, - P1b { - ballot: p1a.ballot, - max_ballot, - accepted: log - } - ))) - .all_ticks() - .send_bincode_interleaved(proposers) +) -> ( + Singleton>, + Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, +) { + let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(); + let a_max_ballot = p_to_acceptors_p1a + .clone() + .inspect(q!(|p1a| println!("Acceptor received P1a: {:?}", p1a))) + .persist() + .map(q!(|p1a| p1a.ballot)) + .max() + .unwrap_or(acceptors.singleton_each_tick(q!(Ballot { + num: 0, + proposer_id: ClusterId::from_raw(0) + }))); + + ( + a_max_ballot.clone(), + p_to_acceptors_p1a + .cross_singleton(a_max_ballot) + .cross_singleton(a_log) + .map(q!(|((p1a, max_ballot), (_prev_checkpoint, log))| ( + p1a.ballot.proposer_id, + P1b { + ballot: p1a.ballot, + max_ballot, + accepted: log + } + ))) + .all_ticks() + .send_bincode_interleaved(proposers), + ) } // Proposer logic for processing p1bs, determining if the proposer is now the leader, which uncommitted messages to commit, what the maximum slot is in the p1bs, and which no-ops to commit to fill log holes. @@ -515,7 +522,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( p_log_holes: Stream, Bounded, Tick, Cluster<'a, Proposer>>, f: usize, - a_max_ballot: Singleton>, + a_max_ballot: Singleton>, ) -> ( Stream>, Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, @@ -597,7 +604,7 @@ fn p_p2a<'a, P: PaxosPayload>( let (p_next_slot_complete_cycle, p_next_slot) = proposers.tick_cycle::>(); let p_next_slot_after_reconciling_p1bs = p_max_slot - .unwrap_or(proposers.singleton(q!(-1)).latest_tick()) + .unwrap_or(proposers.singleton_each_tick(q!(-1))) // .inspect(q!(|max_slot| println!("{} p_max_slot: {:?}", context.current_tick(), max_slot))) .continue_unless(p_next_slot.clone()) .map(q!(|max_slot| max_slot + 1)); @@ -652,7 +659,7 @@ fn p_p2a<'a, P: PaxosPayload>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p2<'a, P: PaxosPayload, R>( - a_max_ballot: Singleton>, + a_max_ballot: Singleton>, p_to_acceptors_p2a: Stream, Unbounded, NoTick, Cluster<'a, Acceptor>>, r_to_acceptors_checkpoint: Stream< (ClusterId, i32), @@ -667,6 +674,8 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, ) { + let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(); + // Get the latest checkpoint sequence per replica let a_checkpoint_largest_seqs = r_to_acceptors_checkpoint @@ -688,15 +697,14 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( .continue_if(a_checkpoints_quorum_reached) .map(q!(|(_sender, seq)| seq)) .min() - .unwrap_or(acceptors.singleton(q!(-1)).latest_tick()) + .unwrap_or(acceptors.singleton_each_tick(q!(-1))) .delta() .map(q!(|min_seq| CheckpointOrP2a::Checkpoint(min_seq))); // .inspect(q!(|(min_seq, p2a): &(i32, P2a)| println!("Acceptor new checkpoint: {:?}", min_seq))); - let a_p2as_to_place_in_log = p_to_acceptors_p2a + let a_p2as_to_place_in_log = p_to_acceptors_p2a_batch .clone() - .tick_batch() - .cross_singleton(a_max_ballot.clone().latest_tick()) // Don't consider p2as if the current ballot is higher + .cross_singleton(a_max_ballot.clone()) // Don't consider p2as if the current ballot is higher .filter_map(q!(|(p2a, max_ballot)| if p2a.ballot >= max_ballot { Some(CheckpointOrP2a::P2a(p2a)) @@ -739,9 +747,8 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( }), ); - let a_to_proposers_p2b = p_to_acceptors_p2a - .tick_batch() - .cross_singleton(a_max_ballot.clone().latest_tick()) + let a_to_proposers_p2b = p_to_acceptors_p2a_batch + .cross_singleton(a_max_ballot) .map(q!(|(p2a, max_ballot)| ( p2a.ballot.proposer_id, P2b { diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 5614a8d634b0..3479dce1d41a 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -296,16 +296,14 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( - Tee { - inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), - input: Persist( - Tee { - inner: , - }, - ), - }, + Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), + input: Persist( + Tee { + inner: , + }, + ), }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -346,7 +344,7 @@ expression: built.ir() }, }, Tee { - inner: : Union( + inner: : Union( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( @@ -396,7 +394,7 @@ expression: built.ir() 2, ), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( FilterMap { @@ -405,7 +403,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), @@ -430,7 +428,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: : Filter { + inner: : Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), input: CrossSingleton( Tee { @@ -457,7 +455,7 @@ expression: built.ir() input: DeferTick( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( @@ -469,18 +467,18 @@ expression: built.ir() input: CrossSingleton( Union( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), input: FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { - inner: , + inner: , }, }, }, @@ -507,7 +505,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_5, }, @@ -526,15 +524,15 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads as i32 }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -582,10 +580,10 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: : Filter { + inner: : Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | num_payloads | * num_payloads > 0 }), input: Tee { - inner: , + inner: , }, }, }, @@ -594,7 +592,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -603,7 +601,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -613,7 +611,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -624,7 +622,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -636,7 +634,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -646,7 +644,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -680,18 +678,18 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), input: FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . victory { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { - inner: : Union( + inner: : Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -727,7 +725,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { victory : p2a . ballot == max_ballot , ballot : p2a . ballot , slot : p2a . slot , value : p2a . value }) }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -773,7 +771,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { inner: , @@ -787,13 +785,13 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: , + inner: , }, }, ), @@ -811,7 +809,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -824,11 +822,11 @@ expression: built.ir() CrossSingleton( Enumerate( Tee { - inner: , + inner: , }, ), Tee { - inner: , + inner: , }, ), Tee { @@ -840,7 +838,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -850,7 +848,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -874,10 +872,10 @@ expression: built.ir() }, }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -898,11 +896,11 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { - inner: , + inner: , }, }, Tee { - inner: , + inner: , }, ), }, @@ -916,7 +914,7 @@ expression: built.ir() 3, ), input: Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), input: Persist( @@ -925,10 +923,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -944,7 +942,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : ReduceKeyed { + inner: : ReduceKeyed { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), input: Persist( Network { @@ -1000,7 +998,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1035,7 +1033,7 @@ expression: built.ir() 2, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1052,10 +1050,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -1095,7 +1093,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , value) | (slot , value) }), input: AntiJoin( Tee { - inner: , + inner: , }, CycleSource { ident: Ident { @@ -1124,12 +1122,12 @@ expression: built.ir() ), }, Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | - 1 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Union( CycleSource { @@ -1166,23 +1164,23 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . key , payload . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1203,7 +1201,7 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( @@ -1232,7 +1230,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1247,7 +1245,7 @@ expression: built.ir() 1, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1260,7 +1258,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1296,7 +1294,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . value . parse :: < u32 > () . unwrap ()) , payload) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1312,13 +1310,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1339,7 +1337,7 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_2, }, @@ -1353,9 +1351,9 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: : Delta( + inner: : Delta( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { @@ -1401,7 +1399,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -1411,7 +1409,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1436,10 +1434,10 @@ expression: built.ir() }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1452,7 +1450,7 @@ expression: built.ir() input: CrossSingleton( CrossSingleton( Tee { - inner: : Source { + inner: : Source { source: Interval( { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, ), @@ -1470,10 +1468,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1481,7 +1479,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: , + inner: , }, }, ), @@ -1503,7 +1501,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1514,7 +1512,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1526,7 +1524,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1546,17 +1544,17 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot . leader_id () , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot . leader_id () , ClientPayload { key , value : c_id . raw_id . to_string () }) }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, From e7f49097dc31fb90c27ffd49e3ac00bf03aa7154 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 31 Oct 2024 12:19:14 -0700 Subject: [PATCH 13/74] refactor(paxos): minor additional cleanups (#1489) --- hydroflow_plus_test/src/cluster/paxos.rs | 12 +++++----- ...cluster__paxos_bench__tests__paxos_ir.snap | 22 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 4d60b35e6834..83f502b17e75 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -90,13 +90,14 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( let proposers = flow.cluster::(); let acceptors = flow.cluster::(); - let c_to_proposers = c_to_proposers(&proposers); - - // Proposers. proposers .source_iter(q!(["Proposers say hello"])) .for_each(q!(|s| println!("{}", s))); + acceptors + .source_iter(q!(["Acceptors say hello"])) + .for_each(q!(|s| println!("{}", s))); + let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b_forward_reference) = proposers.forward_ref::>(); let (a_log_complete_cycle, a_log_forward_reference) = @@ -114,6 +115,8 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( a_log_forward_reference, ); + let c_to_proposers = c_to_proposers(&proposers); + let (p_to_clients_new_leader_elected, p_to_replicas, a_log, a_to_proposers_p2b) = sequence_payload( &proposers, @@ -549,9 +552,6 @@ fn sequence_payload<'a, P: PaxosPayload, R>( .all_ticks(); // Acceptors. - acceptors - .source_iter(q!(["Acceptors say hello"])) - .for_each(q!(|s| println!("{}", s))); let r_to_acceptors_checkpoint = r_to_acceptors_checkpoint(acceptors); // p_to_acceptors_p2a.clone().for_each(q!(|p2a: P2a| println!("Acceptor received P2a: {:?}", p2a))); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 3479dce1d41a..0b7822435113 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -14,6 +14,17 @@ expression: built.ir() ), }, }, + ForEach { + f: stageleft :: runtime_support :: fn1_type_hint :: < & str , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | s | println ! ("{}" , s) }), + input: Source { + source: Iter( + { use crate :: __staged :: cluster :: paxos :: * ; ["Acceptors say hello"] }, + ), + location_kind: Cluster( + 3, + ), + }, + }, CycleSink { ident: Ident { sym: cycle_4, @@ -655,17 +666,6 @@ expression: built.ir() ), ), }, - ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < & str , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | s | println ! ("{}" , s) }), - input: Source { - source: Iter( - { use crate :: __staged :: cluster :: paxos :: * ; ["Acceptors say hello"] }, - ), - location_kind: Cluster( - 3, - ), - }, - }, CycleSink { ident: Ident { sym: cycle_6, From 0ba4265c7a02819ff7319d4d934e6a8ec8f1409d Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 31 Oct 2024 12:22:49 -0700 Subject: [PATCH 14/74] refactor(paxos): make leader election generic over logs (#1492) --- hydroflow_plus_test/src/cluster/paxos.rs | 205 ++++--- ...cluster__paxos_bench__tests__paxos_ir.snap | 558 +++++++++--------- 2 files changed, 393 insertions(+), 370 deletions(-) diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 83f502b17e75..8c3c89a2a1b4 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::fmt::Debug; use std::time::Duration; use hydroflow_plus::*; @@ -13,7 +14,7 @@ pub struct Proposer {} pub struct Acceptor {} pub trait PaxosPayload: - Serialize + DeserializeOwned + PartialEq + Eq + Default + Clone + std::fmt::Debug + Serialize + DeserializeOwned + PartialEq + Eq + Default + Clone + Debug { } @@ -42,10 +43,10 @@ struct LogValue

{ } #[derive(Serialize, Deserialize, Clone, Debug)] -struct P1b

{ +struct P1b { ballot: Ballot, max_ballot: Ballot, - accepted: HashMap>, + accepted: L, } #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] @@ -99,21 +100,28 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( .for_each(q!(|s| println!("{}", s))); let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b_forward_reference) = - proposers.forward_ref::>(); + proposers.forward_ref::, _, _, _>>(); let (a_log_complete_cycle, a_log_forward_reference) = - acceptors.tick_forward_ref::>(); + acceptors.tick_forward_ref::>), _, _, _>>(); - let (p_ballot_num, p_is_leader, p_max_slot, p_log_to_try_commit, p_log_holes, a_max_ballot) = - leader_election( - &proposers, - &acceptors, - f, - i_am_leader_send_timeout, - i_am_leader_check_timeout, - i_am_leader_check_timeout_delay_multiplier, - a_to_proposers_p2b_forward_reference, - a_log_forward_reference, - ); + let (p_ballot_num, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( + &proposers, + &acceptors, + f, + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + a_to_proposers_p2b_forward_reference.map(q!(|p2b| p2b.ballot)), + a_log_forward_reference.map(q!(|(_ckpnt, log)| log.clone())), + ); + + let (p_log_to_try_commit, p_max_slot, p_log_holes) = + recommit_after_leader_election(&proposers, p_relevant_p1bs, p_ballot_num.clone(), f); + + let p_log_to_recommit = p_log_to_try_commit + .union(p_log_holes) + .continue_unless(p_is_leader.clone().defer_tick()) + .continue_if(p_is_leader.clone()); // Only resend p1b stuff once the moment we become leader. let c_to_proposers = c_to_proposers(&proposers); @@ -126,13 +134,12 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( p_ballot_num, p_is_leader, p_max_slot, - p_log_to_try_commit, - p_log_holes, + p_log_to_recommit, f, a_max_ballot, ); - a_log_complete_cycle.complete(a_log.clone()); + a_log_complete_cycle.complete(a_log); a_to_proposers_p2b_complete_cycle.complete(a_to_proposers_p2b); ( @@ -148,28 +155,26 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( clippy::too_many_arguments, reason = "internal paxos code // TODO" )] -fn leader_election<'a, P: PaxosPayload>( +fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, - a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, - a_log: Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, + p_received_p2b_ballots: Stream>, + a_log: Singleton>, ) -> ( Singleton>, Optional>, - Optional>, - Stream, Bounded, Tick, Cluster<'a, Proposer>>, - Stream, Bounded, Tick, Cluster<'a, Proposer>>, + Stream, Bounded, Tick, Cluster<'a, Proposer>>, Singleton>, ) { - let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b) = - proposers.forward_ref::>(); - let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader) = + let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b_forward_ref) = + proposers.forward_ref::, _, _, _>>(); + let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader_forward_ref) = proposers.forward_ref::>(); - let (p_is_leader_complete_cycle, p_is_leader) = + let (p_is_leader_complete_cycle, p_is_leader_forward_ref) = proposers.tick_forward_ref::>(); // a_to_proposers_p2b.clone().for_each(q!(|(_, p2b): (u32, P2b)| println!("Proposer received P2b: {:?}", p2b))); // p_to_proposers_i_am_leader.clone().for_each(q!(|ballot: Ballot| println!("Proposer received I am leader: {:?}", ballot))); @@ -177,29 +182,36 @@ fn leader_election<'a, P: PaxosPayload>( let p_received_max_ballot = p_max_ballot( proposers, - a_to_proposers_p1b, - a_to_proposers_p2b, - p_to_proposers_i_am_leader.clone(), + a_to_proposers_p1b_forward_ref.map(q!(|p1a| p1a.max_ballot)), + p_received_p2b_ballots, + p_to_proposers_i_am_leader_forward_ref, ); let (p_ballot_num, p_has_largest_ballot) = p_ballot_calc(proposers, p_received_max_ballot.latest_tick()); - let (p_to_proposers_i_am_leader_from_others, p_to_acceptors_p1a) = p_p1a( - p_ballot_num.clone(), - p_is_leader.clone(), + let (p_to_proposers_i_am_leader, p_trigger_election) = p_leader_heartbeat( proposers, - acceptors, + p_is_leader_forward_ref, + p_ballot_num.clone(), i_am_leader_send_timeout, i_am_leader_check_timeout, i_am_leader_check_timeout_delay_multiplier, ); - p_to_proposers_i_am_leader_complete_cycle.complete(p_to_proposers_i_am_leader_from_others); + + p_to_proposers_i_am_leader_complete_cycle.complete(p_to_proposers_i_am_leader); + + let p_to_acceptors_p1a = p_p1a( + p_ballot_num.clone(), + p_trigger_election, + proposers, + acceptors, + ); let (a_max_ballot, a_to_proposers_p1b) = acceptor_p1(acceptors, p_to_acceptors_p1a, a_log, proposers); a_to_proposers_p1b_complete_cycle.complete(a_to_proposers_p1b.clone()); - let (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) = p_p1b( + let (p_is_leader, p_relevant_p1bs) = p_p1b( proposers, a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), p_ballot_num.clone(), @@ -208,25 +220,16 @@ fn leader_election<'a, P: PaxosPayload>( ); p_is_leader_complete_cycle.complete(p_is_leader.clone()); - ( - p_ballot_num, - p_is_leader, - p_max_slot, - p_log_to_try_commit, - p_log_holes, - a_max_ballot, - ) + (p_ballot_num, p_is_leader, p_relevant_p1bs, a_max_ballot) } // Proposer logic to calculate the largest ballot received so far. -fn p_max_ballot<'a, P: PaxosPayload>( +fn p_max_ballot<'a>( proposers: &Cluster<'a, Proposer>, - a_to_proposers_p1b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, - a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + p_received_p1b_ballots: Stream>, + p_received_p2b_ballots: Stream>, p_to_proposers_i_am_leader: Stream>, ) -> Singleton> { - let p_received_p1b_ballots = a_to_proposers_p1b.clone().map(q!(|p1b| p1b.max_ballot)); - let p_received_p2b_ballots = a_to_proposers_p2b.clone().map(q!(|p2b| p2b.ballot)); p_received_p1b_ballots .union(p_received_p2b_ballots) .union(p_to_proposers_i_am_leader) @@ -308,24 +311,22 @@ fn p_leader_expired<'a>( })) } -// Proposer logic to send "I am leader" messages periodically to other proposers, or send p1a to acceptors if other leaders expired. #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -fn p_p1a<'a>( - p_ballot_num: Singleton>, - p_is_leader: Optional>, +fn p_leader_heartbeat<'a>( proposers: &Cluster<'a, Proposer>, - acceptors: &Cluster<'a, Acceptor>, + p_is_leader: Optional>, + p_ballot_num: Singleton>, i_am_leader_send_timeout: u64, // How often to heartbeat i_am_leader_check_timeout: u64, // How often to check if heartbeat expired i_am_leader_check_timeout_delay_multiplier: usize, /* Initial delay, multiplied by proposer pid, to stagger proposers checking for timeouts */ ) -> ( Stream>, - Stream>, + Optional, Bounded, Tick, Cluster<'a, Proposer>>, ) { let p_id = proposers.self_id(); let p_to_proposers_i_am_leader = p_is_leader .clone() - .then(p_ballot_num.clone()) + .then(p_ballot_num) .latest() .sample_every(q!(Duration::from_secs(i_am_leader_send_timeout))) .map(q!(move |ballot_num| Ballot { @@ -340,21 +341,31 @@ fn p_p1a<'a>( i_am_leader_check_timeout, ); + // Add random delay depending on node ID so not everyone sends p1a at the same time + let p_trigger_election = p_leader_expired.continue_if( + proposers + .source_interval_delayed( + q!(Duration::from_secs( + (p_id.raw_id * i_am_leader_check_timeout_delay_multiplier as u32).into() + )), + q!(Duration::from_secs(i_am_leader_check_timeout)), + ) + .latest_tick(), + ); + (p_to_proposers_i_am_leader, p_trigger_election) +} + +// Proposer logic to send "I am leader" messages periodically to other proposers, or send p1a to acceptors if other leaders expired. +fn p_p1a<'a>( + p_ballot_num: Singleton>, + p_trigger_election: Optional, Bounded, Tick, Cluster<'a, Proposer>>, + proposers: &Cluster<'a, Proposer>, + acceptors: &Cluster<'a, Acceptor>, +) -> Stream> { let p_id = proposers.self_id(); - // Add random delay depending on node ID so not everyone sends p1a at the same time - let p_to_acceptors_p1a = p_leader_expired + p_trigger_election .then(p_ballot_num) - .continue_if( - proposers - .source_interval_delayed( - q!(Duration::from_secs( - (p_id.raw_id * i_am_leader_check_timeout_delay_multiplier as u32).into() - )), - q!(Duration::from_secs(i_am_leader_check_timeout)), - ) - .latest_tick(), - ) .map(q!(move |ballot_num| P1a { ballot: Ballot { num: ballot_num, @@ -363,19 +374,18 @@ fn p_p1a<'a>( })) .all_ticks() .inspect(q!(|_| println!("Proposer leader expired, sending P1a"))) - .broadcast_bincode_interleaved(acceptors); - (p_to_proposers_i_am_leader, p_to_acceptors_p1a) + .broadcast_bincode_interleaved(acceptors) } #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -fn acceptor_p1<'a, P: PaxosPayload>( +fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( acceptors: &Cluster<'a, Acceptor>, p_to_acceptors_p1a: Stream>, - a_log: Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, + a_log: Singleton>, proposers: &Cluster<'a, Proposer>, ) -> ( Singleton>, - Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, ) { let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(); let a_max_ballot = p_to_acceptors_p1a @@ -394,7 +404,7 @@ fn acceptor_p1<'a, P: PaxosPayload>( p_to_acceptors_p1a .cross_singleton(a_max_ballot) .cross_singleton(a_log) - .map(q!(|((p1a, max_ballot), (_prev_checkpoint, log))| ( + .map(q!(|((p1a, max_ballot), log)| ( p1a.ballot.proposer_id, P1b { ballot: p1a.ballot, @@ -409,7 +419,7 @@ fn acceptor_p1<'a, P: PaxosPayload>( // Proposer logic for processing p1bs, determining if the proposer is now the leader, which uncommitted messages to commit, what the maximum slot is in the p1bs, and which no-ops to commit to fill log holes. #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -fn p_p1b<'a, P: PaxosPayload>( +fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( proposers: &Cluster<'a, Proposer>, a_to_proposers_p1b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, p_ballot_num: Singleton>, @@ -417,9 +427,7 @@ fn p_p1b<'a, P: PaxosPayload>( f: usize, ) -> ( Optional>, - Stream, Bounded, Tick, Cluster<'a, Proposer>>, - Optional>, - Stream, Bounded, Tick, Cluster<'a, Proposer>>, + Stream, Bounded, Tick, Cluster<'a, Proposer>>, ) { let p_id = proposers.self_id(); let p_relevant_p1bs = a_to_proposers_p1b @@ -440,6 +448,22 @@ fn p_p1b<'a, P: PaxosPayload>( })); let p_is_leader = p_received_quorum_of_p1bs.continue_if(p_has_largest_ballot.clone()); + (p_is_leader, p_relevant_p1bs) +} + +#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] +fn recommit_after_leader_election<'a, P: PaxosPayload>( + proposers: &Cluster<'a, Proposer>, + p_relevant_p1bs: Stream>>, Bounded, Tick, Cluster<'a, Proposer>>, + p_ballot_num: Singleton>, + f: usize, +) -> ( + Stream, Bounded, Tick, Cluster<'a, Proposer>>, + Optional>, + Stream, Bounded, Tick, Cluster<'a, Proposer>>, +) { + let p_id = proposers.self_id(); + let p_p1b_highest_entries_and_count = p_relevant_p1bs .flat_map(q!(|p1b| p1b.accepted.into_iter())) // Convert HashMap log back to stream .fold_keyed(q!(|| (0, LogValue { ballot: Ballot { num: 0, proposer_id: ClusterId::from_raw(0) }, value: Default::default() })), q!(|curr_entry, new_entry| { @@ -496,7 +520,7 @@ fn p_p1b<'a, P: PaxosPayload>( slot, value: Default::default() })); - (p_is_leader, p_log_to_try_commit, p_max_slot, p_log_holes) + (p_log_to_try_commit, p_max_slot, p_log_holes) } #[expect( @@ -521,8 +545,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( p_is_leader: Optional>, p_max_slot: Optional>, - p_log_to_try_commit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, - p_log_holes: Stream, Bounded, Tick, Cluster<'a, Proposer>>, + p_log_to_recommit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, f: usize, a_max_ballot: Singleton>, @@ -538,8 +561,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( p_max_slot, c_to_proposers, p_ballot_num.clone(), - p_log_to_try_commit, - p_log_holes, + p_log_to_recommit, p_is_leader.clone(), acceptors, ); @@ -582,18 +604,13 @@ enum CheckpointOrP2a

{ } // Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. -#[expect( - clippy::type_complexity, - clippy::too_many_arguments, - reason = "internal paxos code // TODO" -)] +#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_p2a<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, p_max_slot: Optional>, c_to_proposers: Stream>, p_ballot_num: Singleton>, - p_log_to_try_commit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, - p_log_holes: Stream, Bounded, Tick, Cluster<'a, Proposer>>, + p_log_to_recommit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, p_is_leader: Optional>, acceptors: &Cluster<'a, Acceptor>, ) -> ( @@ -620,9 +637,7 @@ fn p_p2a<'a, P: PaxosPayload>( // .inspect(q!(|ballot_num| println!("{} p_indexed_payloads ballot_num: {}", context.current_tick(), ballot_num)))) .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { ballot: Ballot { num: ballot_num, proposer_id: p_id }, slot: next_slot + index as i32, value: payload })); // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); - let p_to_acceptors_p2a = p_log_to_try_commit - .union(p_log_holes) - .continue_unless(p_next_slot.clone()) // Only resend p1b stuff once. Once it's resent, next_slot will exist. + let p_to_acceptors_p2a = p_log_to_recommit .union(p_indexed_payloads) .continue_if(p_is_leader.clone()) .all_ticks() diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 0b7822435113..4c2a5f103d18 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -44,41 +44,35 @@ expression: built.ir() Union( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . max_ballot }), - input: Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_1, - }, - location_kind: Cluster( - 2, - ), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), + input: CycleSource { + ident: Ident { + sym: cycle_1, }, + location_kind: Cluster( + 2, + ), }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . ballot }), - input: Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 2, - ), + input: CycleSource { + ident: Ident { + sym: cycle_0, }, + location_kind: Cluster( + 2, + ), }, }, ), - Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_2, - }, - location_kind: Cluster( - 2, - ), + CycleSource { + ident: Ident { + sym: cycle_2, }, + location_kind: Cluster( + 2, + ), }, ), ), @@ -96,7 +90,7 @@ expression: built.ir() ), }, Tee { - inner: : Union( + inner: : Union( CycleSource { ident: Ident { sym: cycle_4, @@ -129,7 +123,7 @@ expression: built.ir() 2, ), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -172,12 +166,12 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_3, }, @@ -216,8 +210,8 @@ expression: built.ir() 2, ), input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 3, @@ -232,7 +226,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -243,17 +237,17 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >)) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , (_prev_checkpoint , log)) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), input: CrossSingleton( CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -294,15 +288,15 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( + Tee { + inner: , + }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), @@ -312,7 +306,7 @@ expression: built.ir() acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), input: Persist( Tee { - inner: , + inner: , }, ), }, @@ -324,7 +318,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -332,17 +326,17 @@ expression: built.ir() ), }, }, - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Source { - source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, - ), - location_kind: Cluster( - 2, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Source { + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, + ), + location_kind: Cluster( + 2, + ), + }, + }, ), }, }, @@ -355,7 +349,7 @@ expression: built.ir() }, }, Tee { - inner: : Union( + inner: : Union( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( @@ -364,7 +358,7 @@ expression: built.ir() input: Inspect { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), input: Tee { - inner: , + inner: , }, }, }, @@ -383,13 +377,16 @@ expression: built.ir() ), }, ), - CycleSource { - ident: Ident { - sym: cycle_0, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), + input: CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 3, + ), }, - location_kind: Cluster( - 3, - ), }, ), }, @@ -405,30 +402,30 @@ expression: built.ir() 2, ), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), input: CrossSingleton( Persist( Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), input: Tee { - inner: , + inner: , }, }, ), Tee { - inner: , + inner: , }, ), }, @@ -439,14 +436,14 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: : Filter { + inner: : Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), input: CrossSingleton( Tee { inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -466,7 +463,7 @@ expression: built.ir() input: DeferTick( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( @@ -478,18 +475,18 @@ expression: built.ir() input: CrossSingleton( Union( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { - inner: , + inner: , }, }, }, @@ -516,7 +513,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_5, }, @@ -535,15 +532,15 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads as i32 }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -591,10 +588,10 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: : Filter { + inner: : Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | num_payloads | * num_payloads > 0 }), input: Tee { - inner: , + inner: , }, }, }, @@ -603,7 +600,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -612,7 +609,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -622,7 +619,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -633,7 +630,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -645,7 +642,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -655,7 +652,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -678,18 +675,18 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), input: FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . victory { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { - inner: : Union( + inner: : Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -725,7 +722,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { victory : p2a . ballot == max_ballot , ballot : p2a . ballot , slot : p2a . slot , value : p2a . value }) }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -766,52 +763,65 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( - Union( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), - input: CrossSingleton( - Difference( - FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), - input: Tee { - inner: , + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Union( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), + input: CrossSingleton( + Tee { + inner: , }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), - input: Tee { - inner: , + Tee { + inner: , }, - }, - ), - Tee { - inner: , + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), + input: CrossSingleton( + Difference( + FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), + input: Tee { + inner: , + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: , + }, + }, + ), + Tee { + inner: , + }, + ), }, ), - }, - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: stream :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: stream :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: DeferTick( + Tee { + inner: , + }, + ), + }, }, }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Tee { + inner: , }, }, ), @@ -822,15 +832,15 @@ expression: built.ir() CrossSingleton( Enumerate( Tee { - inner: , + inner: , }, ), Tee { - inner: , + inner: , }, ), Tee { - inner: , + inner: , }, ), }, @@ -838,7 +848,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -848,7 +858,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -872,10 +882,10 @@ expression: built.ir() }, }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -896,11 +906,11 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { - inner: , + inner: , }, }, Tee { - inner: , + inner: , }, ), }, @@ -913,116 +923,114 @@ expression: built.ir() location_kind: Cluster( 3, ), - input: Tee { - inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), - input: Persist( - Union( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), - input: Delta( - Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), + input: Persist( + Union( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), + input: Delta( + Union( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: : ReduceKeyed { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), - input: Persist( - Network { - from_location: Cluster( - 1, - ), - from_key: None, - to_location: Cluster( - 3, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , i32) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < i32 > (& data) . unwrap () . into ()) }", - ], - }, - ), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : ReduceKeyed { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), + input: Persist( + Network { + from_location: Cluster( + 1, + ), + from_key: None, + to_location: Cluster( + 3, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , i32) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < i32 > (& data) . unwrap () . into ()) }", + ], + }, ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", - ], - }, - ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", + ], + }, ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 1, - ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: CycleSource { + ident: Ident { + sym: cycle_0, }, + location_kind: Cluster( + 1, + ), }, }, - ), - }, + }, + ), }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , }, }, }, - ), - }, - }, - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, - ), - location_kind: Cluster( - 3, + }, ), }, - ), + }, + }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, + ), + location_kind: Cluster( + 3, + ), + }, ), ), - }, - ), + ), + }, ), - }, + ), }, }, CycleSink { @@ -1033,7 +1041,7 @@ expression: built.ir() 2, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1050,10 +1058,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -1093,7 +1101,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , value) | (slot , value) }), input: AntiJoin( Tee { - inner: , + inner: , }, CycleSource { ident: Ident { @@ -1122,12 +1130,12 @@ expression: built.ir() ), }, Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | - 1 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Union( CycleSource { @@ -1164,23 +1172,23 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . key , payload . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1201,7 +1209,7 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( @@ -1230,7 +1238,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1245,7 +1253,7 @@ expression: built.ir() 1, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1258,7 +1266,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1294,7 +1302,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . value . parse :: < u32 > () . unwrap ()) , payload) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1310,13 +1318,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1337,7 +1345,7 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_2, }, @@ -1351,9 +1359,9 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: : Delta( + inner: : Delta( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { @@ -1399,7 +1407,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -1409,7 +1417,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1417,7 +1425,7 @@ expression: built.ir() ), }, Tee { - inner: , + inner: , }, ), }, @@ -1434,10 +1442,10 @@ expression: built.ir() }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1450,7 +1458,7 @@ expression: built.ir() input: CrossSingleton( CrossSingleton( Tee { - inner: : Source { + inner: : Source { source: Interval( { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, ), @@ -1468,10 +1476,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1479,7 +1487,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: , + inner: , }, }, ), @@ -1501,7 +1509,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1512,7 +1520,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1524,7 +1532,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1544,17 +1552,17 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot . leader_id () , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot . leader_id () , ClientPayload { key , value : c_id . raw_id . to_string () }) }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, From 871dc01fa0d07571a21d2a1d9731bf71f747c8bc Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 31 Oct 2024 13:17:49 -0700 Subject: [PATCH 15/74] refactor(paxos): simplify slot computation logic (#1493) --- hydroflow_plus_test/src/cluster/paxos.rs | 43 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 442 ++++++++---------- 2 files changed, 198 insertions(+), 287 deletions(-) diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 8c3c89a2a1b4..5af3d1a62fdc 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -58,8 +58,8 @@ struct P2a

{ #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] struct P2b

{ - victory: bool, ballot: Ballot, + max_ballot: Ballot, slot: i32, value: P, } @@ -111,7 +111,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( i_am_leader_send_timeout, i_am_leader_check_timeout, i_am_leader_check_timeout_delay_multiplier, - a_to_proposers_p2b_forward_reference.map(q!(|p2b| p2b.ballot)), + a_to_proposers_p2b_forward_reference.map(q!(|p2b| p2b.max_ballot)), a_log_forward_reference.map(q!(|(_ckpnt, log)| log.clone())), ); @@ -621,14 +621,13 @@ fn p_p2a<'a, P: PaxosPayload>( let (p_next_slot_complete_cycle, p_next_slot) = proposers.tick_cycle::>(); let p_next_slot_after_reconciling_p1bs = p_max_slot - .unwrap_or(proposers.singleton_each_tick(q!(-1))) + .map(q!(|max_slot| max_slot + 1)) + .unwrap_or(proposers.singleton_each_tick(q!(0))) // .inspect(q!(|max_slot| println!("{} p_max_slot: {:?}", context.current_tick(), max_slot))) - .continue_unless(p_next_slot.clone()) - .map(q!(|max_slot| max_slot + 1)); + .continue_unless(p_next_slot.clone()); // Send p2as let p_indexed_payloads = c_to_proposers - .clone() .tick_batch() .enumerate() .cross_singleton(p_next_slot.clone()) @@ -638,36 +637,25 @@ fn p_p2a<'a, P: PaxosPayload>( .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { ballot: Ballot { num: ballot_num, proposer_id: p_id }, slot: next_slot + index as i32, value: payload })); // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); let p_to_acceptors_p2a = p_log_to_recommit - .union(p_indexed_payloads) + .union(p_indexed_payloads.clone()) .continue_if(p_is_leader.clone()) .all_ticks() .broadcast_bincode_interleaved(acceptors); - let p_num_payloads = c_to_proposers.clone().tick_batch().count(); - let p_exists_payloads = p_num_payloads - .clone() - .filter(q!(|num_payloads| *num_payloads > 0)); + let p_num_payloads = p_indexed_payloads.count(); let p_next_slot_after_sending_payloads = p_num_payloads - .continue_if(p_exists_payloads.clone()) .clone() .cross_singleton(p_next_slot.clone()) .map(q!( |(num_payloads, next_slot)| next_slot + num_payloads as i32 )); - let p_next_slot_if_no_payloads = p_next_slot.clone().continue_unless(p_exists_payloads); - let p_new_next_slot_calculated = p_next_slot_after_reconciling_p1bs + + let p_new_next_slot = p_next_slot_after_reconciling_p1bs // .inspect(q!(|slot| println!("{} p_new_next_slot_after_reconciling_p1bs: {:?}", context.current_tick(), slot))) .union(p_next_slot_after_sending_payloads) // .inspect(q!(|slot| println!("{} p_next_slot_after_sending_payloads: {:?}", context.current_tick(), slot)))) - .union(p_next_slot_if_no_payloads) - // .inspect(q!(|slot| println!("{} p_next_slot_if_no_payloads: {:?}", context.current_tick(), slot)))) .continue_if(p_is_leader.clone()); - let p_new_next_slot_default = p_is_leader // Default next slot to 0 if there haven't been any payloads at all - .clone() - .continue_unless(p_new_next_slot_calculated.clone()) - .map(q!(|_| 0)); - // .inspect(q!(|slot| println!("{} p_new_next_slot_default: {:?}", context.current_tick(), slot))); - let p_new_next_slot = p_new_next_slot_calculated.union(p_new_next_slot_default); + p_next_slot_complete_cycle.complete_next_tick(p_new_next_slot); (p_next_slot, p_to_acceptors_p2a) } @@ -767,8 +755,8 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( .map(q!(|(p2a, max_ballot)| ( p2a.ballot.proposer_id, P2b { - victory: p2a.ballot == max_ballot, ballot: p2a.ballot, + max_ballot, slot: p2a.slot, value: p2a.value } @@ -785,13 +773,10 @@ fn p_p2b<'a, P: PaxosPayload>( ) -> Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>> { let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposers.tick_cycle(); let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposers.tick_cycle(); - let p_p2b = a_to_proposers_p2b - .clone() - .tick_batch() - .union(p_persisted_p2bs); + let p_p2b = a_to_proposers_p2b.tick_batch().union(p_persisted_p2bs); let p_count_matching_p2bs = p_p2b .clone() - .filter_map(q!(|p2b| if p2b.victory { + .filter_map(q!(|p2b| if p2b.ballot == p2b.max_ballot { // Only consider p2bs where max ballot = ballot, which means that no one preempted us Some(((p2b.slot, p2b.ballot), p2b.value)) } else { @@ -800,7 +785,6 @@ fn p_p2b<'a, P: PaxosPayload>( .fold_keyed( q!(|| (0, Default::default())), q!(|accum, value| { - // TODO(shadaj): why is sender unused? should we de-dup? accum.0 += 1; accum.1 = value; }), @@ -816,7 +800,6 @@ fn p_p2b<'a, P: PaxosPayload>( let p_to_replicas = p_p2b_quorum_reached .clone() .anti_join(p_broadcasted_p2b_slots) // Only tell the replicas about committed values once - .map(q!(|(slot, value)| (slot, value))) .all_ticks(); let p_p2b_all_commit_slots = diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 4c2a5f103d18..169fe4a64112 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -55,7 +55,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -461,206 +461,152 @@ expression: built.ir() 2, ), input: DeferTick( - Union( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | max_slot + 1 }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( - Tee { - inner: : Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: : Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { - inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), - input: Tee { - inner: , - }, - }, - }, - }, - }, - }, - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, - ), - location_kind: Cluster( - 2, - ), - }, - ), - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_5, - }, - location_kind: Cluster( - 2, - ), + inner: , }, }, }, }, }, - ), - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads as i32 }), - input: CrossSingleton( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 0, - ), - from_key: None, - to_location: Cluster( - 2, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 0, - ), - }, - }, - }, - }, - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Tee { - inner: : Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | num_payloads | * num_payloads > 0 }), - input: Tee { - inner: , - }, - }, - }, - }, - ), - }, }, - Tee { - inner: , - }, - ), + }, }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e] }, + ), + location_kind: Cluster( + 2, + ), + }, + ), ), Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: : CycleSource { + ident: Ident { + sym: cycle_5, }, + location_kind: Cluster( + 2, + ), }, }, }, - ), + }, }, ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Tee { - inner: , - }, - }, - ), - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | 0 }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads as i32 }), + input: CrossSingleton( + Tee { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : payload } }), + input: CrossSingleton( + CrossSingleton( + Enumerate( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 0, + ), + from_key: None, + to_location: Cluster( + 2, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 0, + ), + }, + }, + }, + ), + Tee { + inner: , + }, + ), + Tee { + inner: , + }, + ), + }, }, }, }, - }, - ), + Tee { + inner: , + }, + ), + }, + ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Tee { + inner: , + }, }, - }, - ), + ), + }, ), }, CycleSink { @@ -675,18 +621,18 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . victory { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { - inner: : Union( + inner: : Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -719,10 +665,10 @@ expression: built.ir() ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { victory : p2a . ballot == max_ballot , ballot : p2a . ballot , slot : p2a . slot , value : p2a . value }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -771,7 +717,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { inner: , @@ -785,13 +731,13 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: , + inner: , }, }, ), @@ -826,23 +772,8 @@ expression: built.ir() }, ), }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : payload } }), - input: CrossSingleton( - CrossSingleton( - Enumerate( - Tee { - inner: , - }, - ), - Tee { - inner: , - }, - ), - Tee { - inner: , - }, - ), + Tee { + inner: , }, ), Map { @@ -882,10 +813,10 @@ expression: built.ir() }, }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -906,11 +837,11 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { - inner: , + inner: , }, }, Tee { - inner: , + inner: , }, ), }, @@ -932,7 +863,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { inner: , @@ -951,7 +882,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : ReduceKeyed { + inner: : ReduceKeyed { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), input: Persist( Network { @@ -1007,7 +938,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1041,7 +972,7 @@ expression: built.ir() 2, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1058,10 +989,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -1097,22 +1028,19 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (slot , data) | ReplicaPayload { seq : slot , key : data . key , value : data . value } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , value) | (slot , value) }), - input: AntiJoin( - Tee { - inner: , - }, - CycleSource { - ident: Ident { - sym: cycle_6, - }, - location_kind: Cluster( - 2, - ), + input: AntiJoin( + Tee { + inner: , + }, + CycleSource { + ident: Ident { + sym: cycle_6, }, - ), - }, + location_kind: Cluster( + 2, + ), + }, + ), }, }, }, @@ -1130,12 +1058,12 @@ expression: built.ir() ), }, Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | - 1 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Union( CycleSource { @@ -1172,23 +1100,23 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . key , payload . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1209,7 +1137,7 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( @@ -1238,7 +1166,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1253,7 +1181,7 @@ expression: built.ir() 1, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1266,7 +1194,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1302,7 +1230,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . value . parse :: < u32 > () . unwrap ()) , payload) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1318,13 +1246,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1345,7 +1273,7 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_2, }, @@ -1359,9 +1287,9 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: : Delta( + inner: : Delta( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { @@ -1417,7 +1345,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1442,10 +1370,10 @@ expression: built.ir() }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1458,7 +1386,7 @@ expression: built.ir() input: CrossSingleton( CrossSingleton( Tee { - inner: : Source { + inner: : Source { source: Interval( { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, ), @@ -1476,10 +1404,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1487,7 +1415,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: , + inner: , }, }, ), @@ -1509,7 +1437,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1520,7 +1448,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1532,7 +1460,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1552,17 +1480,17 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot . leader_id () , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot . leader_id () , ClientPayload { key , value : c_id . raw_id . to_string () }) }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, From 57a2b81fd4143a2ba42b0b53ee4c612db20be843 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 31 Oct 2024 15:20:39 -0700 Subject: [PATCH 16/74] refactor(paxos): extract leader election notification (#1514) --- hydroflow_plus_test/src/cluster/paxos.rs | 74 +++--- .../src/cluster/paxos_bench.rs | 25 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 221 ++++++++---------- 3 files changed, 142 insertions(+), 178 deletions(-) diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 5af3d1a62fdc..2f704830447d 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -8,8 +8,6 @@ use serde::{Deserialize, Serialize}; use stageleft::*; use tokio::time::Instant; -use super::paxos_bench::LeaderElected; - pub struct Proposer {} pub struct Acceptor {} @@ -25,12 +23,6 @@ pub struct Ballot { pub proposer_id: ClusterId, } -impl LeaderElected for Ballot { - fn leader_id(&self) -> ClusterId { - self.proposer_id - } -} - #[derive(Serialize, Deserialize, Clone, Debug)] struct P1a { ballot: Ballot, @@ -85,7 +77,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( ) -> ( Cluster<'a, Proposer>, Cluster<'a, Acceptor>, - Stream>, + Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, ) { let proposers = flow.cluster::(); @@ -115,29 +107,37 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( a_log_forward_reference.map(q!(|(_ckpnt, log)| log.clone())), ); + let just_became_leader = p_is_leader + .clone() + .continue_unless(p_is_leader.clone().defer_tick()); + + // Tell clients that leader election has completed and they can begin sending messages + let p_to_clients_new_leader_elected = just_became_leader + .clone() + .map(q!(move |_| ())) // Only tell the clients once when leader election concludes + .all_ticks(); + let (p_log_to_try_commit, p_max_slot, p_log_holes) = recommit_after_leader_election(&proposers, p_relevant_p1bs, p_ballot_num.clone(), f); let p_log_to_recommit = p_log_to_try_commit .union(p_log_holes) - .continue_unless(p_is_leader.clone().defer_tick()) - .continue_if(p_is_leader.clone()); // Only resend p1b stuff once the moment we become leader. + .continue_if(just_became_leader); // Only resend p1b stuff once the moment we become leader. let c_to_proposers = c_to_proposers(&proposers); - let (p_to_clients_new_leader_elected, p_to_replicas, a_log, a_to_proposers_p2b) = - sequence_payload( - &proposers, - &acceptors, - c_to_proposers, - r_to_acceptors_checkpoint, - p_ballot_num, - p_is_leader, - p_max_slot, - p_log_to_recommit, - f, - a_max_ballot, - ); + let (p_to_replicas, a_log, a_to_proposers_p2b) = sequence_payload( + &proposers, + &acceptors, + c_to_proposers, + r_to_acceptors_checkpoint, + p_ballot_num, + p_is_leader, + p_max_slot, + p_log_to_recommit, + f, + a_max_ballot, + ); a_log_complete_cycle.complete(a_log); a_to_proposers_p2b_complete_cycle.complete(a_to_proposers_p2b); @@ -550,13 +550,11 @@ fn sequence_payload<'a, P: PaxosPayload, R>( a_max_ballot: Singleton>, ) -> ( - Stream>, Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, ) { - let p_id = proposers.self_id(); - let (p_next_slot, p_to_acceptors_p2a) = p_p2a( + let p_to_acceptors_p2a = p_p2a( proposers, p_max_slot, c_to_proposers, @@ -566,13 +564,6 @@ fn sequence_payload<'a, P: PaxosPayload, R>( acceptors, ); - // Tell clients that leader election has completed and they can begin sending messages - let p_to_clients_new_leader_elected = p_is_leader.clone() - .continue_unless(p_next_slot) - .cross_singleton(p_ballot_num) - .map(q!(move |(_is_leader, ballot_num)| Ballot { num: ballot_num, proposer_id: p_id})) // Only tell the clients once when leader election concludes - .all_ticks(); - // Acceptors. let r_to_acceptors_checkpoint = r_to_acceptors_checkpoint(acceptors); @@ -589,12 +580,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( // End tell clients that leader election has completed let p_to_replicas = p_p2b(proposers, a_to_proposers_p2b.clone(), f); - ( - p_to_clients_new_leader_elected, - p_to_replicas, - a_log, - a_to_proposers_p2b, - ) + (p_to_replicas, a_log, a_to_proposers_p2b) } #[derive(Clone)] @@ -604,7 +590,6 @@ enum CheckpointOrP2a

{ } // Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. -#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_p2a<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, p_max_slot: Optional>, @@ -613,10 +598,7 @@ fn p_p2a<'a, P: PaxosPayload>( p_log_to_recommit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, p_is_leader: Optional>, acceptors: &Cluster<'a, Acceptor>, -) -> ( - Optional>, - Stream, Unbounded, NoTick, Cluster<'a, Acceptor>>, -) { +) -> Stream, Unbounded, NoTick, Cluster<'a, Acceptor>> { let p_id = proposers.self_id(); let (p_next_slot_complete_cycle, p_next_slot) = proposers.tick_cycle::>(); @@ -657,7 +639,7 @@ fn p_p2a<'a, P: PaxosPayload>( .continue_if(p_is_leader.clone()); p_next_slot_complete_cycle.complete_next_tick(p_new_next_slot); - (p_next_slot, p_to_acceptors_p2a) + p_to_acceptors_p2a } #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 37cfc078305d..897727de986d 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -7,11 +7,7 @@ use hydroflow_plus::*; use serde::{Deserialize, Serialize}; use stageleft::*; -use super::paxos::{paxos_core, Acceptor, Ballot, PaxosPayload, Proposer}; - -pub trait LeaderElected: Ord + Clone { - fn leader_id(&self) -> ClusterId; -} +use super::paxos::{paxos_core, Acceptor, PaxosPayload, Proposer}; pub struct Replica {} @@ -79,7 +75,9 @@ pub fn paxos_bench<'a>( c_to_proposers_complete_cycle.complete(bench_client( &clients, - p_to_clients_new_leader_elected.broadcast_bincode_interleaved(&clients), + p_to_clients_new_leader_elected + .broadcast_bincode(&clients) + .map(q!(|(leader_id, _)| leader_id)), r_new_processed_payloads.send_bincode(&clients), num_clients_per_node, median_latency_window_size, @@ -111,7 +109,7 @@ fn paxos_with_replica<'a>( ) -> ( Cluster<'a, Proposer>, Cluster<'a, Acceptor>, - Stream>, + Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, Stream<(ClusterId, ReplicaPayload), Unbounded, NoTick, Cluster<'a, Replica>>, ) { let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = @@ -251,9 +249,14 @@ pub fn replica<'a>( } // Clients. All relations for clients will be prefixed with c. All ClientPayloads will contain the virtual client number as key and the client's machine ID (to string) as value. Expects p_to_clients_leader_elected containing Ballots whenever the leader is elected, and r_to_clients_payload_applied containing ReplicaPayloads whenever a payload is committed. Outputs (leader address, ClientPayload) when a new leader is elected or when the previous payload is committed. -fn bench_client<'a, B: LeaderElected + std::fmt::Debug>( +fn bench_client<'a>( clients: &Cluster<'a, Client>, - p_to_clients_leader_elected: Stream>, + p_to_clients_leader_elected: Stream< + ClusterId, + Unbounded, + NoTick, + Cluster<'a, Client>, + >, r_to_clients_payload_applied: Stream< (ClusterId, ReplicaPayload), Unbounded, @@ -280,7 +283,7 @@ fn bench_client<'a, B: LeaderElected + std::fmt::Debug>( .clone() .flat_map(q!(move |leader_ballot| (0..num_clients_per_node).map( move |i| ( - leader_ballot.leader_id(), + leader_ballot, ClientPayload { key: i as u32, value: c_id.raw_id.to_string() @@ -320,7 +323,7 @@ fn bench_client<'a, B: LeaderElected + std::fmt::Debug>( .clone() .cross_singleton(c_max_leader_ballot.clone().latest_tick()) .map(q!(move |(key, leader_ballot)| ( - leader_ballot.leader_id(), + leader_ballot, ClientPayload { key, value: c_id.raw_id.to_string() diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 169fe4a64112..c2f43c08201f 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -709,65 +709,67 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), + Union( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), + input: CrossSingleton( + Tee { + inner: , }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), - input: CrossSingleton( - Difference( - FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), - input: Tee { - inner: , - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), - input: Tee { - inner: , - }, - }, - ), - Tee { - inner: , - }, - ), + Tee { + inner: , }, ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: stream :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: DeferTick( - Tee { - inner: , - }, - ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), + input: CrossSingleton( + Difference( + FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), + input: Tee { + inner: , + }, }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: , + }, + }, + ), + Tee { + inner: , }, - }, - ), - }, + ), + }, + ), Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), input: Tee { - inner: , + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: DeferTick( + Tee { + inner: , + }, + ), + }, + }, + }, + ), + }, }, }, ), @@ -813,7 +815,7 @@ expression: built.ir() }, }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { inner: , @@ -841,7 +843,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -882,7 +884,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : ReduceKeyed { + inner: : ReduceKeyed { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), input: Persist( Network { @@ -938,7 +940,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -989,10 +991,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Union( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -1058,12 +1060,12 @@ expression: built.ir() ), }, Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | - 1 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Union( CycleSource { @@ -1100,23 +1102,23 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . key , payload . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1137,7 +1139,7 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( @@ -1166,7 +1168,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1181,7 +1183,7 @@ expression: built.ir() 1, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1194,7 +1196,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1230,7 +1232,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . value . parse :: < u32 > () . unwrap ()) , payload) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1246,13 +1248,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1273,7 +1275,7 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_2, }, @@ -1285,17 +1287,17 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < std :: time :: SystemTime , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node = 1usize ; move | now | (0 .. num_clients_per_node) . map (move | virtual_id | (virtual_id , now)) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: : Delta( + inner: : Delta( Tee { - inner: : Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + inner: : Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , ()) , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (leader_id , _) | leader_id }), input: Network { from_location: Cluster( 2, @@ -1310,7 +1312,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: Ballot) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , ()) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < () > (& data) . unwrap () . into ()) }", ], }, ), @@ -1321,41 +1323,18 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < () > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , u32) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (_is_leader , ballot_num) | Ballot { num : ballot_num , proposer_id : p_id } }), - input: CrossSingleton( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, - }, - }, - }, - ), - }, - Tee { - inner: , - }, - ), + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use crate :: __staged :: cluster :: paxos :: * ; move | _ | () }), + input: Tee { + inner: , + }, }, }, }, @@ -1370,10 +1349,10 @@ expression: built.ir() }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1386,7 +1365,7 @@ expression: built.ir() input: CrossSingleton( CrossSingleton( Tee { - inner: : Source { + inner: : Source { source: Interval( { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, ), @@ -1404,10 +1383,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1415,7 +1394,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: , + inner: , }, }, ), @@ -1437,7 +1416,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1448,7 +1427,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1460,7 +1439,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1478,19 +1457,19 @@ expression: built.ir() ), input: Union( FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot . leader_id () , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), input: Tee { - inner: , + inner: , }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot . leader_id () , ClientPayload { key , value : c_id . raw_id . to_string () }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot , ClientPayload { key , value : c_id . raw_id . to_string () }) }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, From 38b17cd977fb6c00ddc37e7a5b30e45dba17329e Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Sat, 2 Nov 2024 17:08:40 -0700 Subject: [PATCH 17/74] refactor(paxos_bench): simplify latency calculations (#1515) --- hydroflow_plus/src/stream.rs | 7 + .../src/cluster/paxos_bench.rs | 65 +++---- ...cluster__paxos_bench__tests__paxos_ir.snap | 162 ++++++++++-------- 3 files changed, 118 insertions(+), 116 deletions(-) diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index f374d5761bb9..7c6c9cb142a3 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -176,6 +176,13 @@ impl<'a, T, W, C, N: Location<'a>> Stream { ) } + pub fn flatten(self) -> Stream + where + T: IntoIterator, + { + self.flat_map(q!(|d| d)) + } + pub fn filter bool + 'a>( self, f: impl IntoQuotedMut<'a, F>, diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 897727de986d..a1a0ec690594 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -370,6 +370,7 @@ fn bench_client<'a>( ))) .union(c_latency_reset.into_stream()) .all_ticks() + .flatten() .fold( // Create window with ring buffer using vec + wraparound index // TODO: Would be nice if I could use vec![] instead, but that doesn't work in HF+ with RuntimeData *median_latency_window_size @@ -378,31 +379,20 @@ fn bench_client<'a>( median_latency_window_size ))), 0usize, - false )), - q!(move |(latencies, write_index, has_any_value), latency| { + q!(move |(latencies, write_index), latency| { let mut latencies_mut = latencies.borrow_mut(); - if let Some(latency) = latency { - // Insert into latencies - if let Some(prev_latency) = latencies_mut.get_mut(*write_index) { - *prev_latency = latency; - } else { - latencies_mut.push(latency); - } - *has_any_value = true; - // Increment write index and wrap around - *write_index += 1; - if *write_index == median_latency_window_size { - *write_index = 0; - } + if *write_index < latencies_mut.len() { + latencies_mut[*write_index] = latency; } else { - // reset latencies - latencies_mut.clear(); - *write_index = 0; - *has_any_value = false; + latencies_mut.push(latency); } + // Increment write index and wrap around + *write_index = (*write_index + 1) % median_latency_window_size; }), - ); + ) + .map(q!(|(latencies, _)| latencies)); + let c_throughput_new_batch = c_received_quorum_payloads .clone() .count() @@ -419,37 +409,30 @@ fn bench_client<'a>( .union(c_throughput_reset) .all_ticks() .fold( - q!(|| (0, 0)), - q!(|(total, num_ticks), (batch_size, reset)| { + q!(|| 0), + q!(|total, (batch_size, reset)| { if reset { *total = 0; - *num_ticks = 0; } else { - *total += batch_size as u32; - *num_ticks += 1; + *total += batch_size; } }), ); - c_stats_output_timer - .cross_singleton(c_latencies) + c_latencies .cross_singleton(c_throughput) - .tick_samples() - .for_each(q!(move |( - (_, (latencies, _write_index, has_any_value)), - (throughput, num_ticks), - )| { + .latest_tick() + .continue_if(c_stats_output_timer.latest_tick()) + .all_ticks() + .for_each(q!(move |(latencies, throughput)| { let mut latencies_mut = latencies.borrow_mut(); - let median_latency = if has_any_value { - let (_, median, _) = - latencies_mut.select_nth_unstable(median_latency_window_size / 2); - *median - } else { - 0 - }; - println!("Median latency: {}ms", median_latency as f64 / 1000.0); + if latencies_mut.len() > 0 { + let middle_idx = latencies_mut.len() / 2; + let (_, median, _) = latencies_mut.select_nth_unstable(middle_idx); + println!("Median latency: {}ms", (*median) as f64 / 1000.0); + } + println!("Throughput: {} requests/s", throughput); - println!("Num ticks per second: {}", num_ticks); })); // End track statistics c_to_proposers diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index c2f43c08201f..f1f81755fdd1 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -1361,92 +1361,104 @@ expression: built.ir() ), }, ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((() , (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize , bool)) , (u32 , i32)) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size = 1usize ; move | ((_ , (latencies , _write_index , has_any_value)) , (throughput , num_ticks) ,) | { let mut latencies_mut = latencies . borrow_mut () ; let median_latency = if has_any_value { let (_ , median , _) = latencies_mut . select_nth_unstable (median_latency_window_size / 2) ; * median } else { 0 } ; println ! ("Median latency: {}ms" , median_latency as f64 / 1000.0) ; println ! ("Throughput: {} requests/s" , throughput) ; println ! ("Num ticks per second: {}" , num_ticks) ; } }), - input: CrossSingleton( - CrossSingleton( - Tee { - inner: : Source { - source: Interval( - { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, - ), - location_kind: Cluster( - 0, - ), - }, - }, - Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size = 1usize ; move | | (Rc :: new (RefCell :: new (Vec :: < u128 > :: with_capacity (median_latency_window_size))) , 0usize , false) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize , bool) , core :: option :: Option < u128 > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size = 1usize ; move | (latencies , write_index , has_any_value) , latency | { let mut latencies_mut = latencies . borrow_mut () ; if let Some (latency) = latency { if let Some (prev_latency) = latencies_mut . get_mut (* write_index) { * prev_latency = latency ; } else { latencies_mut . push (latency) ; } * has_any_value = true ; * write_index += 1 ; if * write_index == median_latency_window_size { * write_index = 0 ; } } else { latencies_mut . clear () ; * write_index = 0 ; * has_any_value = false ; } } }), - input: Persist( - Union( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), - input: Join( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - DeferTick( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), - input: Tee { - inner: , - }, + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; move | (latencies , throughput) | { let mut latencies_mut = latencies . borrow_mut () ; if latencies_mut . len () > 0 { let middle_idx = latencies_mut . len () / 2 ; let (_ , median , _) = latencies_mut . select_nth_unstable (middle_idx) ; println ! ("Median latency: {}ms" , (* median) as f64 / 1000.0) ; } println ! ("Throughput: {} requests/s" , throughput) ; } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , ()) , (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + CrossSingleton( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (latencies , _) | latencies }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size = 1usize ; move | | (Rc :: new (RefCell :: new (Vec :: < u128 > :: with_capacity (median_latency_window_size))) , 0usize ,) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , u128 , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size = 1usize ; move | (latencies , write_index) , latency | { let mut latencies_mut = latencies . borrow_mut () ; if * write_index < latencies_mut . len () { latencies_mut [* write_index] = latency ; } else { latencies_mut . push (latency) ; } * write_index = (* write_index + 1) % median_latency_window_size ; } }), + input: Persist( + FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < u128 > , core :: option :: Option < u128 > > ({ use hydroflow_plus :: __staged :: stream :: * ; | d | d }), + input: Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), + input: Join( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, + DeferTick( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), + input: Tee { + inner: : Source { + source: Interval( + { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, + ), + location_kind: Cluster( + 0, + ), + }, + }, + }, + ), + ), }, ), - ), - ), - }, - ), - Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (0 , 0) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , i32) , (usize , bool) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (total , num_ticks) , (batch_size , reset) | { if reset { * total = 0 ; * num_ticks = 0 ; } else { * total += batch_size as u32 ; * num_ticks += 1 ; } } }), - input: Persist( - Union( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | batch_size | (batch_size , false) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { + }, + }, + Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (usize , bool) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | total , (batch_size , reset) | { if reset { * total = 0 ; } else { * total += batch_size ; } } }), + input: Persist( + Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | batch_size | (batch_size , false) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, + }, }, }, + ), + }, + }, + DeferTick( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), + input: Tee { + inner: , }, }, ), - }, - }, - DeferTick( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), - input: Tee { - inner: , - }, - }, + ), ), - ), + }, ), - }, - ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Tee { + inner: , + }, + }, + ), + }, }, CycleSink { ident: Ident { From e5b456bdafcb80aae6039e4c90a2e60098e499bf Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Sat, 2 Nov 2024 17:18:29 -0700 Subject: [PATCH 18/74] refactor(hydroflow_plus, paxos)!: simplify intervals and split Paxos-KV into separate module (#1516) --- hydroflow_plus/src/location.rs | 20 +- hydroflow_plus_test/src/cluster/mod.rs | 1 + hydroflow_plus_test/src/cluster/paxos.rs | 50 +- .../src/cluster/paxos_bench.rs | 299 +++-------- hydroflow_plus_test/src/cluster/paxos_kv.rs | 177 +++++++ ...cluster__paxos_bench__tests__paxos_ir.snap | 486 +++++++++--------- 6 files changed, 516 insertions(+), 517 deletions(-) create mode 100644 hydroflow_plus_test/src/cluster/paxos_kv.rs diff --git a/hydroflow_plus/src/location.rs b/hydroflow_plus/src/location.rs index 66b6aee9292c..65b05334fe69 100644 --- a/hydroflow_plus/src/location.rs +++ b/hydroflow_plus/src/location.rs @@ -148,36 +148,26 @@ pub trait Location<'a> { fn source_interval( &self, interval: impl Quoted<'a, Duration> + Copy + 'a, - ) -> Optional<(), Unbounded, NoTick, Self> + ) -> Stream where Self: Sized, { - let interval = interval.splice_untyped(); - - Optional::new( - self.id(), - self.flow_state().clone(), - HfPlusNode::Persist(Box::new(HfPlusNode::Source { - source: HfPlusSource::Interval(interval.into()), - location_kind: self.id(), - })), - ) + self.source_stream(q!(tokio_stream::wrappers::IntervalStream::new( + tokio::time::interval(interval) + ))) } fn source_interval_delayed( &self, delay: impl Quoted<'a, Duration> + Copy + 'a, interval: impl Quoted<'a, Duration> + Copy + 'a, - ) -> Optional + ) -> Stream where Self: Sized, { self.source_stream(q!(tokio_stream::wrappers::IntervalStream::new( tokio::time::interval_at(tokio::time::Instant::now() + delay, interval) ))) - .tick_batch() - .first() - .latest() } fn forward_ref>( diff --git a/hydroflow_plus_test/src/cluster/mod.rs b/hydroflow_plus_test/src/cluster/mod.rs index bcc449c3195d..b4e5be9bb006 100644 --- a/hydroflow_plus_test/src/cluster/mod.rs +++ b/hydroflow_plus_test/src/cluster/mod.rs @@ -3,5 +3,6 @@ pub mod many_to_many; pub mod map_reduce; pub mod paxos; pub mod paxos_bench; +pub mod paxos_kv; pub mod simple_cluster; pub mod two_pc; diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 2f704830447d..05dcb8382f53 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -56,33 +56,29 @@ struct P2b

{ value: P, } -#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] +#[expect( + clippy::too_many_arguments, + clippy::type_complexity, + reason = "internal paxos code // TODO" +)] pub fn paxos_core<'a, P: PaxosPayload, R>( - flow: &FlowBuilder<'a>, - r_to_acceptors_checkpoint: impl FnOnce( - &Cluster<'a, Acceptor>, - ) -> Stream< + proposers: &Cluster<'a, Proposer>, + acceptors: &Cluster<'a, Acceptor>, + r_to_acceptors_checkpoint: Stream< (ClusterId, i32), Unbounded, NoTick, Cluster<'a, Acceptor>, >, - c_to_proposers: impl FnOnce( - &Cluster<'a, Proposer>, - ) -> Stream>, + c_to_proposers: Stream>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, ) -> ( - Cluster<'a, Proposer>, - Cluster<'a, Acceptor>, Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, ) { - let proposers = flow.cluster::(); - let acceptors = flow.cluster::(); - proposers .source_iter(q!(["Proposers say hello"])) .for_each(q!(|s| println!("{}", s))); @@ -97,8 +93,8 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( acceptors.tick_forward_ref::>), _, _, _>>(); let (p_ballot_num, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( - &proposers, - &acceptors, + proposers, + acceptors, f, i_am_leader_send_timeout, i_am_leader_check_timeout, @@ -118,17 +114,15 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( .all_ticks(); let (p_log_to_try_commit, p_max_slot, p_log_holes) = - recommit_after_leader_election(&proposers, p_relevant_p1bs, p_ballot_num.clone(), f); + recommit_after_leader_election(proposers, p_relevant_p1bs, p_ballot_num.clone(), f); let p_log_to_recommit = p_log_to_try_commit .union(p_log_holes) .continue_if(just_became_leader); // Only resend p1b stuff once the moment we become leader. - let c_to_proposers = c_to_proposers(&proposers); - let (p_to_replicas, a_log, a_to_proposers_p2b) = sequence_payload( - &proposers, - &acceptors, + proposers, + acceptors, c_to_proposers, r_to_acceptors_checkpoint, p_ballot_num, @@ -142,12 +136,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( a_log_complete_cycle.complete(a_log); a_to_proposers_p2b_complete_cycle.complete(a_to_proposers_p2b); - ( - proposers, - acceptors, - p_to_clients_new_leader_elected, - p_to_replicas, - ) + (p_to_clients_new_leader_elected, p_to_replicas) } #[expect( @@ -350,7 +339,8 @@ fn p_leader_heartbeat<'a>( )), q!(Duration::from_secs(i_am_leader_check_timeout)), ) - .latest_tick(), + .tick_batch() + .first(), ); (p_to_proposers_i_am_leader, p_trigger_election) } @@ -532,9 +522,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, c_to_proposers: Stream>, - r_to_acceptors_checkpoint: impl FnOnce( - &Cluster<'a, Acceptor>, - ) -> Stream< + r_to_acceptors_checkpoint: Stream< (ClusterId, i32), Unbounded, NoTick, @@ -565,8 +553,6 @@ fn sequence_payload<'a, P: PaxosPayload, R>( ); // Acceptors. - let r_to_acceptors_checkpoint = r_to_acceptors_checkpoint(acceptors); - // p_to_acceptors_p2a.clone().for_each(q!(|p2a: P2a| println!("Acceptor received P2a: {:?}", p2a))); let (a_log, a_to_proposers_p2b) = acceptor_p2( a_max_ballot.clone(), diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index a1a0ec690594..2332a176597c 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -1,43 +1,15 @@ use std::cell::RefCell; -use std::collections::HashMap; use std::rc::Rc; use std::time::{Duration, SystemTime}; use hydroflow_plus::*; -use serde::{Deserialize, Serialize}; use stageleft::*; -use super::paxos::{paxos_core, Acceptor, PaxosPayload, Proposer}; - -pub struct Replica {} +use super::paxos::{Acceptor, Proposer}; +use super::paxos_kv::{paxos_kv, KvPayload, Replica, SequencedKv}; pub struct Client {} -#[derive(Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] -pub struct ReplicaPayload { - // Note: Important that seq is the first member of the struct for sorting - pub seq: i32, - pub key: u32, - pub value: String, -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] -pub struct ClientPayload { - pub key: u32, - pub value: String, -} - -impl Default for ClientPayload { - fn default() -> Self { - Self { - key: 0, - value: "".to_string(), - } - } -} - -impl PaxosPayload for ClientPayload {} - // Important: By convention, all relations that represent booleans either have a single "true" value or nothing. // This allows us to use the continue_if_exists() and continue_if_empty() operators as if they were if (true) and if (false) statements. #[expect(clippy::too_many_arguments, reason = "internal paxos code // TODO")] @@ -56,196 +28,48 @@ pub fn paxos_bench<'a>( Cluster<'a, Client>, Cluster<'a, Replica>, ) { + let proposers = flow.cluster::(); + let acceptors = flow.cluster::(); let clients = flow.cluster::(); let replicas = flow.cluster::(); - let (c_to_proposers_complete_cycle, c_to_proposers) = clients.forward_ref(); - - let (proposers, acceptors, p_to_clients_new_leader_elected, r_new_processed_payloads) = - paxos_with_replica( - flow, - &replicas, - c_to_proposers, - f, - i_am_leader_send_timeout, - i_am_leader_check_timeout, - i_am_leader_check_timeout_delay_multiplier, - checkpoint_frequency, - ); + let (new_leader_elected_complete, new_leader_elected) = + clients.forward_ref::>(); - c_to_proposers_complete_cycle.complete(bench_client( + bench_client( &clients, - p_to_clients_new_leader_elected - .broadcast_bincode(&clients) - .map(q!(|(leader_id, _)| leader_id)), - r_new_processed_payloads.send_bincode(&clients), + new_leader_elected, + |c_to_proposers| { + let (new_leader_elected, processed_payloads) = paxos_kv( + &proposers, + &acceptors, + &replicas, + c_to_proposers.send_bincode_interleaved(&proposers), + f, + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + checkpoint_frequency, + ); + + new_leader_elected_complete.complete( + new_leader_elected + .broadcast_bincode(&clients) + .map(q!(|(leader_id, _)| leader_id)), + ); + processed_payloads + .map(q!(|payload| ( + ClusterId::from_raw(payload.kv.value.parse::().unwrap()), + payload + ))) + .send_bincode(&clients) + }, num_clients_per_node, median_latency_window_size, f, - )); - - (proposers, acceptors, clients, replicas) -} - -#[expect( - clippy::type_complexity, - clippy::too_many_arguments, - reason = "internal paxos code // TODO" -)] -fn paxos_with_replica<'a>( - flow: &FlowBuilder<'a>, - replicas: &Cluster<'a, Replica>, - c_to_proposers: Stream< - (ClusterId, ClientPayload), - Unbounded, - NoTick, - Cluster<'a, Client>, - >, - f: usize, - i_am_leader_send_timeout: u64, - i_am_leader_check_timeout: u64, - i_am_leader_check_timeout_delay_multiplier: usize, - checkpoint_frequency: usize, -) -> ( - Cluster<'a, Proposer>, - Cluster<'a, Acceptor>, - Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, - Stream<(ClusterId, ReplicaPayload), Unbounded, NoTick, Cluster<'a, Replica>>, -) { - let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = - replicas.forward_ref::>(); - - let (proposers, acceptors, p_to_clients_new_leader_elected, p_to_replicas) = paxos_core( - flow, - |acceptors| r_to_acceptors_checkpoint.broadcast_bincode(acceptors), - |proposers| c_to_proposers.send_bincode_interleaved(proposers), - f, - i_am_leader_send_timeout, - i_am_leader_check_timeout, - i_am_leader_check_timeout_delay_multiplier, - ); - - let (r_to_acceptors_checkpoint_new, r_new_processed_payloads) = replica( - replicas, - p_to_replicas - .map(q!(|(slot, data)| ReplicaPayload { - seq: slot, - key: data.key, - value: data.value - })) - .broadcast_bincode_interleaved(replicas), - checkpoint_frequency, ); - r_to_acceptors_checkpoint_complete_cycle.complete(r_to_acceptors_checkpoint_new); - - ( - proposers, - acceptors, - p_to_clients_new_leader_elected, - r_new_processed_payloads, - ) -} - -// Replicas. All relations for replicas will be prefixed with r. Expects ReplicaPayload on p_to_replicas, outputs a stream of (client address, ReplicaPayload) after processing. -#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -pub fn replica<'a>( - replicas: &Cluster<'a, Replica>, - p_to_replicas: Stream>, - checkpoint_frequency: usize, -) -> ( - Stream>, - Stream<(ClusterId, ReplicaPayload), Unbounded, NoTick, Cluster<'a, Replica>>, -) { - let (r_buffered_payloads_complete_cycle, r_buffered_payloads) = replicas.tick_cycle(); - // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); - let r_sorted_payloads = p_to_replicas - .clone() - .tick_batch() - .union(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet - .sort(); - // Create a cycle since we'll use this seq before we define it - let (r_highest_seq_complete_cycle, r_highest_seq) = - replicas.tick_cycle::>(); - let empty_slot = replicas.singleton_first_tick(q!(-1)); - // Either the max sequence number executed so far or -1. Need to union otherwise r_highest_seq is empty and joins with it will fail - let r_highest_seq_with_default = r_highest_seq.union(empty_slot); - // Find highest the sequence number of any payload that can be processed in this tick. This is the payload right before a hole. - let r_highest_seq_processable_payload = r_sorted_payloads - .clone() - .cross_singleton(r_highest_seq_with_default) - .fold( - q!(|| -1), - q!(|filled_slot, (sorted_payload, highest_seq)| { - // Note: This function only works if the input is sorted on seq. - let next_slot = std::cmp::max(*filled_slot, highest_seq); - - *filled_slot = if sorted_payload.seq == next_slot + 1 { - sorted_payload.seq - } else { - *filled_slot - }; - }), - ); - // Find all payloads that can and cannot be processed in this tick. - let r_processable_payloads = r_sorted_payloads - .clone() - .cross_singleton(r_highest_seq_processable_payload.clone()) - .filter(q!( - |(sorted_payload, highest_seq)| sorted_payload.seq <= *highest_seq - )) - .map(q!(|(sorted_payload, _)| { sorted_payload })); - let r_new_non_processable_payloads = r_sorted_payloads - .clone() - .cross_singleton(r_highest_seq_processable_payload.clone()) - .filter(q!( - |(sorted_payload, highest_seq)| sorted_payload.seq > *highest_seq - )) - .map(q!(|(sorted_payload, _)| { sorted_payload })); - // Save these, we can process them once the hole has been filled - r_buffered_payloads_complete_cycle.complete_next_tick(r_new_non_processable_payloads); - - let r_kv_store = r_processable_payloads - .clone() - .persist() // Optimization: all_ticks() + fold() = fold, where the state of the previous fold is saved and persisted values are deleted. - .fold(q!(|| (HashMap::::new(), -1)), q!(|state, payload| { - let kv_store = &mut state.0; - let last_seq = &mut state.1; - kv_store.insert(payload.key, payload.value); - debug_assert!(payload.seq == *last_seq + 1, "Hole in log between seq {} and {}", *last_seq, payload.seq); - *last_seq = payload.seq; - // println!("Replica kv store: {:?}", kv_store); - })); - // Update the highest seq for the next tick - let r_new_highest_seq = r_kv_store.map(q!(|(_kv_store, highest_seq)| highest_seq)); - r_highest_seq_complete_cycle.complete_next_tick(r_new_highest_seq.clone().into()); - - // Send checkpoints to the acceptors when we've processed enough payloads - let (r_checkpointed_seqs_complete_cycle, r_checkpointed_seqs) = - replicas.tick_cycle::>(); - let r_max_checkpointed_seq = r_checkpointed_seqs - .persist() - .max() - .unwrap_or(replicas.singleton(q!(-1)).latest_tick()); - let r_checkpoint_seq_new = r_max_checkpointed_seq - .cross_singleton(r_new_highest_seq) - .filter_map(q!( - move |(max_checkpointed_seq, new_highest_seq)| if new_highest_seq - max_checkpointed_seq - >= checkpoint_frequency as i32 - { - Some(new_highest_seq) - } else { - None - } - )); - r_checkpointed_seqs_complete_cycle.complete_next_tick(r_checkpoint_seq_new.clone()); - - // Tell clients that the payload has been committed. All ReplicaPayloads contain the client's machine ID (to string) as value. - let r_to_clients = p_to_replicas.map(q!(|payload| ( - ClusterId::from_raw(payload.value.parse::().unwrap()), - payload - ))); - (r_checkpoint_seq_new.all_ticks(), r_to_clients) + (proposers, acceptors, clients, replicas) } // Clients. All relations for clients will be prefixed with c. All ClientPayloads will contain the virtual client number as key and the client's machine ID (to string) as value. Expects p_to_clients_leader_elected containing Ballots whenever the leader is elected, and r_to_clients_payload_applied containing ReplicaPayloads whenever a payload is committed. Outputs (leader address, ClientPayload) when a new leader is elected or when the previous payload is committed. @@ -257,8 +81,10 @@ fn bench_client<'a>( NoTick, Cluster<'a, Client>, >, - r_to_clients_payload_applied: Stream< - (ClusterId, ReplicaPayload), + transaction_cycle: impl FnOnce( + Stream<(ClusterId, KvPayload), Unbounded, NoTick, Cluster<'a, Client>>, + ) -> Stream< + (ClusterId, SequencedKv), Unbounded, NoTick, Cluster<'a, Client>, @@ -266,17 +92,17 @@ fn bench_client<'a>( num_clients_per_node: usize, median_latency_window_size: usize, f: usize, -) -> Stream<(ClusterId, ClientPayload), Unbounded, NoTick, Cluster<'a, Client>> { +) { let c_id = clients.self_id(); // r_to_clients_payload_applied.clone().inspect(q!(|payload: &(u32, ReplicaPayload)| println!("Client received payload: {:?}", payload))); // Only keep the latest leader - let c_max_leader_ballot = p_to_clients_leader_elected + let current_leader = p_to_clients_leader_elected .inspect(q!(|ballot| println!( "Client notified that leader was elected: {:?}", ballot ))) .max(); - let c_new_leader_ballot = c_max_leader_ballot.clone().latest_tick().delta(); + let c_new_leader_ballot = current_leader.clone().latest_tick().delta(); // Whenever the leader changes, make all clients send a message let c_new_payloads_when_leader_elected = c_new_leader_ballot @@ -284,19 +110,23 @@ fn bench_client<'a>( .flat_map(q!(move |leader_ballot| (0..num_clients_per_node).map( move |i| ( leader_ballot, - ClientPayload { + KvPayload { key: i as u32, value: c_id.raw_id.to_string() } ) ))); + + let (c_to_proposers_complete_cycle, c_to_proposers) = clients.forward_ref(); + let transaction_results = transaction_cycle(c_to_proposers); + // Whenever replicas confirm that a payload was committed, collected it and wait for a quorum let (c_pending_quorum_payloads_complete_cycle, c_pending_quorum_payloads) = clients.tick_cycle(); - let c_received_payloads = r_to_clients_payload_applied + let c_received_payloads = transaction_results .tick_batch() .map(q!(|(sender, replica_payload)| ( - replica_payload.key, + replica_payload.kv.key, sender ))) .union(c_pending_quorum_payloads); @@ -321,17 +151,19 @@ fn bench_client<'a>( // Whenever all replicas confirm that a payload was committed, send another payload let c_new_payloads_when_committed = c_received_quorum_payloads .clone() - .cross_singleton(c_max_leader_ballot.clone().latest_tick()) - .map(q!(move |(key, leader_ballot)| ( - leader_ballot, - ClientPayload { + .cross_singleton(current_leader.clone().latest_tick()) + .map(q!(move |(key, cur_leader)| ( + cur_leader, + KvPayload { key, value: c_id.raw_id.to_string() } ))); - let c_to_proposers = c_new_payloads_when_leader_elected - .union(c_new_payloads_when_committed) - .all_ticks(); + c_to_proposers_complete_cycle.complete( + c_new_payloads_when_leader_elected + .union(c_new_payloads_when_committed) + .all_ticks(), + ); // Track statistics let (c_timers_complete_cycle, c_timers) = @@ -355,13 +187,12 @@ fn bench_client<'a>( })); c_timers_complete_cycle.complete_next_tick(c_new_timers); - let c_stats_output_timer = clients.source_interval(q!(Duration::from_secs(1))); + let c_stats_output_timer = clients + .source_interval(q!(Duration::from_secs(1))) + .tick_batch() + .first(); - let c_latency_reset = c_stats_output_timer - .clone() - .latest_tick() - .map(q!(|_| None)) - .defer_tick(); + let c_latency_reset = c_stats_output_timer.clone().map(q!(|_| None)).defer_tick(); let c_latencies = c_timers .join(c_updated_timers) @@ -396,12 +227,11 @@ fn bench_client<'a>( let c_throughput_new_batch = c_received_quorum_payloads .clone() .count() - .continue_unless(c_stats_output_timer.clone().latest_tick()) + .continue_unless(c_stats_output_timer.clone()) .map(q!(|batch_size| (batch_size, false))); let c_throughput_reset = c_stats_output_timer .clone() - .latest_tick() .map(q!(|_| (0, true))) .defer_tick(); @@ -422,7 +252,7 @@ fn bench_client<'a>( c_latencies .cross_singleton(c_throughput) .latest_tick() - .continue_if(c_stats_output_timer.latest_tick()) + .continue_if(c_stats_output_timer) .all_ticks() .for_each(q!(move |(latencies, throughput)| { let mut latencies_mut = latencies.borrow_mut(); @@ -435,7 +265,6 @@ fn bench_client<'a>( println!("Throughput: {} requests/s", throughput); })); // End track statistics - c_to_proposers } #[cfg(test)] diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs new file mode 100644 index 000000000000..ba9a40e73ef4 --- /dev/null +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -0,0 +1,177 @@ +use std::collections::HashMap; + +use hydroflow_plus::*; +use serde::{Deserialize, Serialize}; +use stageleft::*; + +use super::paxos::{paxos_core, Acceptor, PaxosPayload, Proposer}; + +pub struct Replica {} + +#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub struct KvPayload { + pub key: u32, + pub value: String, +} + +impl Default for KvPayload { + fn default() -> Self { + Self { + key: 0, + value: "".to_string(), + } + } +} + +impl PaxosPayload for KvPayload {} + +#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub struct SequencedKv { + // Note: Important that seq is the first member of the struct for sorting + pub seq: i32, + pub kv: KvPayload, +} + +#[expect( + clippy::type_complexity, + clippy::too_many_arguments, + reason = "internal paxos code // TODO" +)] +pub fn paxos_kv<'a>( + proposers: &Cluster<'a, Proposer>, + acceptors: &Cluster<'a, Acceptor>, + replicas: &Cluster<'a, Replica>, + c_to_proposers: Stream>, + f: usize, + i_am_leader_send_timeout: u64, + i_am_leader_check_timeout: u64, + i_am_leader_check_timeout_delay_multiplier: usize, + checkpoint_frequency: usize, +) -> ( + Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, + Stream>, +) { + let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = + replicas.forward_ref::>(); + + let (p_to_clients_new_leader_elected, p_to_replicas) = paxos_core( + proposers, + acceptors, + r_to_acceptors_checkpoint.broadcast_bincode(acceptors), + c_to_proposers, + f, + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + ); + + let (r_to_acceptors_checkpoint_new, r_new_processed_payloads) = replica( + replicas, + p_to_replicas + .map(q!(|(slot, kv)| SequencedKv { seq: slot, kv })) + .broadcast_bincode_interleaved(replicas), + checkpoint_frequency, + ); + + r_to_acceptors_checkpoint_complete_cycle.complete(r_to_acceptors_checkpoint_new); + + (p_to_clients_new_leader_elected, r_new_processed_payloads) +} + +// Replicas. All relations for replicas will be prefixed with r. Expects ReplicaPayload on p_to_replicas, outputs a stream of (client address, ReplicaPayload) after processing. +#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] +pub fn replica<'a>( + replicas: &Cluster<'a, Replica>, + p_to_replicas: Stream>, + checkpoint_frequency: usize, +) -> ( + Stream>, + Stream>, +) { + let (r_buffered_payloads_complete_cycle, r_buffered_payloads) = replicas.tick_cycle(); + // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); + let r_sorted_payloads = p_to_replicas + .clone() + .tick_batch() + .union(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet + .sort(); + // Create a cycle since we'll use this seq before we define it + let (r_highest_seq_complete_cycle, r_highest_seq) = + replicas.tick_cycle::>(); + let empty_slot = replicas.singleton_first_tick(q!(-1)); + // Either the max sequence number executed so far or -1. Need to union otherwise r_highest_seq is empty and joins with it will fail + let r_highest_seq_with_default = r_highest_seq.union(empty_slot); + // Find highest the sequence number of any payload that can be processed in this tick. This is the payload right before a hole. + let r_highest_seq_processable_payload = r_sorted_payloads + .clone() + .cross_singleton(r_highest_seq_with_default) + .fold( + q!(|| -1), + q!(|filled_slot, (sorted_payload, highest_seq)| { + // Note: This function only works if the input is sorted on seq. + let next_slot = std::cmp::max(*filled_slot, highest_seq); + + *filled_slot = if sorted_payload.seq == next_slot + 1 { + sorted_payload.seq + } else { + *filled_slot + }; + }), + ); + // Find all payloads that can and cannot be processed in this tick. + let r_processable_payloads = r_sorted_payloads + .clone() + .cross_singleton(r_highest_seq_processable_payload.clone()) + .filter(q!( + |(sorted_payload, highest_seq)| sorted_payload.seq <= *highest_seq + )) + .map(q!(|(sorted_payload, _)| { sorted_payload })); + let r_new_non_processable_payloads = r_sorted_payloads + .clone() + .cross_singleton(r_highest_seq_processable_payload.clone()) + .filter(q!( + |(sorted_payload, highest_seq)| sorted_payload.seq > *highest_seq + )) + .map(q!(|(sorted_payload, _)| { sorted_payload })); + // Save these, we can process them once the hole has been filled + r_buffered_payloads_complete_cycle.complete_next_tick(r_new_non_processable_payloads); + + let r_kv_store = r_processable_payloads + .clone() + .persist() // Optimization: all_ticks() + fold() = fold, where the state of the previous fold is saved and persisted values are deleted. + .fold(q!(|| (HashMap::::new(), -1)), q!(|state, payload| { + let kv_store = &mut state.0; + let last_seq = &mut state.1; + kv_store.insert(payload.kv.key, payload.kv.value); + debug_assert!(payload.seq == *last_seq + 1, "Hole in log between seq {} and {}", *last_seq, payload.seq); + *last_seq = payload.seq; + // println!("Replica kv store: {:?}", kv_store); + })); + // Update the highest seq for the next tick + let r_new_highest_seq = r_kv_store.map(q!(|(_kv_store, highest_seq)| highest_seq)); + r_highest_seq_complete_cycle.complete_next_tick(r_new_highest_seq.clone().into()); + + // Send checkpoints to the acceptors when we've processed enough payloads + let (r_checkpointed_seqs_complete_cycle, r_checkpointed_seqs) = + replicas.tick_cycle::>(); + let r_max_checkpointed_seq = r_checkpointed_seqs + .persist() + .max() + .unwrap_or(replicas.singleton(q!(-1)).latest_tick()); + let r_checkpoint_seq_new = r_max_checkpointed_seq + .cross_singleton(r_new_highest_seq) + .filter_map(q!( + move |(max_checkpointed_seq, new_highest_seq)| if new_highest_seq - max_checkpointed_seq + >= checkpoint_frequency as i32 + { + Some(new_highest_seq) + } else { + None + } + )); + r_checkpointed_seqs_complete_cycle.complete_next_tick(r_checkpoint_seq_new.clone()); + + // Tell clients that the payload has been committed. All ReplicaPayloads contain the client's machine ID (to string) as value. + let r_to_clients = p_to_replicas; + (r_checkpoint_seq_new.all_ticks(), r_to_clients) +} diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index f1f81755fdd1..6103176aa6d9 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -10,7 +10,7 @@ expression: built.ir() { use crate :: __staged :: cluster :: paxos :: * ; ["Proposers say hello"] }, ), location_kind: Cluster( - 2, + 0, ), }, }, @@ -21,7 +21,7 @@ expression: built.ir() { use crate :: __staged :: cluster :: paxos :: * ; ["Acceptors say hello"] }, ), location_kind: Cluster( - 3, + 1, ), }, }, @@ -30,11 +30,11 @@ expression: built.ir() sym: cycle_4, }, location_kind: Cluster( - 2, + 0, ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , proposer_id : p_id , }) { received_max_ballot . num + 1 } else { ballot_num } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , proposer_id : p_id , }) { received_max_ballot . num + 1 } else { ballot_num } } }), input: CrossSingleton( Tee { inner: : Union( @@ -44,24 +44,24 @@ expression: built.ir() Union( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_1, }, location_kind: Cluster( - 2, + 0, ), }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_0, }, location_kind: Cluster( - 2, + 0, ), }, }, @@ -71,7 +71,7 @@ expression: built.ir() sym: cycle_2, }, location_kind: Cluster( - 2, + 0, ), }, ), @@ -83,7 +83,7 @@ expression: built.ir() { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, ), location_kind: Cluster( - 2, + 0, ), }, ), @@ -96,7 +96,7 @@ expression: built.ir() sym: cycle_4, }, location_kind: Cluster( - 2, + 0, ), }, Persist( @@ -105,7 +105,7 @@ expression: built.ir() { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e] }, ), location_kind: Cluster( - 2, + 0, ), }, ), @@ -120,18 +120,18 @@ expression: built.ir() sym: cycle_2, }, location_kind: Cluster( - 2, + 0, ), input: Tee { inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( - 2, + 0, ), from_key: None, to_location: Cluster( - 2, + 0, ), to_key: None, serialize_pipeline: Some( @@ -156,9 +156,9 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | Ballot { num : ballot_num , proposer_id : p_id } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ballot_num | Ballot { num : ballot_num , proposer_id : p_id } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( @@ -176,7 +176,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Cluster( - 2, + 0, ), }, }, @@ -190,7 +190,7 @@ expression: built.ir() stageleft :: runtime_support :: type_hint :: < core :: time :: Duration > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) }), ), location_kind: Cluster( - 2, + 0, ), }, }, @@ -207,18 +207,18 @@ expression: built.ir() sym: cycle_1, }, location_kind: Cluster( - 2, + 0, ), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( - 3, + 1, ), from_key: None, to_location: Cluster( - 2, + 0, ), to_key: None, serialize_pipeline: Some( @@ -226,7 +226,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -237,13 +237,13 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), input: CrossSingleton( CrossSingleton( Tee { @@ -251,11 +251,11 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( - 2, + 0, ), from_key: None, to_location: Cluster( - 3, + 1, ), to_key: None, serialize_pipeline: Some( @@ -280,11 +280,11 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Inspect { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , proposer_id : p_id } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , proposer_id : p_id } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( @@ -330,10 +330,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Source { source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, + { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, ), location_kind: Cluster( - 2, + 0, ), }, }, @@ -370,7 +370,7 @@ expression: built.ir() { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, ), location_kind: Cluster( - 3, + 1, ), }, ), @@ -378,13 +378,13 @@ expression: built.ir() }, ), Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > >) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), input: CycleSource { ident: Ident { sym: cycle_0, }, location_kind: Cluster( - 3, + 1, ), }, }, @@ -399,7 +399,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Cluster( - 2, + 0, ), input: Tee { inner: : Map { @@ -409,16 +409,16 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), input: CrossSingleton( Persist( Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), input: Tee { inner: , }, @@ -437,7 +437,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { inner: : Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), input: CrossSingleton( Tee { inner: , @@ -458,7 +458,7 @@ expression: built.ir() sym: cycle_5, }, location_kind: Cluster( - 2, + 0, ), input: DeferTick( Map { @@ -475,13 +475,13 @@ expression: built.ir() inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { inner: , }, @@ -498,7 +498,7 @@ expression: built.ir() { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e] }, ), location_kind: Cluster( - 2, + 0, ), }, ), @@ -516,7 +516,7 @@ expression: built.ir() sym: cycle_5, }, location_kind: Cluster( - 2, + 0, ), }, }, @@ -531,22 +531,22 @@ expression: built.ir() Tee { inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : payload } }), input: CrossSingleton( CrossSingleton( Enumerate( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( - 0, + 2, ), from_key: None, to_location: Cluster( - 2, + 0, ), to_key: None, serialize_pipeline: Some( @@ -554,7 +554,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > (& data) . unwrap () . into ()) }", ], }, ), @@ -565,17 +565,17 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > (& b) . unwrap ()) }", ], }, ), ), input: CycleSource { ident: Ident { - sym: cycle_0, + sym: cycle_1, }, location_kind: Cluster( - 0, + 2, ), }, }, @@ -614,33 +614,33 @@ expression: built.ir() sym: cycle_6, }, location_kind: Cluster( - 2, + 0, ), input: DeferTick( Difference( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { inner: : Union( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( - 3, + 1, ), from_key: None, to_location: Cluster( - 2, + 0, ), to_key: None, serialize_pipeline: Some( @@ -648,7 +648,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > (& data) . unwrap () . into ()) }", ], }, ), @@ -659,24 +659,24 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), input: CrossSingleton( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( - 2, + 0, ), from_key: None, to_location: Cluster( - 3, + 1, ), to_key: None, serialize_pipeline: Some( @@ -684,7 +684,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > (& data) . unwrap () . into ()) }", ], }, ), @@ -695,23 +695,23 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), input: CrossSingleton( Tee { inner: , @@ -722,7 +722,7 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), input: CrossSingleton( Difference( FlatMap { @@ -732,7 +732,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: , }, @@ -803,7 +803,7 @@ expression: built.ir() sym: cycle_7, }, location_kind: Cluster( - 2, + 0, ), }, ), @@ -816,7 +816,7 @@ expression: built.ir() }, Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { inner: , }, @@ -830,14 +830,14 @@ expression: built.ir() sym: cycle_7, }, location_kind: Cluster( - 2, + 0, ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), input: AntiJoin( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { inner: , }, @@ -854,15 +854,15 @@ expression: built.ir() sym: cycle_0, }, location_kind: Cluster( - 3, + 1, ), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), input: Persist( Union( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( Tee { inner: , @@ -873,15 +873,15 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), input: Delta( Union( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { inner: : ReduceKeyed { @@ -889,11 +889,11 @@ expression: built.ir() input: Persist( Network { from_location: Cluster( - 1, + 3, ), from_key: None, to_location: Cluster( - 3, + 1, ), to_key: None, serialize_pipeline: Some( @@ -912,19 +912,19 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: CycleSource { ident: Ident { sym: cycle_0, }, location_kind: Cluster( - 1, + 3, ), }, }, @@ -938,7 +938,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: , }, @@ -955,7 +955,7 @@ expression: built.ir() { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, ), location_kind: Cluster( - 3, + 1, ), }, ), @@ -971,7 +971,7 @@ expression: built.ir() sym: cycle_0, }, location_kind: Cluster( - 2, + 0, ), input: Tee { inner: , @@ -982,27 +982,27 @@ expression: built.ir() sym: cycle_1, }, location_kind: Cluster( - 1, + 3, ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { inner: : Sort( Union( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( - 2, + 0, ), from_key: None, to_location: Cluster( - 1, + 3, ), to_key: None, serialize_pipeline: Some( @@ -1010,7 +1010,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > (& data) . unwrap () . into ()) }", ], }, ), @@ -1021,15 +1021,15 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (slot , data) | ReplicaPayload { seq : slot , key : data . key , value : data . value } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), input: AntiJoin( Tee { inner: , @@ -1039,7 +1039,7 @@ expression: built.ir() sym: cycle_6, }, location_kind: Cluster( - 2, + 0, ), }, ), @@ -1053,7 +1053,7 @@ expression: built.ir() sym: cycle_1, }, location_kind: Cluster( - 1, + 3, ), }, ), @@ -1061,8 +1061,8 @@ expression: built.ir() }, Tee { inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | - 1 }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | - 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { inner: , @@ -1073,15 +1073,15 @@ expression: built.ir() sym: cycle_2, }, location_kind: Cluster( - 1, + 3, ), }, Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos_bench :: * ; - 1 } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos_kv :: * ; - 1 } ; [e] }, ), location_kind: Cluster( - 1, + 3, ), }, ), @@ -1098,21 +1098,21 @@ expression: built.ir() sym: cycle_2, }, location_kind: Cluster( - 1, + 3, ), input: DeferTick( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_kv_store , highest_seq) | highest_seq }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . key , payload . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . kv . key , payload . kv . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { inner: , @@ -1135,12 +1135,12 @@ expression: built.ir() sym: cycle_3, }, location_kind: Cluster( - 1, + 3, ), input: DeferTick( Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( Reduce { @@ -1151,7 +1151,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Cluster( - 1, + 3, ), }, ), @@ -1159,10 +1159,10 @@ expression: built.ir() Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos_bench :: * ; - 1 } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos_kv :: * ; - 1 } ; [e] }, ), location_kind: Cluster( - 1, + 3, ), }, ), @@ -1180,7 +1180,7 @@ expression: built.ir() sym: cycle_0, }, location_kind: Cluster( - 1, + 3, ), input: Tee { inner: , @@ -1188,24 +1188,75 @@ expression: built.ir() }, CycleSink { ident: Ident { - sym: cycle_1, + sym: cycle_0, }, location_kind: Cluster( - 0, + 2, + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , ()) , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (leader_id , _) | leader_id }), + input: Network { + from_location: Cluster( + 0, + ), + from_key: None, + to_location: Cluster( + 2, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , ()) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < () > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < () > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use crate :: __staged :: cluster :: paxos :: * ; move | _ | () }), + input: Tee { + inner: , + }, + }, + }, + }, + }, + }, + CycleSink { + ident: Ident { + sym: cycle_2, + }, + location_kind: Cluster( + 2, ), input: DeferTick( AntiJoin( Tee { inner: : Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . kv . key , sender) }), input: Network { from_location: Cluster( - 1, + 3, ), from_key: None, to_location: Cluster( - 0, + 2, ), to_key: None, serialize_pipeline: Some( @@ -1213,7 +1264,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > (& data) . unwrap () . into ()) }", ], }, ), @@ -1224,13 +1275,13 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_bench :: ReplicaPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . value . parse :: < u32 > () . unwrap ()) , payload) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . kv . value . parse :: < u32 > () . unwrap ()) , payload) }), input: Tee { inner: , }, @@ -1239,10 +1290,10 @@ expression: built.ir() }, CycleSource { ident: Ident { - sym: cycle_1, + sym: cycle_2, }, location_kind: Cluster( - 0, + 2, ), }, ), @@ -1252,7 +1303,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { inner: , }, @@ -1264,10 +1315,56 @@ expression: built.ir() }, CycleSink { ident: Ident { - sym: cycle_2, + sym: cycle_1, }, location_kind: Cluster( - 0, + 2, + ), + input: Union( + FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , KvPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), + input: Tee { + inner: : Delta( + Tee { + inner: : Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( + Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), + input: CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 2, + ), + }, + }, + ), + }, + }, + ), + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , cur_leader) | (cur_leader , KvPayload { key , value : c_id . raw_id . to_string () }) }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, + ), + }, + CycleSink { + ident: Ident { + sym: cycle_3, + }, + location_kind: Cluster( + 2, ), input: DeferTick( ReduceKeyed { @@ -1275,12 +1372,12 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { - sym: cycle_2, + sym: cycle_3, }, location_kind: Cluster( - 0, + 2, ), }, }, @@ -1289,61 +1386,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: : Delta( - Tee { - inner: : Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , ()) , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (leader_id , _) | leader_id }), - input: Network { - from_location: Cluster( - 2, - ), - from_key: None, - to_location: Cluster( - 0, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , ()) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < () > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < () > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use crate :: __staged :: cluster :: paxos :: * ; move | _ | () }), - input: Tee { - inner: , - }, - }, - }, - }, - }, - }, - ), - }, - }, - ), + inner: , }, }, }, @@ -1379,7 +1422,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { inner: , @@ -1388,14 +1431,14 @@ expression: built.ir() }, DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { inner: : Source { - source: Interval( - { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) }, + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, ), location_kind: Cluster( - 0, + 2, ), }, }, @@ -1429,7 +1472,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: , }, @@ -1441,7 +1484,7 @@ expression: built.ir() }, DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { inner: , }, @@ -1452,7 +1495,7 @@ expression: built.ir() }, ), Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { inner: , }, @@ -1460,31 +1503,4 @@ expression: built.ir() ), }, }, - CycleSink { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 0, - ), - input: Union( - FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , ClientPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), - input: Tee { - inner: , - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_bench :: ClientPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (key , leader_ballot) | (leader_ballot , ClientPayload { key , value : c_id . raw_id . to_string () }) }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - ), - }, ] From 0a5abab3dac224c9591bcdd837d07c6e5c2773c6 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Sun, 3 Nov 2024 14:54:58 -0800 Subject: [PATCH 19/74] refactor(paxos): make Paxos-KV generic (#1517) --- hydroflow_plus/src/persist_pullup.rs | 8 + hydroflow_plus_test/src/cluster/paxos.rs | 73 +-- .../src/cluster/paxos_bench.rs | 25 +- hydroflow_plus_test/src/cluster/paxos_kv.rs | 72 +-- ...cluster__paxos_bench__tests__paxos_ir.snap | 424 +++++++++--------- 5 files changed, 317 insertions(+), 285 deletions(-) diff --git a/hydroflow_plus/src/persist_pullup.rs b/hydroflow_plus/src/persist_pullup.rs index 007e4b7b2e7e..caafcb13836a 100644 --- a/hydroflow_plus/src/persist_pullup.rs +++ b/hydroflow_plus/src/persist_pullup.rs @@ -43,6 +43,14 @@ fn persist_pullup_node( input: behind_persist, })), + HfPlusNode::FilterMap { + f, + input: box HfPlusNode::Persist(behind_persist), + } => HfPlusNode::Persist(Box::new(HfPlusNode::FilterMap { + f, + input: behind_persist, + })), + HfPlusNode::FlatMap { f, input: box HfPlusNode::Persist(behind_persist), diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 05dcb8382f53..2c5b8ce2914e 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -11,10 +11,8 @@ use tokio::time::Instant; pub struct Proposer {} pub struct Acceptor {} -pub trait PaxosPayload: - Serialize + DeserializeOwned + PartialEq + Eq + Default + Clone + Debug -{ -} +pub trait PaxosPayload: Serialize + DeserializeOwned + PartialEq + Eq + Clone + Debug {} +impl PaxosPayload for T {} #[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Hash)] pub struct Ballot { @@ -31,7 +29,7 @@ struct P1a { #[derive(Serialize, Deserialize, Clone, Debug)] struct LogValue

{ ballot: Ballot, - value: P, + value: Option

, // might be a hole } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -45,7 +43,7 @@ struct P1b { struct P2a

{ ballot: Ballot, slot: i32, - value: P, + value: Option

, // might be a re-committed hole } #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] @@ -53,7 +51,7 @@ struct P2b

{ ballot: Ballot, max_ballot: Ballot, slot: i32, - value: P, + value: Option

, // might be a hole } #[expect( @@ -77,7 +75,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( i_am_leader_check_timeout_delay_multiplier: usize, ) -> ( Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, - Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, + Stream<(i32, Option

), Unbounded, NoTick, Cluster<'a, Proposer>>, ) { proposers .source_iter(q!(["Proposers say hello"])) @@ -456,28 +454,33 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( let p_p1b_highest_entries_and_count = p_relevant_p1bs .flat_map(q!(|p1b| p1b.accepted.into_iter())) // Convert HashMap log back to stream - .fold_keyed(q!(|| (0, LogValue { ballot: Ballot { num: 0, proposer_id: ClusterId::from_raw(0) }, value: Default::default() })), q!(|curr_entry, new_entry| { - let same_values = new_entry.value == curr_entry.1.value; - let higher_ballot = new_entry.ballot > curr_entry.1.ballot; - // Increment count if the values are the same - if same_values { - curr_entry.0 += 1; - } - // Replace the ballot with the largest one - if higher_ballot { - curr_entry.1.ballot = new_entry.ballot; - // Replace the value with the one from the largest ballot, if necessary - if !same_values { - curr_entry.0 = 1; - curr_entry.1.value = new_entry.value; + .fold_keyed::<(usize, Option>), _, _>(q!(|| (0, None)), q!(|curr_entry, new_entry| { + if let Some(curr_entry_payload) = &mut curr_entry.1 { + let same_values = new_entry.value == curr_entry_payload.value; + let higher_ballot = new_entry.ballot > curr_entry_payload.ballot; + // Increment count if the values are the same + if same_values { + curr_entry.0 += 1; } + // Replace the ballot with the largest one + if higher_ballot { + curr_entry_payload.ballot = new_entry.ballot; + // Replace the value with the one from the largest ballot, if necessary + if !same_values { + curr_entry.0 = 1; + curr_entry_payload.value = new_entry.value; + } + } + } else { + *curr_entry = (1, Some(new_entry)); } })); let p_log_to_try_commit = p_p1b_highest_entries_and_count .clone() .cross_singleton(p_ballot_num.clone()) - .filter_map(q!( - move |((slot, (count, entry)), ballot_num)| if count <= f as u32 { + .filter_map(q!(move |((slot, (count, entry)), ballot_num)| { + let entry = entry.unwrap(); + if count <= f { Some(P2a { ballot: Ballot { num: ballot_num, @@ -489,7 +492,7 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( } else { None } - )); + })); let p_max_slot = p_p1b_highest_entries_and_count .clone() .map(q!(|(slot, _)| slot)) @@ -508,7 +511,7 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( proposer_id: p_id }, slot, - value: Default::default() + value: None })); (p_log_to_try_commit, p_max_slot, p_log_holes) } @@ -538,7 +541,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( a_max_ballot: Singleton>, ) -> ( - Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>>, + Stream<(i32, Option

), Unbounded, NoTick, Cluster<'a, Proposer>>, Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, ) { @@ -563,7 +566,6 @@ fn sequence_payload<'a, P: PaxosPayload, R>( f, ); - // End tell clients that leader election has completed let p_to_replicas = p_p2b(proposers, a_to_proposers_p2b.clone(), f); (p_to_replicas, a_log, a_to_proposers_p2b) @@ -602,7 +604,11 @@ fn p_p2a<'a, P: PaxosPayload>( // .inspect(q!(|next| println!("{} p_indexed_payloads next slot: {}", context.current_tick(), next)))) .cross_singleton(p_ballot_num.clone()) // .inspect(q!(|ballot_num| println!("{} p_indexed_payloads ballot_num: {}", context.current_tick(), ballot_num)))) - .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { ballot: Ballot { num: ballot_num, proposer_id: p_id }, slot: next_slot + index as i32, value: payload })); + .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { + ballot: Ballot { num: ballot_num, proposer_id: p_id }, + slot: next_slot + index as i32, + value: Some(payload) + })); // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); let p_to_acceptors_p2a = p_log_to_recommit .union(p_indexed_payloads.clone()) @@ -738,7 +744,7 @@ fn p_p2b<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, f: usize, -) -> Stream<(i32, P), Unbounded, NoTick, Cluster<'a, Proposer>> { +) -> Stream<(i32, Option

), Unbounded, NoTick, Cluster<'a, Proposer>> { let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposers.tick_cycle(); let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposers.tick_cycle(); let p_p2b = a_to_proposers_p2b.tick_batch().union(p_persisted_p2bs); @@ -751,12 +757,13 @@ fn p_p2b<'a, P: PaxosPayload>( None })) .fold_keyed( - q!(|| (0, Default::default())), + q!(|| (0, None)), q!(|accum, value| { accum.0 += 1; - accum.1 = value; + accum.1 = Some(value); }), - ); + ) + .map(q!(|(k, (count, v))| (k, (count, v.unwrap())))); let p_p2b_quorum_reached = p_count_matching_p2bs .clone() diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 2332a176597c..baba72b2dd2f 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -6,7 +6,7 @@ use hydroflow_plus::*; use stageleft::*; use super::paxos::{Acceptor, Proposer}; -use super::paxos_kv::{paxos_kv, KvPayload, Replica, SequencedKv}; +use super::paxos_kv::{paxos_kv, KvPayload, Replica}; pub struct Client {} @@ -58,10 +58,7 @@ pub fn paxos_bench<'a>( .map(q!(|(leader_id, _)| leader_id)), ); processed_payloads - .map(q!(|payload| ( - ClusterId::from_raw(payload.kv.value.parse::().unwrap()), - payload - ))) + .map(q!(|payload| (payload.value, payload))) .send_bincode(&clients) }, num_clients_per_node, @@ -82,9 +79,14 @@ fn bench_client<'a>( Cluster<'a, Client>, >, transaction_cycle: impl FnOnce( - Stream<(ClusterId, KvPayload), Unbounded, NoTick, Cluster<'a, Client>>, + Stream< + (ClusterId, KvPayload>), + Unbounded, + NoTick, + Cluster<'a, Client>, + >, ) -> Stream< - (ClusterId, SequencedKv), + (ClusterId, KvPayload>), Unbounded, NoTick, Cluster<'a, Client>, @@ -112,7 +114,7 @@ fn bench_client<'a>( leader_ballot, KvPayload { key: i as u32, - value: c_id.raw_id.to_string() + value: c_id } ) ))); @@ -126,7 +128,7 @@ fn bench_client<'a>( let c_received_payloads = transaction_results .tick_batch() .map(q!(|(sender, replica_payload)| ( - replica_payload.kv.key, + replica_payload.key, sender ))) .union(c_pending_quorum_payloads); @@ -154,10 +156,7 @@ fn bench_client<'a>( .cross_singleton(current_leader.clone().latest_tick()) .map(q!(move |(key, cur_leader)| ( cur_leader, - KvPayload { - key, - value: c_id.raw_id.to_string() - } + KvPayload { key, value: c_id } ))); c_to_proposers_complete_cycle.complete( c_new_payloads_when_leader_elected diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index ba9a40e73ef4..597401290001 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -1,35 +1,45 @@ use std::collections::HashMap; +use std::fmt::Debug; +use std::hash::Hash; use hydroflow_plus::*; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use stageleft::*; -use super::paxos::{paxos_core, Acceptor, PaxosPayload, Proposer}; +use super::paxos::{paxos_core, Acceptor, Proposer}; pub struct Replica {} -#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] -pub struct KvPayload { - pub key: u32, - pub value: String, -} +pub trait KvKey: Serialize + DeserializeOwned + Hash + Eq + Clone + Debug {} +impl KvKey for K {} -impl Default for KvPayload { - fn default() -> Self { - Self { - key: 0, - value: "".to_string(), - } - } -} +pub trait KvValue: Serialize + DeserializeOwned + Eq + Clone + Debug {} +impl KvValue for V {} -impl PaxosPayload for KvPayload {} +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct KvPayload { + pub key: K, + pub value: V, +} -#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] -pub struct SequencedKv { +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct SequencedKv { // Note: Important that seq is the first member of the struct for sorting pub seq: i32, - pub kv: KvPayload, + pub kv: Option>, +} + +impl Ord for SequencedKv { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.seq.cmp(&other.seq) + } +} + +impl PartialOrd for SequencedKv { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } #[expect( @@ -37,11 +47,11 @@ pub struct SequencedKv { clippy::too_many_arguments, reason = "internal paxos code // TODO" )] -pub fn paxos_kv<'a>( +pub fn paxos_kv<'a, K: KvKey, V: KvValue>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, replicas: &Cluster<'a, Replica>, - c_to_proposers: Stream>, + c_to_proposers: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, @@ -49,7 +59,7 @@ pub fn paxos_kv<'a>( checkpoint_frequency: usize, ) -> ( Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, - Stream>, + Stream, Unbounded, NoTick, Cluster<'a, Replica>>, ) { let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = replicas.forward_ref::>(); @@ -80,13 +90,13 @@ pub fn paxos_kv<'a>( // Replicas. All relations for replicas will be prefixed with r. Expects ReplicaPayload on p_to_replicas, outputs a stream of (client address, ReplicaPayload) after processing. #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -pub fn replica<'a>( +pub fn replica<'a, K: KvKey, V: KvValue>( replicas: &Cluster<'a, Replica>, - p_to_replicas: Stream>, + p_to_replicas: Stream, Unbounded, NoTick, Cluster<'a, Replica>>, checkpoint_frequency: usize, ) -> ( Stream>, - Stream>, + Stream, Unbounded, NoTick, Cluster<'a, Replica>>, ) { let (r_buffered_payloads_complete_cycle, r_buffered_payloads) = replicas.tick_cycle(); // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); @@ -139,10 +149,10 @@ pub fn replica<'a>( let r_kv_store = r_processable_payloads .clone() .persist() // Optimization: all_ticks() + fold() = fold, where the state of the previous fold is saved and persisted values are deleted. - .fold(q!(|| (HashMap::::new(), -1)), q!(|state, payload| { - let kv_store = &mut state.0; - let last_seq = &mut state.1; - kv_store.insert(payload.kv.key, payload.kv.value); + .fold(q!(|| (HashMap::new(), -1)), q!(|(kv_store, last_seq), payload| { + if let Some(kv) = payload.kv { + kv_store.insert(kv.key, kv.value); + } debug_assert!(payload.seq == *last_seq + 1, "Hole in log between seq {} and {}", *last_seq, payload.seq); *last_seq = payload.seq; // println!("Replica kv store: {:?}", kv_store); @@ -172,6 +182,8 @@ pub fn replica<'a>( r_checkpointed_seqs_complete_cycle.complete_next_tick(r_checkpoint_seq_new.clone()); // Tell clients that the payload has been committed. All ReplicaPayloads contain the client's machine ID (to string) as value. - let r_to_clients = p_to_replicas; - (r_checkpoint_seq_new.all_ticks(), r_to_clients) + ( + r_checkpoint_seq_new.all_ticks(), + p_to_replicas.filter_map(q!(|t| t.kv)), + ) } diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 6103176aa6d9..711533647bc3 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -44,7 +44,7 @@ expression: built.ir() Union( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_1, @@ -55,7 +55,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -211,7 +211,7 @@ expression: built.ir() ), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 1, @@ -226,7 +226,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -237,13 +237,13 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), input: CrossSingleton( CrossSingleton( Tee { @@ -378,7 +378,7 @@ expression: built.ir() }, ), Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > >) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -409,16 +409,16 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), input: CrossSingleton( Persist( Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), input: Tee { inner: , }, @@ -475,13 +475,13 @@ expression: built.ir() inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , LogValue { ballot : Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } , value : Default :: default () }) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { let same_values = new_entry . value == curr_entry . 1 . value ; let higher_ballot = new_entry . ballot > curr_entry . 1 . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry . 1 . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry . 1 . value = new_entry . value ; } } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { inner: , }, @@ -531,15 +531,15 @@ expression: built.ir() Tee { inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : Some (payload) } }), input: CrossSingleton( CrossSingleton( Enumerate( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 2, @@ -554,7 +554,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -565,7 +565,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), @@ -619,194 +619,197 @@ expression: built.ir() input: DeferTick( Difference( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload)) , core :: option :: Option < (i32 , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (i32 , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { - inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , Default :: default ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = value ; } }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), - input: Tee { - inner: : Union( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 1, - ), - from_key: None, - to_location: Cluster( - 0, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > (& data) . unwrap () . into ()) }", - ], - }, + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), + input: FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = Some (value) ; } }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), + input: Tee { + inner: : Union( + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 1, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 0, ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), - input: CrossSingleton( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 0, - ), - from_key: None, - to_location: Cluster( - 1, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > (& data) . unwrap () . into ()) }", - ], - }, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), + input: CrossSingleton( + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 0, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 1, ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Union( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | if count <= f as u32 { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : Default :: default () } }), - input: CrossSingleton( - Difference( - FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), - input: Tee { - inner: , - }, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Union( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } } }), + input: CrossSingleton( + Tee { + inner: , }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (u32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), - input: Tee { - inner: , - }, + Tee { + inner: , }, ), - Tee { - inner: , - }, - ), - }, - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : None } }), input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: DeferTick( - Tee { - inner: , - }, - ), + Difference( + FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), + input: Tee { + inner: , + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: , }, }, + ), + Tee { + inner: , }, ), }, + ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: DeferTick( + Tee { + inner: , + }, + ), + }, + }, + }, + ), + }, + }, }, - }, - ), - }, - Tee { - inner: , + ), + }, + Tee { + inner: , + }, + ), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Tee { + inner: , + }, }, ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Tee { - inner: , - }, - }, - ), + }, }, }, }, }, - }, - Tee { - inner: , - }, - ), + Tee { + inner: , + }, + ), + }, }, }, }, - }, - CycleSource { - ident: Ident { - sym: cycle_7, + CycleSource { + ident: Ident { + sym: cycle_7, + }, + location_kind: Cluster( + 0, + ), }, - location_kind: Cluster( - 0, - ), - }, - ), + ), + }, }, }, }, @@ -816,7 +819,7 @@ expression: built.ir() }, Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { inner: , }, @@ -834,10 +837,10 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), input: AntiJoin( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { inner: , }, @@ -857,12 +860,12 @@ expression: built.ir() 1, ), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), input: Persist( Union( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( Tee { inner: , @@ -873,7 +876,7 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), input: Delta( Union( Reduce { @@ -986,16 +989,16 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { inner: : Sort( Union( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -1010,7 +1013,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -1021,15 +1024,15 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), input: AntiJoin( Tee { inner: , @@ -1062,7 +1065,7 @@ expression: built.ir() Tee { inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | - 1 }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), input: CrossSingleton( Tee { inner: , @@ -1103,16 +1106,16 @@ expression: built.ir() input: DeferTick( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: < u32 , String > :: new () , - 1) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , std :: string :: String > , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | state , payload | { let kv_store = & mut state . 0 ; let last_seq = & mut state . 1 ; kv_store . insert (payload . kv . key , payload . kv . value) ; debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , - 1) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), input: Persist( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { inner: , @@ -1249,7 +1252,7 @@ expression: built.ir() Tee { inner: : Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . kv . key , sender) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { from_location: Cluster( 3, @@ -1264,7 +1267,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -1275,15 +1278,18 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (ClusterId :: from_raw (payload . kv . value . parse :: < u32 > () . unwrap ()) , payload) }), - input: Tee { - inner: , + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | t | t . kv }), + input: Tee { + inner: , + }, }, }, }, @@ -1322,7 +1328,7 @@ expression: built.ir() ), input: Union( FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , KvPayload { key : i as u32 , value : c_id . raw_id . to_string () })) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , KvPayload { key : i as u32 , value : c_id })) }), input: Tee { inner: : Delta( Tee { @@ -1347,7 +1353,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , cur_leader) | (cur_leader , KvPayload { key , value : c_id . raw_id . to_string () }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , cur_leader) | (cur_leader , KvPayload { key , value : c_id }) }), input: CrossSingleton( Tee { inner: , From 5657563c989566e7c7b69dcb395e40b024c83c6c Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Mon, 4 Nov 2024 12:08:56 -0800 Subject: [PATCH 20/74] refactor(hydroflow_plus)!: fold `Tick` vs `NoTick` into the location type parameter (#1519) Now, when the location is a top-level `Process` or `Cluster` that corresponds to a `NoTick`, and for streams inside a tick we wrap the location type (e.g. `Tick>`). This simplifies type signatures for a lot of our example code. --- hydroflow_plus/src/cycle.rs | 7 +- hydroflow_plus/src/lib.rs | 2 +- hydroflow_plus/src/location.rs | 77 ++++--- hydroflow_plus/src/singleton.rs | 210 +++++++++--------- hydroflow_plus/src/stream.rs | 174 ++++++++------- hydroflow_plus_test/src/cluster/paxos.rs | 160 ++++++------- .../src/cluster/paxos_bench.rs | 13 +- hydroflow_plus_test/src/cluster/paxos_kv.rs | 18 +- 8 files changed, 325 insertions(+), 336 deletions(-) diff --git a/hydroflow_plus/src/cycle.rs b/hydroflow_plus/src/cycle.rs index 4f2f010ecb2f..dcf8bdeb5a29 100644 --- a/hydroflow_plus/src/cycle.rs +++ b/hydroflow_plus/src/cycle.rs @@ -2,7 +2,8 @@ use std::marker::PhantomData; use crate::builder::FlowState; use crate::location::{Location, LocationId}; -use crate::Tick; + +pub struct TickCycle {} pub trait DeferTick { fn defer_tick(self) -> Self; @@ -45,12 +46,12 @@ impl<'a, T, S: CycleComplete<'a, T>> HfForwardRef<'a, T, S> { } } -pub struct HfCycle<'a, S: CycleComplete<'a, Tick> + DeferTick> { +pub struct HfCycle<'a, S: CycleComplete<'a, TickCycle> + DeferTick> { pub(crate) ident: syn::Ident, pub(crate) _phantom: PhantomData<(&'a mut &'a (), S)>, } -impl<'a, S: CycleComplete<'a, Tick> + DeferTick> HfCycle<'a, S> { +impl<'a, S: CycleComplete<'a, TickCycle> + DeferTick> HfCycle<'a, S> { pub fn complete_next_tick(self, stream: S) { let ident = self.ident; S::complete(stream.defer_tick(), ident) diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 8846d15223e6..9bd5b086d872 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -19,7 +19,7 @@ pub mod runtime_support { } pub mod stream; -pub use stream::{Bounded, NoTick, Stream, Tick, Unbounded}; +pub use stream::{Bounded, Stream, Tick, Unbounded}; pub mod singleton; pub use singleton::{Optional, Singleton}; diff --git a/hydroflow_plus/src/location.rs b/hydroflow_plus/src/location.rs index 65b05334fe69..3036a0b74ec4 100644 --- a/hydroflow_plus/src/location.rs +++ b/hydroflow_plus/src/location.rs @@ -12,9 +12,10 @@ use serde::{Deserialize, Serialize}; use stageleft::{q, quote_type, Quoted}; use super::builder::{ClusterIds, ClusterSelfId, FlowState}; -use crate::cycle::{CycleCollection, CycleCollectionWithInitial, DeferTick, HfCycle}; +use crate::cycle::{CycleCollection, CycleCollectionWithInitial, DeferTick, HfCycle, TickCycle}; use crate::ir::{HfPlusNode, HfPlusSource}; -use crate::{Bounded, HfForwardRef, NoTick, Optional, Singleton, Stream, Tick, Unbounded}; +use crate::stream::NoTick; +use crate::{Bounded, HfForwardRef, Optional, Singleton, Stream, Tick, Unbounded}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum LocationId { @@ -28,9 +29,9 @@ pub trait Location<'a> { fn flow_state(&self) -> &FlowState; - fn spin(&self) -> Stream<(), Unbounded, NoTick, Self> + fn spin(&self) -> Stream<(), Unbounded, Self> where - Self: Sized, + Self: Sized + NoTick, { Stream::new( self.id(), @@ -45,9 +46,9 @@ pub trait Location<'a> { fn spin_batch( &self, batch_size: impl Quoted<'a, usize> + Copy + 'a, - ) -> Stream<(), Bounded, Tick, Self> + ) -> Stream<(), Bounded, Tick> where - Self: Sized, + Self: Sized + NoTick, { self.spin() .flat_map(q!(move |_| 0..batch_size)) @@ -58,9 +59,9 @@ pub trait Location<'a> { fn source_stream + Unpin>( &self, e: impl Quoted<'a, E>, - ) -> Stream + ) -> Stream where - Self: Sized, + Self: Sized + NoTick, { let e = e.splice_untyped(); @@ -77,9 +78,9 @@ pub trait Location<'a> { fn source_iter>( &self, e: impl Quoted<'a, E>, - ) -> Stream + ) -> Stream where - Self: Sized, + Self: Sized + NoTick, { let e = e.splice_untyped(); @@ -93,9 +94,9 @@ pub trait Location<'a> { ) } - fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton + fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton where - Self: Sized, + Self: Sized + NoTick, { let e_arr = q!([e]); let e = e_arr.splice_untyped(); @@ -118,9 +119,9 @@ pub trait Location<'a> { fn singleton_each_tick( &self, e: impl Quoted<'a, T>, - ) -> Singleton + ) -> Singleton> where - Self: Sized, + Self: Sized + NoTick, { self.singleton(e).latest_tick() } @@ -128,9 +129,9 @@ pub trait Location<'a> { fn singleton_first_tick( &self, e: impl Quoted<'a, T>, - ) -> Optional + ) -> Optional> where - Self: Sized, + Self: Sized + NoTick, { let e_arr = q!([e]); let e = e_arr.splice_untyped(); @@ -148,9 +149,9 @@ pub trait Location<'a> { fn source_interval( &self, interval: impl Quoted<'a, Duration> + Copy + 'a, - ) -> Stream + ) -> Stream where - Self: Sized, + Self: Sized + NoTick, { self.source_stream(q!(tokio_stream::wrappers::IntervalStream::new( tokio::time::interval(interval) @@ -161,18 +162,21 @@ pub trait Location<'a> { &self, delay: impl Quoted<'a, Duration> + Copy + 'a, interval: impl Quoted<'a, Duration> + Copy + 'a, - ) -> Stream + ) -> Stream where - Self: Sized, + Self: Sized + NoTick, { self.source_stream(q!(tokio_stream::wrappers::IntervalStream::new( tokio::time::interval_at(tokio::time::Instant::now() + delay, interval) ))) } - fn forward_ref>( + fn forward_ref>( &self, - ) -> (HfForwardRef<'a, NoTick, S>, S) { + ) -> (HfForwardRef<'a, (), S>, S) + where + Self: NoTick, + { let next_id = { let on_id = match self.id() { LocationId::Process(id) => id, @@ -199,9 +203,12 @@ pub trait Location<'a> { ) } - fn tick_forward_ref>( + fn tick_forward_ref>( &self, - ) -> (HfForwardRef<'a, Tick, S>, S) { + ) -> (HfForwardRef<'a, TickCycle, S>, S) + where + Self: NoTick, + { let next_id = { let on_id = match self.id() { LocationId::Process(id) => id, @@ -228,9 +235,12 @@ pub trait Location<'a> { ) } - fn tick_cycle + DeferTick>( + fn tick_cycle + DeferTick>( &self, - ) -> (HfCycle<'a, S>, S) { + ) -> (HfCycle<'a, S>, S) + where + Self: NoTick, + { let next_id = { let on_id = match self.id() { LocationId::Process(id) => id, @@ -258,11 +268,14 @@ pub trait Location<'a> { } fn tick_cycle_with_initial< - S: CycleCollectionWithInitial<'a, Tick, Location = Self> + DeferTick, + S: CycleCollectionWithInitial<'a, TickCycle, Location = Self> + DeferTick, >( &self, initial: S, - ) -> (HfCycle<'a, S>, S) { + ) -> (HfCycle<'a, S>, S) + where + Self: NoTick, + { let next_id = { let on_id = match self.id() { LocationId::Process(id) => id, @@ -336,10 +349,10 @@ impl<'a, P> Location<'a> for ExternalProcess<'a, P> { } impl<'a, P> ExternalProcess<'a, P> { - pub fn source_external_bytes>( + pub fn source_external_bytes + NoTick>( &self, to: &L, - ) -> (ExternalBytesPort, Stream) { + ) -> (ExternalBytesPort, Stream) { let next_external_port_id = { let mut flow_state = self.flow_state.borrow_mut(); let id = flow_state.next_external_out; @@ -372,10 +385,10 @@ impl<'a, P> ExternalProcess<'a, P> { ) } - pub fn source_external_bincode, T: Serialize + DeserializeOwned>( + pub fn source_external_bincode + NoTick, T: Serialize + DeserializeOwned>( &self, to: &L, - ) -> (ExternalBincodeSink, Stream) { + ) -> (ExternalBincodeSink, Stream) { let next_external_port_id = { let mut flow_state = self.flow_state.borrow_mut(); let id = flow_state.next_external_out; diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 9a3beaa036e9..d9db34a831cb 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -6,7 +6,9 @@ use std::rc::Rc; use stageleft::{q, IntoQuotedMut, Quoted}; use crate::builder::FlowState; -use crate::cycle::{CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick}; +use crate::cycle::{ + CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick, TickCycle, +}; use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; use crate::location::{Location, LocationId}; use crate::stream::{Bounded, NoTick, Tick, Unbounded}; @@ -20,16 +22,16 @@ pub trait CrossResult<'a, Other> { fn make(location_kind: LocationId, flow_state: FlowState, ir_node: HfPlusNode) -> Self::Out; } -impl<'a, T, U: Clone, W, C, N: Location<'a>> CrossResult<'a, Singleton> - for Singleton +impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Singleton> + for Singleton { - type Out = Singleton<(T, U), W, C, N>; + type Out = Singleton<(T, U), W, N>; - fn other_location(other: &Singleton) -> LocationId { + fn other_location(other: &Singleton) -> LocationId { other.location_kind } - fn other_ir_node(other: Singleton) -> HfPlusNode { + fn other_ir_node(other: Singleton) -> HfPlusNode { other.ir_node.into_inner() } @@ -38,16 +40,16 @@ impl<'a, T, U: Clone, W, C, N: Location<'a>> CrossResult<'a, Singleton> CrossResult<'a, Optional> - for Singleton +impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Optional> + for Singleton { - type Out = Optional<(T, U), W, C, N>; + type Out = Optional<(T, U), W, N>; - fn other_location(other: &Optional) -> LocationId { + fn other_location(other: &Optional) -> LocationId { other.location_kind } - fn other_ir_node(other: Optional) -> HfPlusNode { + fn other_ir_node(other: Optional) -> HfPlusNode { other.ir_node.into_inner() } @@ -56,16 +58,14 @@ impl<'a, T, U: Clone, W, C, N: Location<'a>> CrossResult<'a, Optional> CrossResult<'a, Optional> - for Optional -{ - type Out = Optional<(T, U), W, C, N>; +impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Optional> for Optional { + type Out = Optional<(T, U), W, N>; - fn other_location(other: &Optional) -> LocationId { + fn other_location(other: &Optional) -> LocationId { other.location_kind } - fn other_ir_node(other: Optional) -> HfPlusNode { + fn other_ir_node(other: Optional) -> HfPlusNode { other.ir_node.into_inner() } @@ -74,16 +74,16 @@ impl<'a, T, U: Clone, W, C, N: Location<'a>> CrossResult<'a, Optional> CrossResult<'a, Singleton> - for Optional +impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Singleton> + for Optional { - type Out = Optional<(T, U), W, C, N>; + type Out = Optional<(T, U), W, N>; - fn other_location(other: &Singleton) -> LocationId { + fn other_location(other: &Singleton) -> LocationId { other.location_kind } - fn other_ir_node(other: Singleton) -> HfPlusNode { + fn other_ir_node(other: Singleton) -> HfPlusNode { other.ir_node.into_inner() } @@ -92,16 +92,16 @@ impl<'a, T, U: Clone, W, C, N: Location<'a>> CrossResult<'a, Singleton { +pub struct Singleton { pub(crate) location_kind: LocationId, flow_state: FlowState, pub(crate) ir_node: RefCell, - _phantom: PhantomData<(T, N, W, C)>, + _phantom: PhantomData<(T, N, W)>, } -impl<'a, T, W, C, N: Location<'a>> Singleton { +impl<'a, T, W, N: Location<'a>> Singleton { pub(crate) fn new( location_kind: LocationId, flow_state: FlowState, @@ -116,10 +116,8 @@ impl<'a, T, W, C, N: Location<'a>> Singleton { } } -impl<'a, T, C, N: Location<'a>> From> - for Singleton -{ - fn from(singleton: Singleton) -> Self { +impl<'a, T, N: Location<'a>> From> for Singleton { + fn from(singleton: Singleton) -> Self { Singleton::new( singleton.location_kind, singleton.flow_state, @@ -128,13 +126,13 @@ impl<'a, T, C, N: Location<'a>> From> } } -impl<'a, T, N: Location<'a>> DeferTick for Singleton { +impl<'a, T, N: Location<'a>> DeferTick for Singleton> { fn defer_tick(self) -> Self { Singleton::defer_tick(self) } } -impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Singleton { +impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> { fn complete(self, ident: syn::Ident) { self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, @@ -144,7 +142,7 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Singleton> CycleCollection<'a, Tick> for Singleton { +impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Singleton> { type Location = N; fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { @@ -159,8 +157,8 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, Tick> for Singleton> CycleCollectionWithInitial<'a, Tick> - for Singleton +impl<'a, T, N: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> + for Singleton> { type Location = N; @@ -184,7 +182,7 @@ impl<'a, T, N: Location<'a>> CycleCollectionWithInitial<'a, Tick> } } -impl<'a, T: Clone, W, C, N: Location<'a>> Clone for Singleton { +impl<'a, T: Clone, W, N: Location<'a>> Clone for Singleton { fn clone(&self) -> Self { if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); @@ -209,8 +207,8 @@ impl<'a, T: Clone, W, C, N: Location<'a>> Clone for Singleton { } } -impl<'a, T, W, C, N: Location<'a>> Singleton { - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Singleton { +impl<'a, T, W, N: Location<'a>> Singleton { + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Singleton { Singleton::new( self.location_kind, self.flow_state, @@ -224,7 +222,7 @@ impl<'a, T, W, C, N: Location<'a>> Singleton { pub fn flat_map, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -235,10 +233,7 @@ impl<'a, T, W, C, N: Location<'a>> Singleton { ) } - pub fn filter bool + 'a>( - self, - f: impl IntoQuotedMut<'a, F>, - ) -> Optional { + pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( self.location_kind, self.flow_state, @@ -252,7 +247,7 @@ impl<'a, T, W, C, N: Location<'a>> Singleton { pub fn filter_map Option + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Optional { + ) -> Optional { Optional::new( self.location_kind, self.flow_state, @@ -264,7 +259,7 @@ impl<'a, T, W, C, N: Location<'a>> Singleton { } } -impl<'a, T, N: Location<'a>> Singleton { +impl<'a, T, N: Location<'a>> Singleton> { pub fn cross_singleton(self, other: Other) -> >::Out where Self: CrossResult<'a, Other>, @@ -285,22 +280,22 @@ impl<'a, T, N: Location<'a>> Singleton { pub fn continue_if( self, - signal: Optional, - ) -> Optional { + signal: Optional>, + ) -> Optional> { self.cross_singleton(signal.map(q!(|_u| ()))) .map(q!(|(d, _signal)| d)) } pub fn continue_unless( self, - other: Optional, - ) -> Optional { + other: Optional>, + ) -> Optional> { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } } -impl<'a, T, N: Location<'a>> Singleton { - pub fn all_ticks(self) -> Stream { +impl<'a, T, N: Location<'a>> Singleton> { + pub fn all_ticks(self) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -308,7 +303,7 @@ impl<'a, T, N: Location<'a>> Singleton { ) } - pub fn latest(self) -> Singleton { + pub fn latest(self) -> Singleton { Singleton::new( self.location_kind, self.flow_state, @@ -316,7 +311,7 @@ impl<'a, T, N: Location<'a>> Singleton { ) } - pub fn defer_tick(self) -> Singleton { + pub fn defer_tick(self) -> Singleton> { Singleton::new( self.location_kind, self.flow_state, @@ -324,7 +319,7 @@ impl<'a, T, N: Location<'a>> Singleton { ) } - pub fn persist(self) -> Stream { + pub fn persist(self) -> Stream> { Stream::new( self.location_kind, self.flow_state, @@ -332,7 +327,7 @@ impl<'a, T, N: Location<'a>> Singleton { ) } - pub fn delta(self) -> Optional { + pub fn delta(self) -> Optional> { Optional::new( self.location_kind, self.flow_state, @@ -341,8 +336,8 @@ impl<'a, T, N: Location<'a>> Singleton { } } -impl<'a, T, B, N: Location<'a>> Singleton { - pub fn latest_tick(self) -> Singleton { +impl<'a, T, B, N: Location<'a> + NoTick> Singleton { + pub fn latest_tick(self) -> Singleton> { Singleton::new( self.location_kind, self.flow_state, @@ -353,10 +348,10 @@ impl<'a, T, B, N: Location<'a>> Singleton { pub fn sample_every( self, duration: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { + ) -> Stream { let interval = duration.splice_typed(); - let samples = Stream::<(), Bounded, Tick, N>::new( + let samples = Stream::<(), Bounded, Tick>::new( self.location_kind, self.flow_state.clone(), HfPlusNode::Source { @@ -372,7 +367,7 @@ impl<'a, T, B, N: Location<'a>> Singleton { } } -impl<'a, T, N: Location<'a>> Singleton { +impl<'a, T, N: Location<'a> + NoTick> Singleton { pub fn cross_singleton(self, other: Other) -> >::Out where Self: CrossResult<'a, Other>, @@ -392,16 +387,16 @@ impl<'a, T, N: Location<'a>> Singleton { } } -pub struct Optional { +pub struct Optional { pub(crate) location_kind: LocationId, flow_state: FlowState, pub(crate) ir_node: RefCell, - _phantom: PhantomData<(T, N, W, C)>, + _phantom: PhantomData<(T, N, W)>, } -impl<'a, T, W, C, N: Location<'a>> Optional { +impl<'a, T, W, N: Location<'a>> Optional { pub(crate) fn new( location_kind: LocationId, flow_state: FlowState, @@ -415,7 +410,7 @@ impl<'a, T, W, C, N: Location<'a>> Optional { } } - pub fn some(singleton: Singleton) -> Self { + pub fn some(singleton: Singleton) -> Self { Optional::new( singleton.location_kind, singleton.flow_state, @@ -424,13 +419,13 @@ impl<'a, T, W, C, N: Location<'a>> Optional { } } -impl<'a, T, N: Location<'a>> DeferTick for Optional { +impl<'a, T, N: Location<'a>> DeferTick for Optional> { fn defer_tick(self) -> Self { Optional::defer_tick(self) } } -impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Optional { +impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Optional> { fn complete(self, ident: syn::Ident) { self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, @@ -440,7 +435,7 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Optional> CycleCollection<'a, Tick> for Optional { +impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> { type Location = N; fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { @@ -455,7 +450,7 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, Tick> for Optional> CycleComplete<'a, NoTick> for Optional { +impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ()> for Optional { fn complete(self, ident: syn::Ident) { self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, @@ -465,7 +460,7 @@ impl<'a, T, W, N: Location<'a>> CycleComplete<'a, NoTick> for Optional> CycleCollection<'a, NoTick> for Optional { +impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ()> for Optional { type Location = N; fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { @@ -480,13 +475,13 @@ impl<'a, T, W, N: Location<'a>> CycleCollection<'a, NoTick> for Optional> From> for Optional { - fn from(singleton: Singleton) -> Self { +impl<'a, T, W, N: Location<'a>> From> for Optional { + fn from(singleton: Singleton) -> Self { Optional::some(singleton) } } -impl<'a, T: Clone, W, C, N: Location<'a>> Clone for Optional { +impl<'a, T: Clone, W, N: Location<'a>> Clone for Optional { fn clone(&self) -> Self { if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); @@ -511,8 +506,8 @@ impl<'a, T: Clone, W, C, N: Location<'a>> Clone for Optional { } } -impl<'a, T, W, C, N: Location<'a>> Optional { - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { +impl<'a, T, W, N: Location<'a>> Optional { + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( self.location_kind, self.flow_state, @@ -526,7 +521,7 @@ impl<'a, T, W, C, N: Location<'a>> Optional { pub fn flat_map, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -537,10 +532,7 @@ impl<'a, T, W, C, N: Location<'a>> Optional { ) } - pub fn filter bool + 'a>( - self, - f: impl IntoQuotedMut<'a, F>, - ) -> Optional { + pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( self.location_kind, self.flow_state, @@ -554,7 +546,7 @@ impl<'a, T, W, C, N: Location<'a>> Optional { pub fn filter_map Option + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Optional { + ) -> Optional { Optional::new( self.location_kind, self.flow_state, @@ -566,9 +558,9 @@ impl<'a, T, W, C, N: Location<'a>> Optional { } } -impl<'a, T, N: Location<'a>> Optional { +impl<'a, T, N: Location<'a>> Optional> { // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream - pub fn into_stream(self) -> Stream { + pub fn into_stream(self) -> Stream> { Stream::new( self.location_kind, self.flow_state, @@ -578,12 +570,12 @@ impl<'a, T, N: Location<'a>> Optional { pub fn cross_singleton( self, - other: impl Into>, - ) -> Optional<(T, O), Bounded, Tick, N> + other: impl Into>>, + ) -> Optional<(T, O), Bounded, Tick> where O: Clone, { - let other: Optional = other.into(); + let other: Optional> = other.into(); if self.location_kind != other.location_kind { panic!("cross_singleton must be called on streams on the same node"); } @@ -600,24 +592,24 @@ impl<'a, T, N: Location<'a>> Optional { pub fn continue_if( self, - signal: Optional, - ) -> Optional { + signal: Optional>, + ) -> Optional> { self.cross_singleton(signal.map(q!(|_u| ()))) .map(q!(|(d, _signal)| d)) } - pub fn then(self, value: Singleton) -> Optional { + pub fn then(self, value: Singleton>) -> Optional> { value.continue_if(self) } pub fn continue_unless( self, - other: Optional, - ) -> Optional { + other: Optional>, + ) -> Optional> { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } - pub fn union(self, other: Optional) -> Optional { + pub fn union(self, other: Optional>) -> Optional> { if self.location_kind != other.location_kind { panic!("union must be called on streams on the same node"); } @@ -634,8 +626,8 @@ impl<'a, T, N: Location<'a>> Optional { pub fn unwrap_or( self, - other: Singleton, - ) -> Singleton { + other: Singleton>, + ) -> Singleton> { if self.location_kind != other.location_kind { panic!("or_else must be called on streams on the same node"); } @@ -651,8 +643,8 @@ impl<'a, T, N: Location<'a>> Optional { } } -impl<'a, T, N: Location<'a>> Optional { - pub fn all_ticks(self) -> Stream { +impl<'a, T, N: Location<'a>> Optional> { + pub fn all_ticks(self) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -660,7 +652,7 @@ impl<'a, T, N: Location<'a>> Optional { ) } - pub fn latest(self) -> Optional { + pub fn latest(self) -> Optional { Optional::new( self.location_kind, self.flow_state, @@ -668,7 +660,7 @@ impl<'a, T, N: Location<'a>> Optional { ) } - pub fn defer_tick(self) -> Optional { + pub fn defer_tick(self) -> Optional> { Optional::new( self.location_kind, self.flow_state, @@ -676,7 +668,7 @@ impl<'a, T, N: Location<'a>> Optional { ) } - pub fn persist(self) -> Stream { + pub fn persist(self) -> Stream> { Stream::new( self.location_kind, self.flow_state, @@ -684,7 +676,7 @@ impl<'a, T, N: Location<'a>> Optional { ) } - pub fn delta(self) -> Optional { + pub fn delta(self) -> Optional> { Optional::new( self.location_kind, self.flow_state, @@ -693,8 +685,8 @@ impl<'a, T, N: Location<'a>> Optional { } } -impl<'a, T, B, N: Location<'a>> Optional { - pub fn latest_tick(self) -> Optional { +impl<'a, T, B, N: Location<'a> + NoTick> Optional { + pub fn latest_tick(self) -> Optional> { Optional::new( self.location_kind, self.flow_state, @@ -702,17 +694,17 @@ impl<'a, T, B, N: Location<'a>> Optional { ) } - pub fn tick_samples(self) -> Stream { + pub fn tick_samples(self) -> Stream { self.latest_tick().all_ticks() } pub fn sample_every( self, duration: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { + ) -> Stream { let interval = duration.splice_typed(); - let samples = Stream::<(), Bounded, Tick, N>::new( + let samples = Stream::<(), Bounded, Tick>::new( self.location_kind, self.flow_state.clone(), HfPlusNode::Source { @@ -729,8 +721,8 @@ impl<'a, T, B, N: Location<'a>> Optional { pub fn unwrap_or( self, - other: impl Into>, - ) -> Singleton { + other: impl Into>, + ) -> Singleton { let other = other.into(); if self.location_kind != other.location_kind { panic!("or_else must be called on streams on the same node"); @@ -740,15 +732,15 @@ impl<'a, T, B, N: Location<'a>> Optional { } } -impl<'a, T, N: Location<'a>> Optional { +impl<'a, T, N: Location<'a> + NoTick> Optional { pub fn cross_singleton( self, - other: impl Into>, - ) -> Optional<(T, O), Unbounded, NoTick, N> + other: impl Into>, + ) -> Optional<(T, O), Unbounded, N> where O: Clone, { - let other: Optional = other.into(); + let other: Optional = other.into(); if self.location_kind != other.location_kind { panic!("cross_singleton must be called on streams on the same node"); } diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 7c6c9cb142a3..55545d46ccec 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -14,7 +14,7 @@ use syn::parse_quote; use super::staging_util::get_this_crate; use crate::builder::{self, FlowState}; -use crate::cycle::{CycleCollection, CycleComplete, DeferTick}; +use crate::cycle::{CycleCollection, CycleComplete, DeferTick, TickCycle}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; use crate::location::{ CanSend, ExternalBincodeStream, ExternalBytesPort, ExternalProcess, Location, LocationId, @@ -29,10 +29,24 @@ pub enum Unbounded {} /// to be complete in finite time. pub enum Bounded {} -/// Marks the stream as existing outside of a clock domain. -pub enum NoTick {} +pub trait NoTick {} +impl NoTick for Process<'_, T> {} +impl NoTick for Cluster<'_, T> {} + /// Marks the stream as being inside the single global clock domain. -pub enum Tick {} +pub struct Tick { + l: L, +} + +impl<'a, L: Location<'a>> Location<'a> for Tick { + fn id(&self) -> LocationId { + self.l.id() + } + + fn flow_state(&self) -> &FlowState { + self.l.flow_state() + } +} /// An infinite stream of elements of type `T`. /// @@ -42,25 +56,23 @@ pub enum Tick {} /// - `T`: the type of elements in the stream /// - `W`: the boundedness of the stream, which is either [`Bounded`] /// or [`Unbounded`] -/// - `C`: the tick domain of the stream, which is either [`Tick`] or -/// [`NoTick`] /// - `N`: the type of the node that the stream is materialized on -pub struct Stream { +pub struct Stream { location_kind: LocationId, flow_state: FlowState, pub(crate) ir_node: RefCell, - _phantom: PhantomData<(T, N, W, C)>, + _phantom: PhantomData<(T, N, W)>, } -impl<'a, T, N: Location<'a>> DeferTick for Stream { +impl<'a, T, N: Location<'a>> DeferTick for Stream> { fn defer_tick(self) -> Self { Stream::defer_tick(self) } } -impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Stream { +impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Stream> { fn complete(self, ident: syn::Ident) { self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, @@ -70,7 +82,7 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, Tick> for Stream> CycleCollection<'a, Tick> for Stream { +impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream> { type Location = N; fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { @@ -85,7 +97,7 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, Tick> for Stream> CycleComplete<'a, NoTick> for Stream { +impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ()> for Stream { fn complete(self, ident: syn::Ident) { self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, @@ -95,7 +107,7 @@ impl<'a, T, W, N: Location<'a>> CycleComplete<'a, NoTick> for Stream> CycleCollection<'a, NoTick> for Stream { +impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ()> for Stream { type Location = N; fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { @@ -110,7 +122,7 @@ impl<'a, T, W, N: Location<'a>> CycleCollection<'a, NoTick> for Stream> Stream { +impl<'a, T, W, N: Location<'a>> Stream { pub(crate) fn new( location_kind: LocationId, flow_state: FlowState, @@ -125,7 +137,7 @@ impl<'a, T, W, C, N: Location<'a>> Stream { } } -impl<'a, T: Clone, W, C, N: Location<'a>> Clone for Stream { +impl<'a, T: Clone, W, N: Location<'a>> Clone for Stream { fn clone(&self) -> Self { if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); @@ -150,8 +162,8 @@ impl<'a, T: Clone, W, C, N: Location<'a>> Clone for Stream { } } -impl<'a, T, W, C, N: Location<'a>> Stream { - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { +impl<'a, T, W, N: Location<'a>> Stream { + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -165,7 +177,7 @@ impl<'a, T, W, C, N: Location<'a>> Stream { pub fn flat_map, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -176,17 +188,14 @@ impl<'a, T, W, C, N: Location<'a>> Stream { ) } - pub fn flatten(self) -> Stream + pub fn flatten(self) -> Stream where T: IntoIterator, { self.flat_map(q!(|d| d)) } - pub fn filter bool + 'a>( - self, - f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -200,7 +209,7 @@ impl<'a, T, W, C, N: Location<'a>> Stream { pub fn filter_map Option + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -213,12 +222,12 @@ impl<'a, T, W, C, N: Location<'a>> Stream { pub fn cross_singleton( self, - other: impl Into>, - ) -> Stream<(T, O), W, C, N> + other: impl Into>, + ) -> Stream<(T, O), W, N> where O: Clone, { - let other: Optional = other.into(); + let other: Optional = other.into(); if self.location_kind != other.location_kind { panic!("cross_singleton must be called on streams on the same node"); } @@ -234,7 +243,7 @@ impl<'a, T, W, C, N: Location<'a>> Stream { } // TODO(shadaj): should allow for differing windows, using strongest one - pub fn cross_product(self, other: Stream) -> Stream<(T, O), W, C, N> + pub fn cross_product(self, other: Stream) -> Stream<(T, O), W, N> where T: Clone, O: Clone, @@ -253,7 +262,7 @@ impl<'a, T, W, C, N: Location<'a>> Stream { ) } - pub fn union(self, other: Stream) -> Stream { + pub fn union(self, other: Stream) -> Stream { if self.location_kind != other.location_kind { panic!("union must be called on streams on the same node"); } @@ -268,7 +277,7 @@ impl<'a, T, W, C, N: Location<'a>> Stream { ) } - pub fn unique(self) -> Stream + pub fn unique(self) -> Stream where T: Eq + Hash, { @@ -287,8 +296,8 @@ impl<'a, T, W, C, N: Location<'a>> Stream { } } -impl<'a, T, N: Location<'a>> Stream { - pub fn all_ticks(self) -> Stream { +impl<'a, T, N: Location<'a>> Stream> { + pub fn all_ticks(self) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -296,7 +305,7 @@ impl<'a, T, N: Location<'a>> Stream { ) } - pub fn persist(self) -> Stream + pub fn persist(self) -> Stream> where T: Clone, { @@ -307,7 +316,7 @@ impl<'a, T, N: Location<'a>> Stream { ) } - pub fn defer_tick(self) -> Stream { + pub fn defer_tick(self) -> Stream> { Stream::new( self.location_kind, self.flow_state, @@ -318,7 +327,7 @@ impl<'a, T, N: Location<'a>> Stream { pub fn inspect( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream> { Stream::new( self.location_kind, self.flow_state, @@ -329,7 +338,7 @@ impl<'a, T, N: Location<'a>> Stream { ) } - pub fn first(self) -> Optional { + pub fn first(self) -> Optional> { Optional::new( self.location_kind, self.flow_state, @@ -340,8 +349,8 @@ impl<'a, T, N: Location<'a>> Stream { /// Allow this stream through if the other stream has elements, otherwise the output is empty. pub fn continue_if( self, - signal: Optional, - ) -> Stream { + signal: Optional>, + ) -> Stream> { self.cross_singleton(signal.map(q!(|_u| ()))) .map(q!(|(d, _signal)| d)) } @@ -349,12 +358,12 @@ impl<'a, T, N: Location<'a>> Stream { /// Allow this stream through if the other stream is empty, otherwise the output is empty. pub fn continue_unless( self, - other: Optional, - ) -> Stream { + other: Optional>, + ) -> Stream> { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } - pub fn enumerate(self) -> Stream<(usize, T), Bounded, Tick, N> { + pub fn enumerate(self) -> Stream<(usize, T), Bounded, Tick> { Stream::new( self.location_kind, self.flow_state, @@ -366,7 +375,7 @@ impl<'a, T, N: Location<'a>> Stream { self, init: impl IntoQuotedMut<'a, I>, comb: impl IntoQuotedMut<'a, F>, - ) -> Singleton { + ) -> Singleton> { Singleton::new( self.location_kind, self.flow_state, @@ -381,7 +390,7 @@ impl<'a, T, N: Location<'a>> Stream { pub fn reduce( self, comb: impl IntoQuotedMut<'a, F>, - ) -> Optional { + ) -> Optional> { Optional::new( self.location_kind, self.flow_state, @@ -392,7 +401,7 @@ impl<'a, T, N: Location<'a>> Stream { ) } - pub fn max(self) -> Optional + pub fn max(self) -> Optional> where T: Ord, { @@ -403,7 +412,7 @@ impl<'a, T, N: Location<'a>> Stream { })) } - pub fn min(self) -> Optional + pub fn min(self) -> Optional> where T: Ord, { @@ -414,7 +423,7 @@ impl<'a, T, N: Location<'a>> Stream { })) } - pub fn sort(self) -> Stream + pub fn sort(self) -> Stream> where T: Ord, { @@ -425,11 +434,11 @@ impl<'a, T, N: Location<'a>> Stream { ) } - pub fn count(self) -> Singleton { + pub fn count(self) -> Singleton> { self.fold(q!(|| 0usize), q!(|count, _| *count += 1)) } - pub fn delta(self) -> Stream { + pub fn delta(self) -> Stream> { Stream::new( self.location_kind, self.flow_state, @@ -438,8 +447,8 @@ impl<'a, T, N: Location<'a>> Stream { } } -impl<'a, T, W, N: Location<'a>> Stream { - pub fn tick_batch(self) -> Stream { +impl<'a, T, W, N: Location<'a> + NoTick> Stream { + pub fn tick_batch(self) -> Stream> { Stream::new( self.location_kind, self.flow_state, @@ -447,14 +456,14 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - pub fn tick_prefix(self) -> Stream + pub fn tick_prefix(self) -> Stream> where T: Clone, { self.tick_batch().persist() } - pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { + pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( self.location_kind, self.flow_state, @@ -466,7 +475,7 @@ impl<'a, T, W, N: Location<'a>> Stream { } } -impl<'a, T, W, N: Location<'a>> Stream { +impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn for_each(self, f: impl IntoQuotedMut<'a, F>) { self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::ForEach { input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), @@ -475,14 +484,14 @@ impl<'a, T, W, N: Location<'a>> Stream { } } -impl<'a, T, N: Location<'a>> Stream { +impl<'a, T, N: Location<'a> + NoTick> Stream { pub fn sample_every( self, duration: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { + ) -> Stream { let interval = duration.splice_typed(); - let samples = Stream::::new( + let samples = Stream::>::new( self.location_kind, self.flow_state.clone(), HfPlusNode::Source { @@ -498,7 +507,7 @@ impl<'a, T, N: Location<'a>> Stream { self, init: impl IntoQuotedMut<'a, I>, comb: impl IntoQuotedMut<'a, F>, - ) -> Singleton { + ) -> Singleton { // unbounded singletons are represented as a stream // which produces all values from all ticks every tick, // so delta will always give the lastest aggregation @@ -516,7 +525,7 @@ impl<'a, T, N: Location<'a>> Stream { pub fn reduce( self, comb: impl IntoQuotedMut<'a, F>, - ) -> Optional { + ) -> Optional { Optional::new( self.location_kind, self.flow_state, @@ -527,7 +536,7 @@ impl<'a, T, N: Location<'a>> Stream { ) } - pub fn max(self) -> Optional + pub fn max(self) -> Optional where T: Ord, { @@ -538,7 +547,7 @@ impl<'a, T, N: Location<'a>> Stream { })) } - pub fn min(self) -> Optional + pub fn min(self) -> Optional where T: Ord, { @@ -550,8 +559,8 @@ impl<'a, T, N: Location<'a>> Stream { } } -impl<'a, T, C, N: Location<'a>> Stream { - pub fn filter_not_in(self, other: Stream) -> Stream +impl<'a, T, N: Location<'a>> Stream { + pub fn filter_not_in(self, other: Stream) -> Stream where T: Eq + Hash, { @@ -570,15 +579,15 @@ impl<'a, T, C, N: Location<'a>> Stream { } } -impl<'a, T: Clone, W, C, N: Location<'a>> Stream<&T, W, C, N> { - pub fn cloned(self) -> Stream { +impl<'a, T: Clone, W, N: Location<'a>> Stream<&T, W, N> { + pub fn cloned(self) -> Stream { self.map(q!(|d| d.clone())) } } -impl<'a, K, V1, W, C, N: Location<'a>> Stream<(K, V1), W, C, N> { +impl<'a, K, V1, W, N: Location<'a>> Stream<(K, V1), W, N> { // TODO(shadaj): figure out window semantics - pub fn join(self, n: Stream<(K, V2), W2, C, N>) -> Stream<(K, (V1, V2)), W, C, N> + pub fn join(self, n: Stream<(K, V2), W2, N>) -> Stream<(K, (V1, V2)), W, N> where K: Eq + Hash, { @@ -596,7 +605,7 @@ impl<'a, K, V1, W, C, N: Location<'a>> Stream<(K, V1), W, C, N> { ) } - pub fn anti_join(self, n: Stream) -> Stream<(K, V1), W, C, N> + pub fn anti_join(self, n: Stream) -> Stream<(K, V1), W, N> where K: Eq + Hash, { @@ -615,12 +624,12 @@ impl<'a, K, V1, W, C, N: Location<'a>> Stream<(K, V1), W, C, N> { } } -impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick, N> { +impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { pub fn fold_keyed A + 'a, F: Fn(&mut A, V) + 'a>( self, init: impl IntoQuotedMut<'a, I>, comb: impl IntoQuotedMut<'a, F>, - ) -> Stream<(K, A), Bounded, Tick, N> { + ) -> Stream<(K, A), Bounded, Tick> { Stream::new( self.location_kind, self.flow_state, @@ -635,7 +644,7 @@ impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick, N> { pub fn reduce_keyed( self, comb: impl IntoQuotedMut<'a, F>, - ) -> Stream<(K, V), Bounded, Tick, N> { + ) -> Stream<(K, V), Bounded, Tick> { Stream::new( self.location_kind, self.flow_state, @@ -688,11 +697,11 @@ pub(super) fn deserialize_bincode(tagged: Option } } -impl<'a, T, W, N: Location<'a>> Stream { +impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn decouple_process( self, other: &Process<'a, P2>, - ) -> Stream> + ) -> Stream> where N: CanSend<'a, Process<'a, P2>, In = T, Out = T>, T: Clone + Serialize + DeserializeOwned, @@ -703,7 +712,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn decouple_cluster( self, other: &Cluster<'a, C2>, - ) -> Stream> + ) -> Stream> where N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)>, T: Clone + Serialize + DeserializeOwned, @@ -723,7 +732,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn send_bincode, CoreType>( self, other: &N2, - ) -> Stream, Unbounded, NoTick, N2> + ) -> Stream, Unbounded, N2> where N: CanSend<'a, N2, In = T>, CoreType: Serialize + DeserializeOwned, @@ -789,10 +798,7 @@ impl<'a, T, W, N: Location<'a>> Stream { } } - pub fn send_bytes>( - self, - other: &N2, - ) -> Stream, Unbounded, NoTick, N2> + pub fn send_bytes>(self, other: &N2) -> Stream, Unbounded, N2> where N: CanSend<'a, N2, In = T>, { @@ -854,7 +860,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn send_bincode_interleaved, Tag, CoreType>( self, other: &N2, - ) -> Stream + ) -> Stream where N: CanSend<'a, N2, In = T, Out = (Tag, CoreType)>, CoreType: Serialize + DeserializeOwned, @@ -865,7 +871,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn send_bytes_interleaved, Tag>( self, other: &N2, - ) -> Stream + ) -> Stream where N: CanSend<'a, N2, In = T, Out = (Tag, Bytes)>, { @@ -875,7 +881,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn broadcast_bincode( self, other: &Cluster<'a, C2>, - ) -> Stream, Unbounded, NoTick, Cluster<'a, C2>> + ) -> Stream, Unbounded, Cluster<'a, C2>> where N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, T: Clone + Serialize + DeserializeOwned, @@ -892,7 +898,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn broadcast_bincode_interleaved( self, other: &Cluster<'a, C2>, - ) -> Stream> + ) -> Stream> where N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, T: Clone + Serialize + DeserializeOwned, @@ -903,7 +909,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn broadcast_bytes( self, other: &Cluster<'a, C2>, - ) -> Stream, Unbounded, NoTick, Cluster<'a, C2>> + ) -> Stream, Unbounded, Cluster<'a, C2>> where N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, T: Clone, @@ -920,7 +926,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn broadcast_bytes_interleaved( self, other: &Cluster<'a, C2>, - ) -> Stream> + ) -> Stream> where N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + 'a, diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 2c5b8ce2914e..b75a3957c97c 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -62,20 +62,15 @@ struct P2b

{ pub fn paxos_core<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, - r_to_acceptors_checkpoint: Stream< - (ClusterId, i32), - Unbounded, - NoTick, - Cluster<'a, Acceptor>, - >, - c_to_proposers: Stream>, + r_to_acceptors_checkpoint: Stream<(ClusterId, i32), Unbounded, Cluster<'a, Acceptor>>, + c_to_proposers: Stream>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, ) -> ( - Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, - Stream<(i32, Option

), Unbounded, NoTick, Cluster<'a, Proposer>>, + Stream<(), Unbounded, Cluster<'a, Proposer>>, + Stream<(i32, Option

), Unbounded, Cluster<'a, Proposer>>, ) { proposers .source_iter(q!(["Proposers say hello"])) @@ -86,9 +81,9 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( .for_each(q!(|s| println!("{}", s))); let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b_forward_reference) = - proposers.forward_ref::, _, _, _>>(); + proposers.forward_ref::, _, _>>(); let (a_log_complete_cycle, a_log_forward_reference) = - acceptors.tick_forward_ref::>), _, _, _>>(); + acceptors.tick_forward_ref::>), _, _>>(); let (p_ballot_num, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( proposers, @@ -149,20 +144,20 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, - p_received_p2b_ballots: Stream>, - a_log: Singleton>, + p_received_p2b_ballots: Stream>, + a_log: Singleton>>, ) -> ( - Singleton>, - Optional>, - Stream, Bounded, Tick, Cluster<'a, Proposer>>, - Singleton>, + Singleton>>, + Optional>>, + Stream, Bounded, Tick>>, + Singleton>>, ) { let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b_forward_ref) = - proposers.forward_ref::, _, _, _>>(); + proposers.forward_ref::, _, _>>(); let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader_forward_ref) = - proposers.forward_ref::>(); + proposers.forward_ref::>(); let (p_is_leader_complete_cycle, p_is_leader_forward_ref) = - proposers.tick_forward_ref::>(); + proposers.tick_forward_ref::>(); // a_to_proposers_p2b.clone().for_each(q!(|(_, p2b): (u32, P2b)| println!("Proposer received P2b: {:?}", p2b))); // p_to_proposers_i_am_leader.clone().for_each(q!(|ballot: Ballot| println!("Proposer received I am leader: {:?}", ballot))); // c_to_proposers.clone().for_each(q!(|payload: ClientPayload| println!("Client sent proposer payload: {:?}", payload))); @@ -213,10 +208,10 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( // Proposer logic to calculate the largest ballot received so far. fn p_max_ballot<'a>( proposers: &Cluster<'a, Proposer>, - p_received_p1b_ballots: Stream>, - p_received_p2b_ballots: Stream>, - p_to_proposers_i_am_leader: Stream>, -) -> Singleton> { + p_received_p1b_ballots: Stream>, + p_received_p2b_ballots: Stream>, + p_to_proposers_i_am_leader: Stream>, +) -> Singleton> { p_received_p1b_ballots .union(p_received_p2b_ballots) .union(p_to_proposers_i_am_leader) @@ -231,10 +226,10 @@ fn p_max_ballot<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_ballot_calc<'a>( proposers: &Cluster<'a, Proposer>, - p_received_max_ballot: Singleton>, + p_received_max_ballot: Singleton>>, ) -> ( - Singleton>, - Optional<(Ballot, u32), Bounded, Tick, Cluster<'a, Proposer>>, + Singleton>>, + Optional<(Ballot, u32), Bounded, Tick>>, ) { let p_id = proposers.self_id(); let (p_ballot_num_complete_cycle, p_ballot_num) = @@ -273,10 +268,10 @@ fn p_ballot_calc<'a>( } fn p_leader_expired<'a>( - p_to_proposers_i_am_leader: Stream>, - p_is_leader: Optional>, + p_to_proposers_i_am_leader: Stream>, + p_is_leader: Optional>>, i_am_leader_check_timeout: u64, // How often to check if heartbeat expired -) -> Optional, Bounded, Tick, Cluster<'a, Proposer>> { +) -> Optional, Bounded, Tick>> { let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold( q!(|| None), q!(|latest, _| { @@ -301,14 +296,14 @@ fn p_leader_expired<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_leader_heartbeat<'a>( proposers: &Cluster<'a, Proposer>, - p_is_leader: Optional>, - p_ballot_num: Singleton>, + p_is_leader: Optional>>, + p_ballot_num: Singleton>>, i_am_leader_send_timeout: u64, // How often to heartbeat i_am_leader_check_timeout: u64, // How often to check if heartbeat expired i_am_leader_check_timeout_delay_multiplier: usize, /* Initial delay, multiplied by proposer pid, to stagger proposers checking for timeouts */ ) -> ( - Stream>, - Optional, Bounded, Tick, Cluster<'a, Proposer>>, + Stream>, + Optional, Bounded, Tick>>, ) { let p_id = proposers.self_id(); let p_to_proposers_i_am_leader = p_is_leader @@ -345,11 +340,11 @@ fn p_leader_heartbeat<'a>( // Proposer logic to send "I am leader" messages periodically to other proposers, or send p1a to acceptors if other leaders expired. fn p_p1a<'a>( - p_ballot_num: Singleton>, - p_trigger_election: Optional, Bounded, Tick, Cluster<'a, Proposer>>, + p_ballot_num: Singleton>>, + p_trigger_election: Optional, Bounded, Tick>>, proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, -) -> Stream> { +) -> Stream> { let p_id = proposers.self_id(); p_trigger_election @@ -368,12 +363,12 @@ fn p_p1a<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( acceptors: &Cluster<'a, Acceptor>, - p_to_acceptors_p1a: Stream>, - a_log: Singleton>, + p_to_acceptors_p1a: Stream>, + a_log: Singleton>>, proposers: &Cluster<'a, Proposer>, ) -> ( - Singleton>, - Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + Singleton>>, + Stream, Unbounded, Cluster<'a, Proposer>>, ) { let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(); let a_max_ballot = p_to_acceptors_p1a @@ -409,13 +404,13 @@ fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( proposers: &Cluster<'a, Proposer>, - a_to_proposers_p1b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, - p_ballot_num: Singleton>, - p_has_largest_ballot: Optional<(Ballot, u32), Bounded, Tick, Cluster<'a, Proposer>>, + a_to_proposers_p1b: Stream, Unbounded, Cluster<'a, Proposer>>, + p_ballot_num: Singleton>>, + p_has_largest_ballot: Optional<(Ballot, u32), Bounded, Tick>>, f: usize, ) -> ( - Optional>, - Stream, Bounded, Tick, Cluster<'a, Proposer>>, + Optional>>, + Stream, Bounded, Tick>>, ) { let p_id = proposers.self_id(); let p_relevant_p1bs = a_to_proposers_p1b @@ -442,13 +437,13 @@ fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn recommit_after_leader_election<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - p_relevant_p1bs: Stream>>, Bounded, Tick, Cluster<'a, Proposer>>, - p_ballot_num: Singleton>, + p_relevant_p1bs: Stream>>, Bounded, Tick>>, + p_ballot_num: Singleton>>, f: usize, ) -> ( - Stream, Bounded, Tick, Cluster<'a, Proposer>>, - Optional>, - Stream, Bounded, Tick, Cluster<'a, Proposer>>, + Stream, Bounded, Tick>>, + Optional>>, + Stream, Bounded, Tick>>, ) { let p_id = proposers.self_id(); @@ -524,26 +519,21 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( fn sequence_payload<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, - c_to_proposers: Stream>, - r_to_acceptors_checkpoint: Stream< - (ClusterId, i32), - Unbounded, - NoTick, - Cluster<'a, Acceptor>, - >, - - p_ballot_num: Singleton>, - p_is_leader: Optional>, - p_max_slot: Optional>, - - p_log_to_recommit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, + c_to_proposers: Stream>, + r_to_acceptors_checkpoint: Stream<(ClusterId, i32), Unbounded, Cluster<'a, Acceptor>>, + + p_ballot_num: Singleton>>, + p_is_leader: Optional>>, + p_max_slot: Optional>>, + + p_log_to_recommit: Stream, Bounded, Tick>>, f: usize, - a_max_ballot: Singleton>, + a_max_ballot: Singleton>>, ) -> ( - Stream<(i32, Option

), Unbounded, NoTick, Cluster<'a, Proposer>>, - Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, - Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + Stream<(i32, Option

), Unbounded, Cluster<'a, Proposer>>, + Singleton<(i32, HashMap>), Bounded, Tick>>, + Stream, Unbounded, Cluster<'a, Proposer>>, ) { let p_to_acceptors_p2a = p_p2a( proposers, @@ -580,16 +570,15 @@ enum CheckpointOrP2a

{ // Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. fn p_p2a<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - p_max_slot: Optional>, - c_to_proposers: Stream>, - p_ballot_num: Singleton>, - p_log_to_recommit: Stream, Bounded, Tick, Cluster<'a, Proposer>>, - p_is_leader: Optional>, + p_max_slot: Optional>>, + c_to_proposers: Stream>, + p_ballot_num: Singleton>>, + p_log_to_recommit: Stream, Bounded, Tick>>, + p_is_leader: Optional>>, acceptors: &Cluster<'a, Acceptor>, -) -> Stream, Unbounded, NoTick, Cluster<'a, Acceptor>> { +) -> Stream, Unbounded, Cluster<'a, Acceptor>> { let p_id = proposers.self_id(); - let (p_next_slot_complete_cycle, p_next_slot) = - proposers.tick_cycle::>(); + let (p_next_slot_complete_cycle, p_next_slot) = proposers.tick_cycle::>(); let p_next_slot_after_reconciling_p1bs = p_max_slot .map(q!(|max_slot| max_slot + 1)) .unwrap_or(proposers.singleton_each_tick(q!(0))) @@ -636,20 +625,15 @@ fn p_p2a<'a, P: PaxosPayload>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p2<'a, P: PaxosPayload, R>( - a_max_ballot: Singleton>, - p_to_acceptors_p2a: Stream, Unbounded, NoTick, Cluster<'a, Acceptor>>, - r_to_acceptors_checkpoint: Stream< - (ClusterId, i32), - Unbounded, - NoTick, - Cluster<'a, Acceptor>, - >, + a_max_ballot: Singleton>>, + p_to_acceptors_p2a: Stream, Unbounded, Cluster<'a, Acceptor>>, + r_to_acceptors_checkpoint: Stream<(ClusterId, i32), Unbounded, Cluster<'a, Acceptor>>, proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, f: usize, ) -> ( - Singleton<(i32, HashMap>), Bounded, Tick, Cluster<'a, Acceptor>>, - Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + Singleton<(i32, HashMap>), Bounded, Tick>>, + Stream, Unbounded, Cluster<'a, Proposer>>, ) { let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(); @@ -742,9 +726,9 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( fn p_p2b<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - a_to_proposers_p2b: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + a_to_proposers_p2b: Stream, Unbounded, Cluster<'a, Proposer>>, f: usize, -) -> Stream<(i32, Option

), Unbounded, NoTick, Cluster<'a, Proposer>> { +) -> Stream<(i32, Option

), Unbounded, Cluster<'a, Proposer>> { let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposers.tick_cycle(); let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposers.tick_cycle(); let p_p2b = a_to_proposers_p2b.tick_batch().union(p_persisted_p2bs); diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index baba72b2dd2f..aa3d3db6c9f1 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -34,7 +34,7 @@ pub fn paxos_bench<'a>( let replicas = flow.cluster::(); let (new_leader_elected_complete, new_leader_elected) = - clients.forward_ref::>(); + clients.forward_ref::>(); bench_client( &clients, @@ -72,23 +72,16 @@ pub fn paxos_bench<'a>( // Clients. All relations for clients will be prefixed with c. All ClientPayloads will contain the virtual client number as key and the client's machine ID (to string) as value. Expects p_to_clients_leader_elected containing Ballots whenever the leader is elected, and r_to_clients_payload_applied containing ReplicaPayloads whenever a payload is committed. Outputs (leader address, ClientPayload) when a new leader is elected or when the previous payload is committed. fn bench_client<'a>( clients: &Cluster<'a, Client>, - p_to_clients_leader_elected: Stream< - ClusterId, - Unbounded, - NoTick, - Cluster<'a, Client>, - >, + p_to_clients_leader_elected: Stream, Unbounded, Cluster<'a, Client>>, transaction_cycle: impl FnOnce( Stream< (ClusterId, KvPayload>), Unbounded, - NoTick, Cluster<'a, Client>, >, ) -> Stream< (ClusterId, KvPayload>), Unbounded, - NoTick, Cluster<'a, Client>, >, num_clients_per_node: usize, @@ -166,7 +159,7 @@ fn bench_client<'a>( // Track statistics let (c_timers_complete_cycle, c_timers) = - clients.tick_cycle::>(); + clients.tick_cycle::>(); let c_new_timers_when_leader_elected = c_new_leader_ballot .map(q!(|_| SystemTime::now())) .flat_map(q!( diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 597401290001..55282bd5b502 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -51,18 +51,18 @@ pub fn paxos_kv<'a, K: KvKey, V: KvValue>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, replicas: &Cluster<'a, Replica>, - c_to_proposers: Stream, Unbounded, NoTick, Cluster<'a, Proposer>>, + c_to_proposers: Stream, Unbounded, Cluster<'a, Proposer>>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, checkpoint_frequency: usize, ) -> ( - Stream<(), Unbounded, NoTick, Cluster<'a, Proposer>>, - Stream, Unbounded, NoTick, Cluster<'a, Replica>>, + Stream<(), Unbounded, Cluster<'a, Proposer>>, + Stream, Unbounded, Cluster<'a, Replica>>, ) { let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = - replicas.forward_ref::>(); + replicas.forward_ref::>(); let (p_to_clients_new_leader_elected, p_to_replicas) = paxos_core( proposers, @@ -92,11 +92,11 @@ pub fn paxos_kv<'a, K: KvKey, V: KvValue>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] pub fn replica<'a, K: KvKey, V: KvValue>( replicas: &Cluster<'a, Replica>, - p_to_replicas: Stream, Unbounded, NoTick, Cluster<'a, Replica>>, + p_to_replicas: Stream, Unbounded, Cluster<'a, Replica>>, checkpoint_frequency: usize, ) -> ( - Stream>, - Stream, Unbounded, NoTick, Cluster<'a, Replica>>, + Stream>, + Stream, Unbounded, Cluster<'a, Replica>>, ) { let (r_buffered_payloads_complete_cycle, r_buffered_payloads) = replicas.tick_cycle(); // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); @@ -107,7 +107,7 @@ pub fn replica<'a, K: KvKey, V: KvValue>( .sort(); // Create a cycle since we'll use this seq before we define it let (r_highest_seq_complete_cycle, r_highest_seq) = - replicas.tick_cycle::>(); + replicas.tick_cycle::>(); let empty_slot = replicas.singleton_first_tick(q!(-1)); // Either the max sequence number executed so far or -1. Need to union otherwise r_highest_seq is empty and joins with it will fail let r_highest_seq_with_default = r_highest_seq.union(empty_slot); @@ -163,7 +163,7 @@ pub fn replica<'a, K: KvKey, V: KvValue>( // Send checkpoints to the acceptors when we've processed enough payloads let (r_checkpointed_seqs_complete_cycle, r_checkpointed_seqs) = - replicas.tick_cycle::>(); + replicas.tick_cycle::>(); let r_max_checkpointed_seq = r_checkpointed_seqs .persist() .max() From 16b730c75cfca79ea5f869308b1e1e14b3e9c155 Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Mon, 4 Nov 2024 14:31:52 -0800 Subject: [PATCH 21/74] fix(hydroflow_lang): `cross_singleton()` forgot value if multiple runs in a tick, fix #1518 (#1520) Adds the minimal reproducer test from @shadaj Note this may have negative performance implications, as the singleton value now is stored in the state API (heap) instead of locally. If we use singleton syntax this duplicate allocation could probably be avoided. > Confirmed that this fixed the bugs in our Paxos implementation, no noticeable performance impact. @shadj --- ...gleton__union_defer_tick@graphvis_dot.snap | 158 ++++++++++++++++++ ...on__union_defer_tick@graphvis_mermaid.snap | 124 ++++++++++++++ hydroflow/tests/surface_cross_singleton.rs | 45 ++++- .../src/graph/ops/cross_singleton.rs | 58 +++++-- 4 files changed, 373 insertions(+), 12 deletions(-) create mode 100644 hydroflow/tests/snapshots/surface_cross_singleton__union_defer_tick@graphvis_dot.snap create mode 100644 hydroflow/tests/snapshots/surface_cross_singleton__union_defer_tick@graphvis_mermaid.snap diff --git a/hydroflow/tests/snapshots/surface_cross_singleton__union_defer_tick@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_cross_singleton__union_defer_tick@graphvis_dot.snap new file mode 100644 index 000000000000..7957a90e7979 --- /dev/null +++ b/hydroflow/tests/snapshots/surface_cross_singleton__union_defer_tick@graphvis_dot.snap @@ -0,0 +1,158 @@ +--- +source: hydroflow/tests/surface_cross_singleton.rs +expression: "df.meta_graph().unwrap().to_dot(& Default :: default())" +--- +digraph { + node [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace", style=filled]; + edge [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace"]; + n1v1 [label="(n1v1) source_stream(cross_rx)", shape=invhouse, fillcolor="#88aaff"] + n2v1 [label="(n2v1) sort()", shape=invhouse, fillcolor="#88aaff"] + n3v1 [label="(n3v1) tee()", shape=house, fillcolor="#ffff88"] + n4v1 [label="(n4v1) defer_tick_lazy()", shape=invhouse, fillcolor="#88aaff"] + n5v1 [label="(n5v1) source_iter([0])", shape=invhouse, fillcolor="#88aaff"] + n6v1 [label="(n6v1) persist::<'static>()", shape=invhouse, fillcolor="#88aaff"] + n7v1 [label="(n7v1) union()", shape=invhouse, fillcolor="#88aaff"] + n8v1 [label="(n8v1) cross_singleton()", shape=invhouse, fillcolor="#88aaff"] + n9v1 [label="(n9v1) tee()", shape=house, fillcolor="#ffff88"] + n10v1 [label="(n10v1) for_each(|x| egress_tx.send(x).unwrap())", shape=house, fillcolor="#ffff88"] + n11v1 [label="(n11v1) fold(|| 0, |_, _| {})", shape=invhouse, fillcolor="#88aaff"] + n12v1 [label="(n12v1) cross_singleton()", shape=invhouse, fillcolor="#88aaff"] + n13v1 [label="(n13v1) fold(|| 0, |_, _| {})", shape=invhouse, fillcolor="#88aaff"] + n14v1 [label="(n14v1) flat_map(|_| [])", shape=invhouse, fillcolor="#88aaff"] + n15v1 [label="(n15v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n16v1 [label="(n16v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n17v1 [label="(n17v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n18v1 [label="(n18v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n19v1 [label="(n19v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n20v1 [label="(n20v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n21v1 [label="(n21v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n22v1 [label="(n22v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n23v1 [label="(n23v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n24v1 [label="(n24v1) identity()", shape=invhouse, fillcolor="#88aaff"] + n25v1 [label="(n25v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n2v1 -> n3v1 + n1v1 -> n15v1 + n3v1 -> n16v1 + n4v1 -> n7v1 [label="0"] + n14v1 -> n17v1 + n5v1 -> n6v1 + n6v1 -> n7v1 [label="1"] + n7v1 -> n18v1 + n8v1 -> n9v1 + n9v1 -> n10v1 + n9v1 -> n19v1 + n3v1 -> n20v1 + n11v1 -> n21v1 + n13v1 -> n22v1 + n12v1 -> n23v1 + n15v1 -> n2v1 [color=red] + n16v1 -> n8v1 [label="input"] + n17v1 -> n24v1 + n18v1 -> n8v1 [label="single", color=red] + n19v1 -> n11v1 [color=red] + n20v1 -> n12v1 [label="input"] + n21v1 -> n12v1 [label="single", color=red] + n22v1 -> n14v1 + n23v1 -> n13v1 [color=red] + n24v1 -> n25v1 + n25v1 -> n4v1 [color=red] + subgraph "cluster n1v1" { + fillcolor="#dddddd" + style=filled + label = "sg_1v1\nstratum 0" + n1v1 + subgraph "cluster_sg_1v1_var_teed_in" { + label="var teed_in" + n1v1 + } + } + subgraph "cluster n2v1" { + fillcolor="#dddddd" + style=filled + label = "sg_2v1\nstratum 1" + n2v1 + n3v1 + subgraph "cluster_sg_2v1_var_teed_in" { + label="var teed_in" + n2v1 + n3v1 + } + } + subgraph "cluster n3v1" { + fillcolor="#dddddd" + style=filled + label = "sg_3v1\nstratum 0" + n4v1 + n5v1 + n6v1 + n7v1 + subgraph "cluster_sg_3v1_var_persisted_stream" { + label="var persisted_stream" + n5v1 + n6v1 + } + subgraph "cluster_sg_3v1_var_unioned_stream" { + label="var unioned_stream" + n7v1 + } + } + subgraph "cluster n4v1" { + fillcolor="#dddddd" + style=filled + label = "sg_4v1\nstratum 1" + n8v1 + n9v1 + n10v1 + subgraph "cluster_sg_4v1_var_join" { + label="var join" + n8v1 + n9v1 + } + } + subgraph "cluster n5v1" { + fillcolor="#dddddd" + style=filled + label = "sg_5v1\nstratum 2" + n11v1 + subgraph "cluster_sg_5v1_var_folded_thing" { + label="var folded_thing" + n11v1 + } + } + subgraph "cluster n6v1" { + fillcolor="#dddddd" + style=filled + label = "sg_6v1\nstratum 3" + n12v1 + subgraph "cluster_sg_6v1_var_joined_folded" { + label="var joined_folded" + n12v1 + } + } + subgraph "cluster n7v1" { + fillcolor="#dddddd" + style=filled + label = "sg_7v1\nstratum 4" + n13v1 + subgraph "cluster_sg_7v1_var_deferred_stream" { + label="var deferred_stream" + n13v1 + } + } + subgraph "cluster n8v1" { + fillcolor="#dddddd" + style=filled + label = "sg_8v1\nstratum 4" + n14v1 + subgraph "cluster_sg_8v1_var_deferred_stream" { + label="var deferred_stream" + n14v1 + } + } + subgraph "cluster n9v1" { + fillcolor="#dddddd" + style=filled + label = "sg_9v1\nstratum 5" + n24v1 + } +} diff --git a/hydroflow/tests/snapshots/surface_cross_singleton__union_defer_tick@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_cross_singleton__union_defer_tick@graphvis_mermaid.snap new file mode 100644 index 000000000000..ae77d69d57cc --- /dev/null +++ b/hydroflow/tests/snapshots/surface_cross_singleton__union_defer_tick@graphvis_mermaid.snap @@ -0,0 +1,124 @@ +--- +source: hydroflow/tests/surface_cross_singleton.rs +expression: "df.meta_graph().unwrap().to_mermaid(& Default :: default())" +--- +%%{init:{'theme':'base','themeVariables':{'clusterBkg':'#ddd','clusterBorder':'#888'}}}%% +flowchart TD +classDef pullClass fill:#8af,stroke:#000,text-align:left,white-space:pre +classDef pushClass fill:#ff8,stroke:#000,text-align:left,white-space:pre +classDef otherClass fill:#fdc,stroke:#000,text-align:left,white-space:pre +linkStyle default stroke:#aaa +1v1[\"(1v1) source_stream(cross_rx)"/]:::pullClass +2v1[\"(2v1) sort()"/]:::pullClass +3v1[/"(3v1) tee()"\]:::pushClass +4v1[\"(4v1) defer_tick_lazy()"/]:::pullClass +5v1[\"(5v1) source_iter([0])"/]:::pullClass +6v1[\"(6v1) persist::<'static>()"/]:::pullClass +7v1[\"(7v1) union()"/]:::pullClass +8v1[\"(8v1) cross_singleton()"/]:::pullClass +9v1[/"(9v1) tee()"\]:::pushClass +10v1[/"(10v1) for_each(|x| egress_tx.send(x).unwrap())"\]:::pushClass +11v1[\"(11v1) fold(|| 0, |_, _| {})"/]:::pullClass +12v1[\"(12v1) cross_singleton()"/]:::pullClass +13v1[\"(13v1) fold(|| 0, |_, _| {})"/]:::pullClass +14v1[\"(14v1) flat_map(|_| [])"/]:::pullClass +15v1["(15v1) handoff"]:::otherClass +16v1["(16v1) handoff"]:::otherClass +17v1["(17v1) handoff"]:::otherClass +18v1["(18v1) handoff"]:::otherClass +19v1["(19v1) handoff"]:::otherClass +20v1["(20v1) handoff"]:::otherClass +21v1["(21v1) handoff"]:::otherClass +22v1["(22v1) handoff"]:::otherClass +23v1["(23v1) handoff"]:::otherClass +24v1[\"(24v1) identity()"/]:::pullClass +25v1["(25v1) handoff"]:::otherClass +2v1-->3v1 +1v1-->15v1 +3v1-->16v1 +4v1-->|0|7v1 +14v1-->17v1 +5v1-->6v1 +6v1-->|1|7v1 +7v1-->18v1 +8v1-->9v1 +9v1-->10v1 +9v1-->19v1 +3v1-->20v1 +11v1-->21v1 +13v1-->22v1 +12v1-->23v1 +15v1--x2v1; linkStyle 15 stroke:red +16v1-->|input|8v1 +17v1-->24v1 +18v1--x|single|8v1; linkStyle 18 stroke:red +19v1--x11v1; linkStyle 19 stroke:red +20v1-->|input|12v1 +21v1--x|single|12v1; linkStyle 21 stroke:red +22v1-->14v1 +23v1--x13v1; linkStyle 23 stroke:red +24v1-->25v1 +25v1--o4v1; linkStyle 25 stroke:red +subgraph sg_1v1 ["sg_1v1 stratum 0"] + 1v1 + subgraph sg_1v1_var_teed_in ["var teed_in"] + 1v1 + end +end +subgraph sg_2v1 ["sg_2v1 stratum 1"] + 2v1 + 3v1 + subgraph sg_2v1_var_teed_in ["var teed_in"] + 2v1 + 3v1 + end +end +subgraph sg_3v1 ["sg_3v1 stratum 0"] + 4v1 + 5v1 + 6v1 + 7v1 + subgraph sg_3v1_var_persisted_stream ["var persisted_stream"] + 5v1 + 6v1 + end + subgraph sg_3v1_var_unioned_stream ["var unioned_stream"] + 7v1 + end +end +subgraph sg_4v1 ["sg_4v1 stratum 1"] + 8v1 + 9v1 + 10v1 + subgraph sg_4v1_var_join ["var join"] + 8v1 + 9v1 + end +end +subgraph sg_5v1 ["sg_5v1 stratum 2"] + 11v1 + subgraph sg_5v1_var_folded_thing ["var folded_thing"] + 11v1 + end +end +subgraph sg_6v1 ["sg_6v1 stratum 3"] + 12v1 + subgraph sg_6v1_var_joined_folded ["var joined_folded"] + 12v1 + end +end +subgraph sg_7v1 ["sg_7v1 stratum 4"] + 13v1 + subgraph sg_7v1_var_deferred_stream ["var deferred_stream"] + 13v1 + end +end +subgraph sg_8v1 ["sg_8v1 stratum 4"] + 14v1 + subgraph sg_8v1_var_deferred_stream ["var deferred_stream"] + 14v1 + end +end +subgraph sg_9v1 ["sg_9v1 stratum 5"] + 24v1 +end diff --git a/hydroflow/tests/surface_cross_singleton.rs b/hydroflow/tests/surface_cross_singleton.rs index 5dedaa432655..497670a28dcc 100644 --- a/hydroflow/tests/surface_cross_singleton.rs +++ b/hydroflow/tests/surface_cross_singleton.rs @@ -2,7 +2,7 @@ use hydroflow::util::collect_ready; use hydroflow::{assert_graphvis_snapshots, hydroflow_syntax}; use multiplatform_test::multiplatform_test; -#[multiplatform_test] +#[multiplatform_test(test, wasm, env_tracing)] pub fn test_basic() { let (single_tx, single_rx) = hydroflow::util::unbounded_channel::<()>(); let (egress_tx, mut egress_rx) = hydroflow::util::unbounded_channel(); @@ -26,3 +26,46 @@ pub fn test_basic() { let out: Vec<_> = collect_ready(&mut egress_rx); assert_eq!(out, vec![(1, ()), (2, ()), (3, ())]); } + +#[multiplatform_test(test, wasm, env_tracing)] +pub fn test_union_defer_tick() { + let (cross_tx, cross_rx) = hydroflow::util::unbounded_channel::(); + let (egress_tx, mut egress_rx) = hydroflow::util::unbounded_channel(); + + let mut df = hydroflow_syntax! { + teed_in = source_stream(cross_rx) -> sort() -> tee(); + teed_in -> [input]join; + + deferred_stream -> defer_tick_lazy() -> [0]unioned_stream; + + persisted_stream = source_iter([0]) -> persist::<'static>(); + persisted_stream -> [1]unioned_stream; + + unioned_stream = union(); + unioned_stream -> [single]join; + + join = cross_singleton() -> tee(); + + join -> for_each(|x| egress_tx.send(x).unwrap()); + + folded_thing = join -> fold(|| 0, |_, _| {}); + + teed_in -> [input]joined_folded; + folded_thing -> [single]joined_folded; + joined_folded = cross_singleton(); + deferred_stream = joined_folded -> fold(|| 0, |_, _| {}) -> flat_map(|_| []); + }; + assert_graphvis_snapshots!(df); + + df.run_available(); + let out: Vec<_> = collect_ready(&mut egress_rx); + assert_eq!(out, vec![]); + + cross_tx.send(1).unwrap(); + cross_tx.send(2).unwrap(); + cross_tx.send(3).unwrap(); + df.run_available(); + + let out: Vec<_> = collect_ready(&mut egress_rx); + assert_eq!(out, vec![(1, 0), (2, 0), (3, 0)]); +} diff --git a/hydroflow_lang/src/graph/ops/cross_singleton.rs b/hydroflow_lang/src/graph/ops/cross_singleton.rs index 0c8780c5ccfa..ee9e2c8def78 100644 --- a/hydroflow_lang/src/graph/ops/cross_singleton.rs +++ b/hydroflow_lang/src/graph/ops/cross_singleton.rs @@ -2,8 +2,8 @@ use quote::{quote_spanned, ToTokens}; use syn::parse_quote; use super::{ - DelayType, OperatorCategory, OperatorConstraints, - OperatorWriteOutput, WriteContextArgs, RANGE_0, RANGE_1, + DelayType, OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, + RANGE_0, RANGE_1, }; use crate::graph::PortIndexValue; @@ -48,6 +48,8 @@ pub const CROSS_SINGLETON: OperatorConstraints = OperatorConstraints { _else => None, }, write_fn: |wc @ &WriteContextArgs { + context, + hydroflow, ident, op_span, inputs, @@ -57,20 +59,54 @@ pub const CROSS_SINGLETON: OperatorConstraints = OperatorConstraints { _diagnostics| { assert!(is_pull); - let input = &inputs[0]; - let singleton = &inputs[1]; - let s_taken_ident = wc.make_ident("singleton_taken"); - let singleton_value_ident = wc.make_ident("singleton_value"); + let stream_input = &inputs[0]; + let singleton_input = &inputs[1]; + let singleton_handle_ident = wc.make_ident("singleton_handle"); + + let write_prologue = quote_spanned! {op_span=> + let #singleton_handle_ident = #hydroflow.add_state( + ::std::cell::RefCell::new(::std::option::Option::None) + ); + // Reset the value if it is a new tick. + #hydroflow.set_state_tick_hook(#singleton_handle_ident, |rcell| { rcell.take(); }); + }; let write_iterator = quote_spanned! {op_span=> - let mut #s_taken_ident = #singleton; - let #ident = #s_taken_ident.next().map(|#singleton_value_ident| { - #input.map(move |x| (x, ::std::clone::Clone::clone(&#singleton_value_ident))) - }).into_iter().flatten(); + let #ident = { + #[inline(always)] + fn cross_singleton_guard( + mut singleton_state_mut: std::cell::RefMut<'_, Option>, + mut singleton_input: impl Iterator, + stream_input: impl Iterator, + ) -> impl Iterator + where + Singleton: ::std::clone::Clone, + { + let singleton_value_opt = match &*singleton_state_mut { + ::std::option::Option::Some(singleton_value) => Some(singleton_value.clone()), + ::std::option::Option::None => { + let singleton_value_opt = singleton_input.next(); + *singleton_state_mut = singleton_value_opt.clone(); + singleton_value_opt + } + }; + singleton_value_opt + .map(|singleton_value| { + stream_input.map(move |item| (item, ::std::clone::Clone::clone(&singleton_value))) + }) + .into_iter() + .flatten() + } + cross_singleton_guard( + #context.state_ref(#singleton_handle_ident).borrow_mut(), + #singleton_input, + #stream_input, + ) + }; }; Ok(OperatorWriteOutput { - write_prologue: Default::default(), + write_prologue, write_iterator, ..Default::default() }) From 534fe974101e38ecb847cd759dbaf503ff97f822 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Tue, 5 Nov 2024 10:15:05 -0800 Subject: [PATCH 22/74] refactor(paxos): use `usize` for slot numbers (#1521) --- hydroflow_plus/src/lib.rs | 18 + hydroflow_plus/src/location.rs | 45 ++ hydroflow_plus/src/singleton.rs | 28 ++ hydroflow_plus/src/stream.rs | 6 + hydroflow_plus_test/src/cluster/paxos.rs | 52 +-- hydroflow_plus_test/src/cluster/paxos_kv.rs | 65 ++- ...cluster__paxos_bench__tests__paxos_ir.snap | 439 +++++++++--------- 7 files changed, 369 insertions(+), 284 deletions(-) diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 9bd5b086d872..b0ca79be28cd 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -50,8 +50,22 @@ pub struct RuntimeContext<'a> { _phantom: PhantomData<&'a mut &'a ()>, } +impl RuntimeContext<'_> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + impl Copy for RuntimeContext<'_> {} +impl Default for RuntimeContext<'_> { + fn default() -> Self { + Self::new() + } +} + impl<'a> FreeVariable<&'a Context> for RuntimeContext<'a> { fn to_tokens(self) -> (Option, Option) { (None, Some(quote!(&context))) @@ -68,6 +82,10 @@ impl HfCompiled<'_, ID> { pub fn hydroflow_ir(&self) -> &BTreeMap { &self.hydroflow_ir } + + pub fn take_ir(self) -> BTreeMap { + self.hydroflow_ir + } } impl<'a> HfCompiled<'a, usize> { diff --git a/hydroflow_plus/src/location.rs b/hydroflow_plus/src/location.rs index 3036a0b74ec4..3e0a88f7047b 100644 --- a/hydroflow_plus/src/location.rs +++ b/hydroflow_plus/src/location.rs @@ -24,11 +24,23 @@ pub enum LocationId { ExternalProcess(usize), } +impl LocationId { + pub fn raw_id(&self) -> usize { + match self { + LocationId::Process(id) => *id, + LocationId::Cluster(id) => *id, + LocationId::ExternalProcess(id) => *id, + } + } +} + pub trait Location<'a> { fn id(&self) -> LocationId; fn flow_state(&self) -> &FlowState; + fn make_from(id: LocationId, flow_state: FlowState) -> Self; + fn spin(&self) -> Stream<(), Unbounded, Self> where Self: Sized + NoTick, @@ -346,6 +358,17 @@ impl<'a, P> Location<'a> for ExternalProcess<'a, P> { fn flow_state(&self) -> &FlowState { &self.flow_state } + + fn make_from(id: LocationId, flow_state: FlowState) -> Self { + match id { + LocationId::ExternalProcess(id) => ExternalProcess { + id, + flow_state, + _phantom: PhantomData, + }, + _ => panic!(), + } + } } impl<'a, P> ExternalProcess<'a, P> { @@ -447,6 +470,17 @@ impl<'a, P> Location<'a> for Process<'a, P> { fn flow_state(&self) -> &FlowState { &self.flow_state } + + fn make_from(id: LocationId, flow_state: FlowState) -> Self { + match id { + LocationId::Process(id) => Process { + id, + flow_state, + _phantom: PhantomData, + }, + _ => panic!(), + } + } } #[repr(transparent)] @@ -581,6 +615,17 @@ impl<'a, C> Location<'a> for Cluster<'a, C> { fn flow_state(&self) -> &FlowState { &self.flow_state } + + fn make_from(id: LocationId, flow_state: FlowState) -> Self { + match id { + LocationId::Cluster(id) => Cluster { + id, + flow_state, + _phantom: PhantomData, + }, + _ => panic!(), + } + } } pub trait CanSend<'a, To: Location<'a>>: Location<'a> { diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index d9db34a831cb..855e5860cad4 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -260,6 +260,15 @@ impl<'a, T, W, N: Location<'a>> Singleton { } impl<'a, T, N: Location<'a>> Singleton> { + // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream + pub fn into_stream(self) -> Stream> { + Stream::new( + self.location_kind, + self.flow_state, + self.ir_node.into_inner(), + ) + } + pub fn cross_singleton(self, other: Other) -> >::Out where Self: CrossResult<'a, Other>, @@ -641,6 +650,16 @@ impl<'a, T, N: Location<'a>> Optional> { ), ) } + + pub fn into_singleton(self) -> Singleton, Bounded, Tick> + where + T: Clone, + N: NoTick, + { + let self_location = N::make_from(self.location_kind, self.flow_state.clone()); + self.map(q!(|v| Some(v))) + .unwrap_or(self_location.singleton_each_tick(q!(None))) + } } impl<'a, T, N: Location<'a>> Optional> { @@ -730,6 +749,15 @@ impl<'a, T, B, N: Location<'a> + NoTick> Optional { self.latest_tick().unwrap_or(other.latest_tick()).latest() } + + pub fn into_singleton(self) -> Singleton, Unbounded, N> + where + T: Clone, + { + let self_location = N::make_from(self.location_kind, self.flow_state.clone()); + self.map(q!(|v| Some(v))) + .unwrap_or(self_location.singleton(q!(None))) + } } impl<'a, T, N: Location<'a> + NoTick> Optional { diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 55545d46ccec..39f664d889d9 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -46,6 +46,12 @@ impl<'a, L: Location<'a>> Location<'a> for Tick { fn flow_state(&self) -> &FlowState { self.l.flow_state() } + + fn make_from(id: LocationId, flow_state: FlowState) -> Self { + Tick { + l: L::make_from(id, flow_state), + } + } } /// An infinite stream of elements of type `T`. diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index b75a3957c97c..32a5074cc8ff 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -42,7 +42,7 @@ struct P1b { #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] struct P2a

{ ballot: Ballot, - slot: i32, + slot: usize, value: Option

, // might be a re-committed hole } @@ -50,7 +50,7 @@ struct P2a

{ struct P2b

{ ballot: Ballot, max_ballot: Ballot, - slot: i32, + slot: usize, value: Option

, // might be a hole } @@ -62,7 +62,7 @@ struct P2b

{ pub fn paxos_core<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, - r_to_acceptors_checkpoint: Stream<(ClusterId, i32), Unbounded, Cluster<'a, Acceptor>>, + r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Unbounded, Cluster<'a, Acceptor>>, c_to_proposers: Stream>, f: usize, i_am_leader_send_timeout: u64, @@ -70,7 +70,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( i_am_leader_check_timeout_delay_multiplier: usize, ) -> ( Stream<(), Unbounded, Cluster<'a, Proposer>>, - Stream<(i32, Option

), Unbounded, Cluster<'a, Proposer>>, + Stream<(usize, Option

), Unbounded, Cluster<'a, Proposer>>, ) { proposers .source_iter(q!(["Proposers say hello"])) @@ -83,7 +83,8 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b_forward_reference) = proposers.forward_ref::, _, _>>(); let (a_log_complete_cycle, a_log_forward_reference) = - acceptors.tick_forward_ref::>), _, _>>(); + acceptors + .tick_forward_ref::, HashMap>), _, _>>(); let (p_ballot_num, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( proposers, @@ -437,12 +438,12 @@ fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn recommit_after_leader_election<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - p_relevant_p1bs: Stream>>, Bounded, Tick>>, + p_relevant_p1bs: Stream>>, Bounded, Tick>>, p_ballot_num: Singleton>>, f: usize, ) -> ( Stream, Bounded, Tick>>, - Optional>>, + Optional>>, Stream, Bounded, Tick>>, ) { let p_id = proposers.self_id(); @@ -520,19 +521,19 @@ fn sequence_payload<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, c_to_proposers: Stream>, - r_to_acceptors_checkpoint: Stream<(ClusterId, i32), Unbounded, Cluster<'a, Acceptor>>, + r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Unbounded, Cluster<'a, Acceptor>>, p_ballot_num: Singleton>>, p_is_leader: Optional>>, - p_max_slot: Optional>>, + p_max_slot: Optional>>, p_log_to_recommit: Stream, Bounded, Tick>>, f: usize, a_max_ballot: Singleton>>, ) -> ( - Stream<(i32, Option

), Unbounded, Cluster<'a, Proposer>>, - Singleton<(i32, HashMap>), Bounded, Tick>>, + Stream<(usize, Option

), Unbounded, Cluster<'a, Proposer>>, + Singleton<(Option, HashMap>), Bounded, Tick>>, Stream, Unbounded, Cluster<'a, Proposer>>, ) { let p_to_acceptors_p2a = p_p2a( @@ -552,7 +553,6 @@ fn sequence_payload<'a, P: PaxosPayload, R>( p_to_acceptors_p2a, r_to_acceptors_checkpoint, proposers, - acceptors, f, ); @@ -563,14 +563,14 @@ fn sequence_payload<'a, P: PaxosPayload, R>( #[derive(Clone)] enum CheckpointOrP2a

{ - Checkpoint(i32), + Checkpoint(usize), P2a(P2a

), } // Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. fn p_p2a<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - p_max_slot: Optional>>, + p_max_slot: Optional>>, c_to_proposers: Stream>, p_ballot_num: Singleton>>, p_log_to_recommit: Stream, Bounded, Tick>>, @@ -578,7 +578,7 @@ fn p_p2a<'a, P: PaxosPayload>( acceptors: &Cluster<'a, Acceptor>, ) -> Stream, Unbounded, Cluster<'a, Acceptor>> { let p_id = proposers.self_id(); - let (p_next_slot_complete_cycle, p_next_slot) = proposers.tick_cycle::>(); + let (p_next_slot_complete_cycle, p_next_slot) = proposers.tick_cycle::>(); let p_next_slot_after_reconciling_p1bs = p_max_slot .map(q!(|max_slot| max_slot + 1)) .unwrap_or(proposers.singleton_each_tick(q!(0))) @@ -595,7 +595,7 @@ fn p_p2a<'a, P: PaxosPayload>( // .inspect(q!(|ballot_num| println!("{} p_indexed_payloads ballot_num: {}", context.current_tick(), ballot_num)))) .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { ballot: Ballot { num: ballot_num, proposer_id: p_id }, - slot: next_slot + index as i32, + slot: next_slot + index, value: Some(payload) })); // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); @@ -609,9 +609,7 @@ fn p_p2a<'a, P: PaxosPayload>( let p_next_slot_after_sending_payloads = p_num_payloads .clone() .cross_singleton(p_next_slot.clone()) - .map(q!( - |(num_payloads, next_slot)| next_slot + num_payloads as i32 - )); + .map(q!(|(num_payloads, next_slot)| next_slot + num_payloads)); let p_new_next_slot = p_next_slot_after_reconciling_p1bs // .inspect(q!(|slot| println!("{} p_new_next_slot_after_reconciling_p1bs: {:?}", context.current_tick(), slot))) @@ -627,12 +625,11 @@ fn p_p2a<'a, P: PaxosPayload>( fn acceptor_p2<'a, P: PaxosPayload, R>( a_max_ballot: Singleton>>, p_to_acceptors_p2a: Stream, Unbounded, Cluster<'a, Acceptor>>, - r_to_acceptors_checkpoint: Stream<(ClusterId, i32), Unbounded, Cluster<'a, Acceptor>>, + r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Unbounded, Cluster<'a, Acceptor>>, proposers: &Cluster<'a, Proposer>, - acceptors: &Cluster<'a, Acceptor>, f: usize, ) -> ( - Singleton<(i32, HashMap>), Bounded, Tick>>, + Singleton<(Option, HashMap>), Bounded, Tick>>, Stream, Unbounded, Cluster<'a, Proposer>>, ) { let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(); @@ -658,7 +655,6 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( .continue_if(a_checkpoints_quorum_reached) .map(q!(|(_sender, seq)| seq)) .min() - .unwrap_or(acceptors.singleton_each_tick(q!(-1))) .delta() .map(q!(|min_seq| CheckpointOrP2a::Checkpoint(min_seq))); // .inspect(q!(|(min_seq, p2a): &(i32, P2a)| println!("Acceptor new checkpoint: {:?}", min_seq))); @@ -677,19 +673,19 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( .union(a_new_checkpoint.into_stream()) .persist() .fold( - q!(|| (-1, HashMap::new())), + q!(|| (None, HashMap::new())), q!(|(prev_checkpoint, log), checkpoint_or_p2a| { match checkpoint_or_p2a { CheckpointOrP2a::Checkpoint(new_checkpoint) => { // This is a checkpoint message. Delete all entries up to the checkpoint - for slot in *prev_checkpoint..new_checkpoint { + for slot in (prev_checkpoint.unwrap_or(0))..new_checkpoint { log.remove(&slot); } - *prev_checkpoint = new_checkpoint; + *prev_checkpoint = Some(new_checkpoint); } CheckpointOrP2a::P2a(p2a) => { // This is a regular p2a message. Insert it into the log if it is not checkpointed and has a higher ballot than what was there before - if p2a.slot > *prev_checkpoint + if prev_checkpoint.map(|prev| p2a.slot > prev).unwrap_or(true) && log .get(&p2a.slot) .map(|prev_p2a: &LogValue<_>| p2a.ballot > prev_p2a.ballot) @@ -728,7 +724,7 @@ fn p_p2b<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, a_to_proposers_p2b: Stream, Unbounded, Cluster<'a, Proposer>>, f: usize, -) -> Stream<(i32, Option

), Unbounded, Cluster<'a, Proposer>> { +) -> Stream<(usize, Option

), Unbounded, Cluster<'a, Proposer>> { let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposers.tick_cycle(); let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposers.tick_cycle(); let p_p2b = a_to_proposers_p2b.tick_batch().union(p_persisted_p2bs); diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 55282bd5b502..8cdd3c7db5de 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -26,7 +26,7 @@ pub struct KvPayload { #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] pub struct SequencedKv { // Note: Important that seq is the first member of the struct for sorting - pub seq: i32, + pub seq: usize, pub kv: Option>, } @@ -95,39 +95,36 @@ pub fn replica<'a, K: KvKey, V: KvValue>( p_to_replicas: Stream, Unbounded, Cluster<'a, Replica>>, checkpoint_frequency: usize, ) -> ( - Stream>, + Stream>, Stream, Unbounded, Cluster<'a, Replica>>, ) { let (r_buffered_payloads_complete_cycle, r_buffered_payloads) = replicas.tick_cycle(); // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); let r_sorted_payloads = p_to_replicas - .clone() .tick_batch() .union(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet .sort(); // Create a cycle since we'll use this seq before we define it let (r_highest_seq_complete_cycle, r_highest_seq) = - replicas.tick_cycle::>(); - let empty_slot = replicas.singleton_first_tick(q!(-1)); - // Either the max sequence number executed so far or -1. Need to union otherwise r_highest_seq is empty and joins with it will fail - let r_highest_seq_with_default = r_highest_seq.union(empty_slot); + replicas.tick_cycle::>(); // Find highest the sequence number of any payload that can be processed in this tick. This is the payload right before a hole. let r_highest_seq_processable_payload = r_sorted_payloads .clone() - .cross_singleton(r_highest_seq_with_default) + .cross_singleton(r_highest_seq.into_singleton()) .fold( - q!(|| -1), + q!(|| None), q!(|filled_slot, (sorted_payload, highest_seq)| { - // Note: This function only works if the input is sorted on seq. - let next_slot = std::cmp::max(*filled_slot, highest_seq); - - *filled_slot = if sorted_payload.seq == next_slot + 1 { - sorted_payload.seq - } else { - *filled_slot - }; + let expected_next_slot = std::cmp::max( + filled_slot.map(|v| v + 1).unwrap_or(0), + highest_seq.map(|v| v + 1).unwrap_or(0), + ); + + if sorted_payload.seq == expected_next_slot { + *filled_slot = Some(sorted_payload.seq); + } }), - ); + ) + .filter_map(q!(|v| v)); // Find all payloads that can and cannot be processed in this tick. let r_processable_payloads = r_sorted_payloads .clone() @@ -149,30 +146,28 @@ pub fn replica<'a, K: KvKey, V: KvValue>( let r_kv_store = r_processable_payloads .clone() .persist() // Optimization: all_ticks() + fold() = fold, where the state of the previous fold is saved and persisted values are deleted. - .fold(q!(|| (HashMap::new(), -1)), q!(|(kv_store, last_seq), payload| { + .fold(q!(|| (HashMap::new(), None)), q!(|(kv_store, last_seq), payload| { if let Some(kv) = payload.kv { kv_store.insert(kv.key, kv.value); } - debug_assert!(payload.seq == *last_seq + 1, "Hole in log between seq {} and {}", *last_seq, payload.seq); - *last_seq = payload.seq; - // println!("Replica kv store: {:?}", kv_store); + + debug_assert!(payload.seq == (last_seq.map(|s| s + 1).unwrap_or(0)), "Hole in log between seq {:?} and {}", *last_seq, payload.seq); + *last_seq = Some(payload.seq); })); // Update the highest seq for the next tick - let r_new_highest_seq = r_kv_store.map(q!(|(_kv_store, highest_seq)| highest_seq)); - r_highest_seq_complete_cycle.complete_next_tick(r_new_highest_seq.clone().into()); + let r_new_highest_seq = r_kv_store.filter_map(q!(|(_kv_store, highest_seq)| highest_seq)); + r_highest_seq_complete_cycle.complete_next_tick(r_new_highest_seq.clone()); // Send checkpoints to the acceptors when we've processed enough payloads let (r_checkpointed_seqs_complete_cycle, r_checkpointed_seqs) = - replicas.tick_cycle::>(); - let r_max_checkpointed_seq = r_checkpointed_seqs - .persist() - .max() - .unwrap_or(replicas.singleton(q!(-1)).latest_tick()); + replicas.tick_cycle::>(); + let r_max_checkpointed_seq = r_checkpointed_seqs.persist().max().into_singleton(); let r_checkpoint_seq_new = r_max_checkpointed_seq .cross_singleton(r_new_highest_seq) .filter_map(q!( - move |(max_checkpointed_seq, new_highest_seq)| if new_highest_seq - max_checkpointed_seq - >= checkpoint_frequency as i32 + move |(max_checkpointed_seq, new_highest_seq)| if max_checkpointed_seq + .map(|m| new_highest_seq - m >= checkpoint_frequency) + .unwrap_or(true) { Some(new_highest_seq) } else { @@ -182,8 +177,8 @@ pub fn replica<'a, K: KvKey, V: KvValue>( r_checkpointed_seqs_complete_cycle.complete_next_tick(r_checkpoint_seq_new.clone()); // Tell clients that the payload has been committed. All ReplicaPayloads contain the client's machine ID (to string) as value. - ( - r_checkpoint_seq_new.all_ticks(), - p_to_replicas.filter_map(q!(|t| t.kv)), - ) + let r_to_clients = r_processable_payloads + .filter_map(q!(|payload| payload.kv)) + .all_ticks(); + (r_checkpoint_seq_new.all_ticks(), r_to_clients) } diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 711533647bc3..2a93b6e9c6f4 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -44,7 +44,7 @@ expression: built.ir() Union( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_1, @@ -211,7 +211,7 @@ expression: built.ir() ), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 1, @@ -226,7 +226,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -237,13 +237,13 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), input: CrossSingleton( CrossSingleton( Tee { @@ -378,7 +378,7 @@ expression: built.ir() }, ), Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -409,16 +409,16 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), input: CrossSingleton( Persist( Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), input: Tee { inner: , }, @@ -462,26 +462,26 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , ()) , i32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | max_slot + 1 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | max_slot + 1 }), input: Tee { inner: : Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { inner: , }, @@ -509,7 +509,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : CycleSource { ident: Ident { @@ -526,7 +526,7 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads as i32 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads }), input: CrossSingleton( Tee { inner: : Fold { @@ -534,7 +534,7 @@ expression: built.ir() acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , i32) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index as i32 , value : Some (payload) } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index , value : Some (payload) } }), input: CrossSingleton( CrossSingleton( Enumerate( @@ -619,18 +619,18 @@ expression: built.ir() input: DeferTick( Difference( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (i32 , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = Some (value) ; } }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { inner: : Union( Tee { @@ -713,7 +713,7 @@ expression: built.ir() input: CrossSingleton( Union( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } } }), input: CrossSingleton( Tee { inner: , @@ -724,17 +724,17 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : None } }), input: CrossSingleton( Difference( FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: ops :: Range < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: ops :: Range < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), input: Tee { inner: , }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: , }, @@ -819,7 +819,7 @@ expression: built.ir() }, Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((i32 , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { inner: , }, @@ -837,10 +837,10 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), input: AntiJoin( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (i32 , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { inner: , }, @@ -860,8 +860,8 @@ expression: built.ir() 1, ), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (- 1 , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (i32 , std :: collections :: hash_map :: HashMap < i32 , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in * prev_checkpoint .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = new_checkpoint ; } CheckpointOrP2a :: P2a (p2a) => { if p2a . slot > * prev_checkpoint && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in (prev_checkpoint . unwrap_or (0)) .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = Some (new_checkpoint) ; } CheckpointOrP2a :: P2a (p2a) => { if prev_checkpoint . map (| prev | p2a . slot > prev) . unwrap_or (true) && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), input: Persist( Union( FilterMap { @@ -876,93 +876,81 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), input: Delta( - Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , i32) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: : ReduceKeyed { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), - input: Persist( - Network { - from_location: Cluster( - 3, - ), - from_key: None, - to_location: Cluster( - 1, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , i32) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < i32 > (& data) . unwrap () . into ()) }", - ], - }, - ), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : ReduceKeyed { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), + input: Persist( + Network { + from_location: Cluster( + 3, + ), + from_key: None, + to_location: Cluster( + 1, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , usize) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < usize > (& data) . unwrap () . into ()) }", + ], + }, ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < i32 > (& b) . unwrap ()) }", - ], - }, - ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < usize > (& b) . unwrap ()) }", + ], + }, ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 3, - ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: CycleSource { + ident: Ident { + sym: cycle_0, }, + location_kind: Cluster( + 3, + ), }, }, - ), - }, + }, + ), }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , i32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , }, }, }, - ), - }, - }, - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; - 1 } ; [e] }, - ), - location_kind: Cluster( - 1, + }, ), }, - ), - ), + }, + }, ), }, ), @@ -989,64 +977,62 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { inner: : Sort( Union( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 0, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 0, + ), + from_key: None, + to_location: Cluster( + 3, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + ], + }, ), - from_key: None, - to_location: Cluster( - 3, + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + ], + }, ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", - ], + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), + input: AntiJoin( + Tee { + inner: , }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", - ], + CycleSource { + ident: Ident { + sym: cycle_6, + }, + location_kind: Cluster( + 0, + ), }, ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), - input: AntiJoin( - Tee { - inner: , - }, - CycleSource { - ident: Ident { - sym: cycle_6, - }, - location_kind: Cluster( - 0, - ), - }, - ), - }, }, }, }, @@ -1063,32 +1049,40 @@ expression: built.ir() ), }, Tee { - inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | - 1 }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let next_slot = std :: cmp :: max (* filled_slot , highest_seq) ; * filled_slot = if sorted_payload . seq == next_slot + 1 { sorted_payload . seq } else { * filled_slot } ; } }), - input: CrossSingleton( - Tee { - inner: , - }, - Union( - CycleSource { - ident: Ident { - sym: cycle_2, - }, - location_kind: Cluster( - 3, - ), + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < usize > , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | v | v }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | None }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < usize > , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let expected_next_slot = std :: cmp :: max (filled_slot . map (| v | v + 1) . unwrap_or (0) , highest_seq . map (| v | v + 1) . unwrap_or (0) ,) ; if sorted_payload . seq == expected_next_slot { * filled_slot = Some (sorted_payload . seq) ; } } }), + input: CrossSingleton( + Tee { + inner: , }, - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos_kv :: * ; - 1 } ; [e] }, - ), - location_kind: Cluster( - 3, + Union( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < usize > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | v | Some (v) }), + input: CycleSource { + ident: Ident { + sym: cycle_2, + }, + location_kind: Cluster( + 3, + ), + }, + }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e = { use hydroflow_plus :: __staged :: singleton :: * ; None } ; [e] }, + ), + location_kind: Cluster( + 3, + ), + }, ), - }, + ), ), - ), + }, }, }, ), @@ -1105,23 +1099,23 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , i32 > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , - 1) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == * last_seq + 1 , "Hole in log between seq {} and {}" , * last_seq , payload . seq) ; * last_seq = payload . seq ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == (last_seq . map (| s | s + 1) . unwrap_or (0)) , "Hole in log between seq {:?} and {}" , * last_seq , payload . seq) ; * last_seq = Some (payload . seq) ; } }), input: Persist( Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , i32) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1142,27 +1136,30 @@ expression: built.ir() ), input: DeferTick( Tee { - inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (i32 , i32) , core :: option :: Option < i32 > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if new_highest_seq - max_checkpointed_seq >= checkpoint_frequency as i32 { Some (new_highest_seq) } else { None } }), + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , usize) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if max_checkpointed_seq . map (| m | new_highest_seq - m >= checkpoint_frequency) . unwrap_or (true) { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Union( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - CycleSource { - ident: Ident { - sym: cycle_3, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < usize > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | v | Some (v) }), + input: Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( + CycleSource { + ident: Ident { + sym: cycle_3, + }, + location_kind: Cluster( + 3, + ), }, - location_kind: Cluster( - 3, - ), - }, - ), + ), + }, }, Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos_kv :: * ; - 1 } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e = { use hydroflow_plus :: __staged :: singleton :: * ; None } ; [e] }, ), location_kind: Cluster( 3, @@ -1171,7 +1168,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1186,7 +1183,7 @@ expression: built.ir() 3, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1250,7 +1247,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Union( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1286,9 +1283,9 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | t | t . kv }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), input: Tee { - inner: , + inner: , }, }, }, @@ -1305,13 +1302,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1330,9 +1327,9 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , KvPayload { key : i as u32 , value : c_id })) }), input: Tee { - inner: : Delta( + inner: : Delta( Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { @@ -1356,10 +1353,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , cur_leader) | (cur_leader , KvPayload { key , value : c_id }) }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1378,7 +1375,7 @@ expression: built.ir() input: Union( Union( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_3, }, @@ -1392,16 +1389,16 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: , + inner: , }, }, }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1428,10 +1425,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1439,7 +1436,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: : Source { + inner: : Source { source: Stream( { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, ), @@ -1469,7 +1466,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1480,7 +1477,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1492,7 +1489,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1503,7 +1500,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), From d9634f242a97c06bdb53011bf3d75256425a1598 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Tue, 5 Nov 2024 10:18:49 -0800 Subject: [PATCH 23/74] refactor(hydroflow_plus): split up location module and store locations directly in streams (#1523) --- hydroflow_plus/src/builder/deploy.rs | 6 +- hydroflow_plus/src/builder/mod.rs | 75 +- hydroflow_plus/src/cycle.rs | 12 +- hydroflow_plus/src/lib.rs | 4 +- hydroflow_plus/src/location.rs | 702 ------------------ hydroflow_plus/src/location/can_send.rs | 76 ++ hydroflow_plus/src/location/cluster.rs | 227 ++++++ .../src/location/external_process.rs | 139 ++++ hydroflow_plus/src/location/mod.rs | 332 +++++++++ hydroflow_plus/src/location/process.rs | 41 + hydroflow_plus/src/location/tick.rs | 34 + hydroflow_plus/src/singleton.rs | 318 +++----- hydroflow_plus/src/stream.rs | 236 ++---- ...ter__compute_pi__tests__compute_pi_ir.snap | 8 +- ..._tests__compute_pi_ir@surface_graph_1.snap | 6 +- ...er__many_to_many__tests__many_to_many.snap | 4 +- ...ter__map_reduce__tests__map_reduce_ir.snap | 4 +- ..._tests__map_reduce_ir@surface_graph_0.snap | 4 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 166 ++--- ...simple_cluster__tests__simple_cluster.snap | 16 +- .../src/distributed/first_ten.rs | 3 +- 21 files changed, 1168 insertions(+), 1245 deletions(-) delete mode 100644 hydroflow_plus/src/location.rs create mode 100644 hydroflow_plus/src/location/can_send.rs create mode 100644 hydroflow_plus/src/location/cluster.rs create mode 100644 hydroflow_plus/src/location/external_process.rs create mode 100644 hydroflow_plus/src/location/mod.rs create mode 100644 hydroflow_plus/src/location/process.rs create mode 100644 hydroflow_plus/src/location/tick.rs diff --git a/hydroflow_plus/src/builder/deploy.rs b/hydroflow_plus/src/builder/deploy.rs index bc70ab3a2aa9..c345d7f7bf43 100644 --- a/hydroflow_plus/src/builder/deploy.rs +++ b/hydroflow_plus/src/builder/deploy.rs @@ -13,10 +13,10 @@ use stageleft::Quoted; use super::built::build_inner; use crate::deploy::{ExternalSpec, LocalDeploy, Node, RegisterPort}; use crate::ir::HfPlusLeaf; -use crate::location::{ - ExternalBincodeSink, ExternalBincodeStream, ExternalBytesPort, ExternalProcess, Location, - LocationId, +use crate::location::external_process::{ + ExternalBincodeSink, ExternalBincodeStream, ExternalBytesPort, }; +use crate::location::{ExternalProcess, Location, LocationId}; use crate::{Cluster, ClusterSpec, Deploy, HfCompiled, Process, ProcessSpec}; pub struct DeployFlow<'a, D: LocalDeploy<'a>> { diff --git a/hydroflow_plus/src/builder/mod.rs b/hydroflow_plus/src/builder/mod.rs index 593b7fb23ffc..e4c65a069a95 100644 --- a/hydroflow_plus/src/builder/mod.rs +++ b/hydroflow_plus/src/builder/mod.rs @@ -3,16 +3,11 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::rc::Rc; -use internal::TokenStream; -use proc_macro2::Span; -use quote::quote; -use runtime_support::FreeVariable; use stageleft::*; -use super::staging_util::get_this_crate; use crate::ir::HfPlusLeaf; use crate::location::{Cluster, ExternalProcess, Process}; -use crate::{ClusterId, RuntimeContext}; +use crate::RuntimeContext; pub mod built; pub mod deploy; @@ -32,74 +27,6 @@ pub struct FlowStateInner { pub type FlowState = Rc>; -pub struct ClusterIds<'a, C> { - pub(crate) id: usize, - pub(crate) _phantom: PhantomData<&'a mut &'a C>, -} - -impl Clone for ClusterIds<'_, C> { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for ClusterIds<'_, C> {} - -impl<'a, C> FreeVariable<&'a Vec>> for ClusterIds<'a, C> { - fn to_tokens(self) -> (Option, Option) - where - Self: Sized, - { - let ident = syn::Ident::new( - &format!("__hydroflow_plus_cluster_ids_{}", self.id), - Span::call_site(), - ); - let root = get_this_crate(); - let c_type = quote_type::(); - ( - None, - Some( - quote! { unsafe { ::std::mem::transmute::<_, &::std::vec::Vec<#root::ClusterId<#c_type>>>(#ident) } }, - ), - ) - } -} - -impl<'a, C> Quoted<'a, &'a Vec>> for ClusterIds<'a, C> {} - -pub(crate) struct ClusterSelfId<'a, C> { - pub(crate) id: usize, - pub(crate) _phantom: PhantomData<&'a mut &'a C>, -} - -impl Clone for ClusterSelfId<'_, C> { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for ClusterSelfId<'_, C> {} - -impl FreeVariable> for ClusterSelfId<'_, C> { - fn to_tokens(self) -> (Option, Option) - where - Self: Sized, - { - let ident = syn::Ident::new( - &format!("__hydroflow_plus_cluster_self_id_{}", self.id), - Span::call_site(), - ); - let root = get_this_crate(); - let c_type: syn::Type = quote_type::(); - ( - None, - Some(quote! { #root::ClusterId::<#c_type>::from_raw(#ident) }), - ) - } -} - -impl<'a, C> Quoted<'a, ClusterId> for ClusterSelfId<'a, C> {} - pub struct FlowBuilder<'a> { flow_state: FlowState, nodes: RefCell>, diff --git a/hydroflow_plus/src/cycle.rs b/hydroflow_plus/src/cycle.rs index dcf8bdeb5a29..5da633542875 100644 --- a/hydroflow_plus/src/cycle.rs +++ b/hydroflow_plus/src/cycle.rs @@ -1,7 +1,6 @@ use std::marker::PhantomData; -use crate::builder::FlowState; -use crate::location::{Location, LocationId}; +use crate::location::Location; pub struct TickCycle {} @@ -16,18 +15,13 @@ pub trait CycleComplete<'a, T> { pub trait CycleCollection<'a, T>: CycleComplete<'a, T> { type Location: Location<'a>; - fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self; + fn create_source(ident: syn::Ident, location: Self::Location) -> Self; } pub trait CycleCollectionWithInitial<'a, T>: CycleComplete<'a, T> { type Location: Location<'a>; - fn create_source( - ident: syn::Ident, - flow_state: FlowState, - initial: Self, - l: LocationId, - ) -> Self; + fn create_source(ident: syn::Ident, initial: Self, location: Self::Location) -> Self; } /// Represents a forward reference in the graph that will be fulfilled diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index b0ca79be28cd..a0fe5f3c9619 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -19,13 +19,13 @@ pub mod runtime_support { } pub mod stream; -pub use stream::{Bounded, Stream, Tick, Unbounded}; +pub use stream::{Bounded, Stream, Unbounded}; pub mod singleton; pub use singleton::{Optional, Singleton}; pub mod location; -pub use location::{Cluster, ClusterId, Location, Process}; +pub use location::{Cluster, ClusterId, Location, Process, Tick}; pub mod deploy; pub use deploy::{ClusterSpec, Deploy, ProcessSpec}; diff --git a/hydroflow_plus/src/location.rs b/hydroflow_plus/src/location.rs deleted file mode 100644 index 3e0a88f7047b..000000000000 --- a/hydroflow_plus/src/location.rs +++ /dev/null @@ -1,702 +0,0 @@ -use std::fmt::{Debug, Display}; -use std::hash::Hash; -use std::marker::PhantomData; -use std::time::Duration; - -use hydroflow::bytes::Bytes; -use hydroflow::futures::stream::Stream as FuturesStream; -use hydroflow::{tokio, tokio_stream}; -use proc_macro2::Span; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use stageleft::{q, quote_type, Quoted}; - -use super::builder::{ClusterIds, ClusterSelfId, FlowState}; -use crate::cycle::{CycleCollection, CycleCollectionWithInitial, DeferTick, HfCycle, TickCycle}; -use crate::ir::{HfPlusNode, HfPlusSource}; -use crate::stream::NoTick; -use crate::{Bounded, HfForwardRef, Optional, Singleton, Stream, Tick, Unbounded}; - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub enum LocationId { - Process(usize), - Cluster(usize), - ExternalProcess(usize), -} - -impl LocationId { - pub fn raw_id(&self) -> usize { - match self { - LocationId::Process(id) => *id, - LocationId::Cluster(id) => *id, - LocationId::ExternalProcess(id) => *id, - } - } -} - -pub trait Location<'a> { - fn id(&self) -> LocationId; - - fn flow_state(&self) -> &FlowState; - - fn make_from(id: LocationId, flow_state: FlowState) -> Self; - - fn spin(&self) -> Stream<(), Unbounded, Self> - where - Self: Sized + NoTick, - { - Stream::new( - self.id(), - self.flow_state().clone(), - HfPlusNode::Persist(Box::new(HfPlusNode::Source { - source: HfPlusSource::Spin(), - location_kind: self.id(), - })), - ) - } - - fn spin_batch( - &self, - batch_size: impl Quoted<'a, usize> + Copy + 'a, - ) -> Stream<(), Bounded, Tick> - where - Self: Sized + NoTick, - { - self.spin() - .flat_map(q!(move |_| 0..batch_size)) - .map(q!(|_| ())) - .tick_batch() - } - - fn source_stream + Unpin>( - &self, - e: impl Quoted<'a, E>, - ) -> Stream - where - Self: Sized + NoTick, - { - let e = e.splice_untyped(); - - Stream::new( - self.id(), - self.flow_state().clone(), - HfPlusNode::Persist(Box::new(HfPlusNode::Source { - source: HfPlusSource::Stream(e.into()), - location_kind: self.id(), - })), - ) - } - - fn source_iter>( - &self, - e: impl Quoted<'a, E>, - ) -> Stream - where - Self: Sized + NoTick, - { - let e = e.splice_untyped(); - - Stream::new( - self.id(), - self.flow_state().clone(), - HfPlusNode::Persist(Box::new(HfPlusNode::Source { - source: HfPlusSource::Iter(e.into()), - location_kind: self.id(), - })), - ) - } - - fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton - where - Self: Sized + NoTick, - { - let e_arr = q!([e]); - let e = e_arr.splice_untyped(); - - // we do a double persist here because if the singleton shows up on every tick, - // we first persist the source so that we store that value and then persist again - // so that it grows every tick - Singleton::new( - self.id(), - self.flow_state().clone(), - HfPlusNode::Persist(Box::new(HfPlusNode::Persist(Box::new( - HfPlusNode::Source { - source: HfPlusSource::Iter(e.into()), - location_kind: self.id(), - }, - )))), - ) - } - - fn singleton_each_tick( - &self, - e: impl Quoted<'a, T>, - ) -> Singleton> - where - Self: Sized + NoTick, - { - self.singleton(e).latest_tick() - } - - fn singleton_first_tick( - &self, - e: impl Quoted<'a, T>, - ) -> Optional> - where - Self: Sized + NoTick, - { - let e_arr = q!([e]); - let e = e_arr.splice_untyped(); - - Optional::new( - self.id(), - self.flow_state().clone(), - HfPlusNode::Source { - source: HfPlusSource::Iter(e.into()), - location_kind: self.id(), - }, - ) - } - - fn source_interval( - &self, - interval: impl Quoted<'a, Duration> + Copy + 'a, - ) -> Stream - where - Self: Sized + NoTick, - { - self.source_stream(q!(tokio_stream::wrappers::IntervalStream::new( - tokio::time::interval(interval) - ))) - } - - fn source_interval_delayed( - &self, - delay: impl Quoted<'a, Duration> + Copy + 'a, - interval: impl Quoted<'a, Duration> + Copy + 'a, - ) -> Stream - where - Self: Sized + NoTick, - { - self.source_stream(q!(tokio_stream::wrappers::IntervalStream::new( - tokio::time::interval_at(tokio::time::Instant::now() + delay, interval) - ))) - } - - fn forward_ref>( - &self, - ) -> (HfForwardRef<'a, (), S>, S) - where - Self: NoTick, - { - let next_id = { - let on_id = match self.id() { - LocationId::Process(id) => id, - LocationId::Cluster(id) => id, - LocationId::ExternalProcess(_) => panic!(), - }; - - let mut flow_state = self.flow_state().borrow_mut(); - let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); - - let id = *next_id_entry; - *next_id_entry += 1; - id - }; - - let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); - - ( - HfForwardRef { - ident: ident.clone(), - _phantom: PhantomData, - }, - S::create_source(ident, self.flow_state().clone(), self.id()), - ) - } - - fn tick_forward_ref>( - &self, - ) -> (HfForwardRef<'a, TickCycle, S>, S) - where - Self: NoTick, - { - let next_id = { - let on_id = match self.id() { - LocationId::Process(id) => id, - LocationId::Cluster(id) => id, - LocationId::ExternalProcess(_) => panic!(), - }; - - let mut flow_state = self.flow_state().borrow_mut(); - let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); - - let id = *next_id_entry; - *next_id_entry += 1; - id - }; - - let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); - - ( - HfForwardRef { - ident: ident.clone(), - _phantom: PhantomData, - }, - S::create_source(ident, self.flow_state().clone(), self.id()), - ) - } - - fn tick_cycle + DeferTick>( - &self, - ) -> (HfCycle<'a, S>, S) - where - Self: NoTick, - { - let next_id = { - let on_id = match self.id() { - LocationId::Process(id) => id, - LocationId::Cluster(id) => id, - LocationId::ExternalProcess(_) => panic!(), - }; - - let mut flow_state = self.flow_state().borrow_mut(); - let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); - - let id = *next_id_entry; - *next_id_entry += 1; - id - }; - - let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); - - ( - HfCycle { - ident: ident.clone(), - _phantom: PhantomData, - }, - S::create_source(ident, self.flow_state().clone(), self.id()), - ) - } - - fn tick_cycle_with_initial< - S: CycleCollectionWithInitial<'a, TickCycle, Location = Self> + DeferTick, - >( - &self, - initial: S, - ) -> (HfCycle<'a, S>, S) - where - Self: NoTick, - { - let next_id = { - let on_id = match self.id() { - LocationId::Process(id) => id, - LocationId::Cluster(id) => id, - LocationId::ExternalProcess(_) => panic!(), - }; - - let mut flow_state = self.flow_state().borrow_mut(); - let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); - - let id = *next_id_entry; - *next_id_entry += 1; - id - }; - - let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); - - ( - HfCycle { - ident: ident.clone(), - _phantom: PhantomData, - }, - S::create_source(ident, self.flow_state().clone(), initial, self.id()), - ) - } -} - -pub struct ExternalBytesPort { - pub(crate) process_id: usize, - pub(crate) port_id: usize, -} - -pub struct ExternalBincodeSink { - pub(crate) process_id: usize, - pub(crate) port_id: usize, - pub(crate) _phantom: PhantomData, -} - -pub struct ExternalBincodeStream { - pub(crate) process_id: usize, - pub(crate) port_id: usize, - pub(crate) _phantom: PhantomData, -} - -pub struct ExternalProcess<'a, P> { - pub(crate) id: usize, - - pub(crate) flow_state: FlowState, - - pub(crate) _phantom: PhantomData<&'a &'a mut P>, -} - -impl

Clone for ExternalProcess<'_, P> { - fn clone(&self) -> Self { - ExternalProcess { - id: self.id, - flow_state: self.flow_state.clone(), - _phantom: PhantomData, - } - } -} - -impl<'a, P> Location<'a> for ExternalProcess<'a, P> { - fn id(&self) -> LocationId { - LocationId::ExternalProcess(self.id) - } - - fn flow_state(&self) -> &FlowState { - &self.flow_state - } - - fn make_from(id: LocationId, flow_state: FlowState) -> Self { - match id { - LocationId::ExternalProcess(id) => ExternalProcess { - id, - flow_state, - _phantom: PhantomData, - }, - _ => panic!(), - } - } -} - -impl<'a, P> ExternalProcess<'a, P> { - pub fn source_external_bytes + NoTick>( - &self, - to: &L, - ) -> (ExternalBytesPort, Stream) { - let next_external_port_id = { - let mut flow_state = self.flow_state.borrow_mut(); - let id = flow_state.next_external_out; - flow_state.next_external_out += 1; - id - }; - - ( - ExternalBytesPort { - process_id: self.id, - port_id: next_external_port_id, - }, - Stream::new( - to.id(), - self.flow_state().clone(), - HfPlusNode::Persist(Box::new(HfPlusNode::Network { - from_location: LocationId::ExternalProcess(self.id), - from_key: Some(next_external_port_id), - to_location: to.id(), - to_key: None, - serialize_pipeline: None, - instantiate_fn: crate::ir::DebugInstantiate::Building(), - deserialize_pipeline: Some(syn::parse_quote!(map(|b| b.unwrap().freeze()))), - input: Box::new(HfPlusNode::Source { - source: HfPlusSource::ExternalNetwork(), - location_kind: LocationId::ExternalProcess(self.id), - }), - })), - ), - ) - } - - pub fn source_external_bincode + NoTick, T: Serialize + DeserializeOwned>( - &self, - to: &L, - ) -> (ExternalBincodeSink, Stream) { - let next_external_port_id = { - let mut flow_state = self.flow_state.borrow_mut(); - let id = flow_state.next_external_out; - flow_state.next_external_out += 1; - id - }; - - ( - ExternalBincodeSink { - process_id: self.id, - port_id: next_external_port_id, - _phantom: PhantomData, - }, - Stream::new( - to.id(), - self.flow_state().clone(), - HfPlusNode::Persist(Box::new(HfPlusNode::Network { - from_location: LocationId::ExternalProcess(self.id), - from_key: Some(next_external_port_id), - to_location: to.id(), - to_key: None, - serialize_pipeline: None, - instantiate_fn: crate::ir::DebugInstantiate::Building(), - deserialize_pipeline: Some(crate::stream::deserialize_bincode::(None)), - input: Box::new(HfPlusNode::Source { - source: HfPlusSource::ExternalNetwork(), - location_kind: LocationId::ExternalProcess(self.id), - }), - })), - ), - ) - } -} - -pub struct Process<'a, P> { - pub(crate) id: usize, - pub(crate) flow_state: FlowState, - pub(crate) _phantom: PhantomData<&'a &'a mut P>, -} - -impl

Clone for Process<'_, P> { - fn clone(&self) -> Self { - Process { - id: self.id, - flow_state: self.flow_state.clone(), - _phantom: PhantomData, - } - } -} - -impl<'a, P> Location<'a> for Process<'a, P> { - fn id(&self) -> LocationId { - LocationId::Process(self.id) - } - - fn flow_state(&self) -> &FlowState { - &self.flow_state - } - - fn make_from(id: LocationId, flow_state: FlowState) -> Self { - match id { - LocationId::Process(id) => Process { - id, - flow_state, - _phantom: PhantomData, - }, - _ => panic!(), - } - } -} - -#[repr(transparent)] -pub struct ClusterId { - pub raw_id: u32, - pub(crate) _phantom: PhantomData, -} - -impl Debug for ClusterId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "ClusterId::<{}>({})", - std::any::type_name::(), - self.raw_id - ) - } -} - -impl Display for ClusterId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "ClusterId::<{}>({})", - std::any::type_name::(), - self.raw_id - ) - } -} - -impl Clone for ClusterId { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for ClusterId {} - -impl Serialize for ClusterId { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - self.raw_id.serialize(serializer) - } -} - -impl<'de, C> Deserialize<'de> for ClusterId { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - u32::deserialize(deserializer).map(|id| ClusterId { - raw_id: id, - _phantom: PhantomData, - }) - } -} - -impl PartialEq for ClusterId { - fn eq(&self, other: &Self) -> bool { - self.raw_id == other.raw_id - } -} - -impl Eq for ClusterId {} - -impl PartialOrd for ClusterId { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for ClusterId { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.raw_id.cmp(&other.raw_id) - } -} - -impl Hash for ClusterId { - fn hash(&self, state: &mut H) { - self.raw_id.hash(state) - } -} - -impl ClusterId { - pub fn from_raw(id: u32) -> Self { - ClusterId { - raw_id: id, - _phantom: PhantomData, - } - } -} - -pub struct Cluster<'a, C> { - pub(crate) id: usize, - pub(crate) flow_state: FlowState, - pub(crate) _phantom: PhantomData<&'a &'a mut C>, -} - -impl<'a, C> Cluster<'a, C> { - pub fn self_id(&self) -> impl Quoted<'a, ClusterId> + Copy + 'a { - ClusterSelfId { - id: self.id, - _phantom: PhantomData, - } - } - - pub fn members(&self) -> impl Quoted<'a, &'a Vec>> + Copy + 'a { - ClusterIds { - id: self.id, - _phantom: PhantomData, - } - } -} - -impl Clone for Cluster<'_, C> { - fn clone(&self) -> Self { - Cluster { - id: self.id, - flow_state: self.flow_state.clone(), - _phantom: PhantomData, - } - } -} - -impl<'a, C> Location<'a> for Cluster<'a, C> { - fn id(&self) -> LocationId { - LocationId::Cluster(self.id) - } - - fn flow_state(&self) -> &FlowState { - &self.flow_state - } - - fn make_from(id: LocationId, flow_state: FlowState) -> Self { - match id { - LocationId::Cluster(id) => Cluster { - id, - flow_state, - _phantom: PhantomData, - }, - _ => panic!(), - } - } -} - -pub trait CanSend<'a, To: Location<'a>>: Location<'a> { - type In; - type Out; - - fn is_demux() -> bool; - fn tagged_type() -> Option; -} - -impl<'a, P1, P2> CanSend<'a, Process<'a, P2>> for Process<'a, P1> { - type In = T; - type Out = T; - - fn is_demux() -> bool { - false - } - - fn tagged_type() -> Option { - None - } -} - -impl<'a, P1, C2> CanSend<'a, Cluster<'a, C2>> for Process<'a, P1> { - type In = (ClusterId, T); - type Out = T; - - fn is_demux() -> bool { - true - } - - fn tagged_type() -> Option { - None - } -} - -impl<'a, C1, P2> CanSend<'a, Process<'a, P2>> for Cluster<'a, C1> { - type In = T; - type Out = (ClusterId, T); - - fn is_demux() -> bool { - false - } - - fn tagged_type() -> Option { - Some(quote_type::()) - } -} - -impl<'a, C1, C2> CanSend<'a, Cluster<'a, C2>> for Cluster<'a, C1> { - type In = (ClusterId, T); - type Out = (ClusterId, T); - - fn is_demux() -> bool { - true - } - - fn tagged_type() -> Option { - Some(quote_type::()) - } -} - -impl<'a, P1, E2> CanSend<'a, ExternalProcess<'a, E2>> for Process<'a, P1> { - type In = T; - type Out = T; - - fn is_demux() -> bool { - false - } - - fn tagged_type() -> Option { - None - } -} diff --git a/hydroflow_plus/src/location/can_send.rs b/hydroflow_plus/src/location/can_send.rs new file mode 100644 index 000000000000..ddbec2719872 --- /dev/null +++ b/hydroflow_plus/src/location/can_send.rs @@ -0,0 +1,76 @@ +use stageleft::quote_type; + +use super::{Cluster, ClusterId, ExternalProcess, Location, Process}; + +pub trait CanSend<'a, To: Location<'a>>: Location<'a> { + type In; + type Out; + + fn is_demux() -> bool; + fn tagged_type() -> Option; +} + +impl<'a, P1, P2> CanSend<'a, Process<'a, P2>> for Process<'a, P1> { + type In = T; + type Out = T; + + fn is_demux() -> bool { + false + } + + fn tagged_type() -> Option { + None + } +} + +impl<'a, P1, C2> CanSend<'a, Cluster<'a, C2>> for Process<'a, P1> { + type In = (ClusterId, T); + type Out = T; + + fn is_demux() -> bool { + true + } + + fn tagged_type() -> Option { + None + } +} + +impl<'a, C1, P2> CanSend<'a, Process<'a, P2>> for Cluster<'a, C1> { + type In = T; + type Out = (ClusterId, T); + + fn is_demux() -> bool { + false + } + + fn tagged_type() -> Option { + Some(quote_type::()) + } +} + +impl<'a, C1, C2> CanSend<'a, Cluster<'a, C2>> for Cluster<'a, C1> { + type In = (ClusterId, T); + type Out = (ClusterId, T); + + fn is_demux() -> bool { + true + } + + fn tagged_type() -> Option { + Some(quote_type::()) + } +} + +impl<'a, P1, E2> CanSend<'a, ExternalProcess<'a, E2>> for Process<'a, P1> { + type In = T; + type Out = T; + + fn is_demux() -> bool { + false + } + + fn tagged_type() -> Option { + None + } +} diff --git a/hydroflow_plus/src/location/cluster.rs b/hydroflow_plus/src/location/cluster.rs new file mode 100644 index 000000000000..e18b5f2af1b8 --- /dev/null +++ b/hydroflow_plus/src/location/cluster.rs @@ -0,0 +1,227 @@ +use std::fmt::{Debug, Display}; +use std::hash::Hash; +use std::marker::PhantomData; + +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use serde::{Deserialize, Serialize}; +use stageleft::runtime_support::FreeVariable; +use stageleft::{quote_type, Quoted}; + +// TODO(shadaj): have to use super due to stageleft limitations +use super::super::staging_util::get_this_crate; +use super::{Location, LocationId}; +use crate::builder::FlowState; + +pub struct Cluster<'a, C> { + pub(crate) id: usize, + pub(crate) flow_state: FlowState, + pub(crate) _phantom: PhantomData<&'a &'a mut C>, +} + +impl<'a, C> Cluster<'a, C> { + pub fn self_id(&self) -> impl Quoted<'a, ClusterId> + Copy + 'a { + ClusterSelfId { + id: self.id, + _phantom: PhantomData, + } + } + + pub fn members(&self) -> impl Quoted<'a, &'a Vec>> + Copy + 'a { + ClusterIds { + id: self.id, + _phantom: PhantomData, + } + } +} + +impl Clone for Cluster<'_, C> { + fn clone(&self) -> Self { + Cluster { + id: self.id, + flow_state: self.flow_state.clone(), + _phantom: PhantomData, + } + } +} + +impl<'a, C> Location<'a> for Cluster<'a, C> { + fn id(&self) -> LocationId { + LocationId::Cluster(self.id) + } + + fn flow_state(&self) -> &FlowState { + &self.flow_state + } + + fn make_from(id: LocationId, flow_state: FlowState) -> Self { + match id { + LocationId::Cluster(id) => Cluster { + id, + flow_state, + _phantom: PhantomData, + }, + _ => panic!(), + } + } +} + +#[repr(transparent)] +pub struct ClusterId { + pub raw_id: u32, + pub(crate) _phantom: PhantomData, +} + +impl Debug for ClusterId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ClusterId::<{}>({})", + std::any::type_name::(), + self.raw_id + ) + } +} + +impl Display for ClusterId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ClusterId::<{}>({})", + std::any::type_name::(), + self.raw_id + ) + } +} + +impl Clone for ClusterId { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for ClusterId {} + +impl Serialize for ClusterId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + self.raw_id.serialize(serializer) + } +} + +impl<'de, C> Deserialize<'de> for ClusterId { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + u32::deserialize(deserializer).map(|id| ClusterId { + raw_id: id, + _phantom: PhantomData, + }) + } +} + +impl PartialEq for ClusterId { + fn eq(&self, other: &Self) -> bool { + self.raw_id == other.raw_id + } +} + +impl Eq for ClusterId {} + +impl PartialOrd for ClusterId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ClusterId { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.raw_id.cmp(&other.raw_id) + } +} + +impl Hash for ClusterId { + fn hash(&self, state: &mut H) { + self.raw_id.hash(state) + } +} + +impl ClusterId { + pub fn from_raw(id: u32) -> Self { + ClusterId { + raw_id: id, + _phantom: PhantomData, + } + } +} + +pub struct ClusterIds<'a, C> { + pub(crate) id: usize, + pub(crate) _phantom: PhantomData<&'a mut &'a C>, +} + +impl Clone for ClusterIds<'_, C> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for ClusterIds<'_, C> {} + +impl<'a, C> FreeVariable<&'a Vec>> for ClusterIds<'a, C> { + fn to_tokens(self) -> (Option, Option) + where + Self: Sized, + { + let ident = syn::Ident::new( + &format!("__hydroflow_plus_cluster_ids_{}", self.id), + Span::call_site(), + ); + let root = get_this_crate(); + let c_type = quote_type::(); + ( + None, + Some( + quote! { unsafe { ::std::mem::transmute::<_, &::std::vec::Vec<#root::ClusterId<#c_type>>>(#ident) } }, + ), + ) + } +} + +impl<'a, C> Quoted<'a, &'a Vec>> for ClusterIds<'a, C> {} + +pub struct ClusterSelfId<'a, C> { + pub(crate) id: usize, + pub(crate) _phantom: PhantomData<&'a mut &'a C>, +} + +impl Clone for ClusterSelfId<'_, C> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for ClusterSelfId<'_, C> {} + +impl FreeVariable> for ClusterSelfId<'_, C> { + fn to_tokens(self) -> (Option, Option) + where + Self: Sized, + { + let ident = syn::Ident::new( + &format!("__hydroflow_plus_cluster_self_id_{}", self.id), + Span::call_site(), + ); + let root = get_this_crate(); + let c_type: syn::Type = quote_type::(); + ( + None, + Some(quote! { #root::ClusterId::<#c_type>::from_raw(#ident) }), + ) + } +} + +impl<'a, C> Quoted<'a, ClusterId> for ClusterSelfId<'a, C> {} diff --git a/hydroflow_plus/src/location/external_process.rs b/hydroflow_plus/src/location/external_process.rs new file mode 100644 index 000000000000..4d992d36f5af --- /dev/null +++ b/hydroflow_plus/src/location/external_process.rs @@ -0,0 +1,139 @@ +use std::marker::PhantomData; + +use hydroflow::bytes::Bytes; +use serde::de::DeserializeOwned; +use serde::Serialize; + +use super::{Location, LocationId, NoTick}; +use crate::builder::FlowState; +use crate::ir::{HfPlusNode, HfPlusSource}; +use crate::{Stream, Unbounded}; + +pub struct ExternalBytesPort { + pub(crate) process_id: usize, + pub(crate) port_id: usize, +} + +pub struct ExternalBincodeSink { + pub(crate) process_id: usize, + pub(crate) port_id: usize, + pub(crate) _phantom: PhantomData, +} + +pub struct ExternalBincodeStream { + pub(crate) process_id: usize, + pub(crate) port_id: usize, + pub(crate) _phantom: PhantomData, +} + +pub struct ExternalProcess<'a, P> { + pub(crate) id: usize, + + pub(crate) flow_state: FlowState, + + pub(crate) _phantom: PhantomData<&'a &'a mut P>, +} + +impl

Clone for ExternalProcess<'_, P> { + fn clone(&self) -> Self { + ExternalProcess { + id: self.id, + flow_state: self.flow_state.clone(), + _phantom: PhantomData, + } + } +} + +impl<'a, P> Location<'a> for ExternalProcess<'a, P> { + fn id(&self) -> LocationId { + LocationId::ExternalProcess(self.id) + } + + fn flow_state(&self) -> &FlowState { + &self.flow_state + } + + fn make_from(id: LocationId, flow_state: FlowState) -> Self { + match id { + LocationId::ExternalProcess(id) => ExternalProcess { + id, + flow_state, + _phantom: PhantomData, + }, + _ => panic!(), + } + } +} + +impl<'a, P> ExternalProcess<'a, P> { + pub fn source_external_bytes + NoTick>( + &self, + to: &L, + ) -> (ExternalBytesPort, Stream) { + let next_external_port_id = { + let mut flow_state = self.flow_state.borrow_mut(); + let id = flow_state.next_external_out; + flow_state.next_external_out += 1; + id + }; + + ( + ExternalBytesPort { + process_id: self.id, + port_id: next_external_port_id, + }, + Stream::new( + to.clone(), + HfPlusNode::Persist(Box::new(HfPlusNode::Network { + from_location: LocationId::ExternalProcess(self.id), + from_key: Some(next_external_port_id), + to_location: to.id(), + to_key: None, + serialize_pipeline: None, + instantiate_fn: crate::ir::DebugInstantiate::Building(), + deserialize_pipeline: Some(syn::parse_quote!(map(|b| b.unwrap().freeze()))), + input: Box::new(HfPlusNode::Source { + source: HfPlusSource::ExternalNetwork(), + location_kind: LocationId::ExternalProcess(self.id), + }), + })), + ), + ) + } + + pub fn source_external_bincode + NoTick, T: Serialize + DeserializeOwned>( + &self, + to: &L, + ) -> (ExternalBincodeSink, Stream) { + let next_external_port_id = { + let mut flow_state = self.flow_state.borrow_mut(); + let id = flow_state.next_external_out; + flow_state.next_external_out += 1; + id + }; + + ( + ExternalBincodeSink { + process_id: self.id, + port_id: next_external_port_id, + _phantom: PhantomData, + }, + Stream::new( + to.clone(), + HfPlusNode::Persist(Box::new(HfPlusNode::Network { + from_location: LocationId::ExternalProcess(self.id), + from_key: Some(next_external_port_id), + to_location: to.id(), + to_key: None, + serialize_pipeline: None, + instantiate_fn: crate::ir::DebugInstantiate::Building(), + deserialize_pipeline: Some(crate::stream::deserialize_bincode::(None)), + input: Box::new(HfPlusNode::Source { + source: HfPlusSource::ExternalNetwork(), + location_kind: LocationId::ExternalProcess(self.id), + }), + })), + ), + ) + } +} diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs new file mode 100644 index 000000000000..be906087291e --- /dev/null +++ b/hydroflow_plus/src/location/mod.rs @@ -0,0 +1,332 @@ +use std::fmt::Debug; +use std::marker::PhantomData; +use std::time::Duration; + +use hydroflow::futures::stream::Stream as FuturesStream; +use hydroflow::{tokio, tokio_stream}; +use proc_macro2::Span; +use stageleft::{q, Quoted}; + +use super::builder::FlowState; +use crate::cycle::{CycleCollection, CycleCollectionWithInitial, DeferTick, HfCycle, TickCycle}; +use crate::ir::{HfPlusNode, HfPlusSource}; +use crate::{Bounded, HfForwardRef, Optional, Singleton, Stream, Unbounded}; + +pub mod external_process; +pub use external_process::ExternalProcess; + +pub mod process; +pub use process::Process; + +pub mod cluster; +pub use cluster::{Cluster, ClusterId}; + +pub mod can_send; +pub use can_send::CanSend; + +pub mod tick; +pub use tick::{NoTick, Tick}; + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum LocationId { + Process(usize), + Cluster(usize), + ExternalProcess(usize), +} + +impl LocationId { + pub fn raw_id(&self) -> usize { + match self { + LocationId::Process(id) => *id, + LocationId::Cluster(id) => *id, + LocationId::ExternalProcess(id) => *id, + } + } +} + +pub fn check_matching_location<'a, L: Location<'a>>(l1: &L, l2: &L) { + assert_eq!(l1.id(), l2.id(), "locations do not match"); +} + +pub trait Location<'a>: Clone { + fn id(&self) -> LocationId; + + fn flow_state(&self) -> &FlowState; + + fn make_from(id: LocationId, flow_state: FlowState) -> Self; + + fn nest(&self) -> Tick + where + Self: NoTick, + { + Tick { l: self.clone() } + } + + fn spin(&self) -> Stream<(), Unbounded, Self> + where + Self: Sized + NoTick, + { + Stream::new( + self.clone(), + HfPlusNode::Persist(Box::new(HfPlusNode::Source { + source: HfPlusSource::Spin(), + location_kind: self.id(), + })), + ) + } + + fn spin_batch( + &self, + batch_size: impl Quoted<'a, usize> + Copy + 'a, + ) -> Stream<(), Bounded, Tick> + where + Self: Sized + NoTick, + { + self.spin() + .flat_map(q!(move |_| 0..batch_size)) + .map(q!(|_| ())) + .tick_batch() + } + + fn source_stream + Unpin>( + &self, + e: impl Quoted<'a, E>, + ) -> Stream + where + Self: Sized + NoTick, + { + let e = e.splice_untyped(); + + Stream::new( + self.clone(), + HfPlusNode::Persist(Box::new(HfPlusNode::Source { + source: HfPlusSource::Stream(e.into()), + location_kind: self.id(), + })), + ) + } + + fn source_iter>( + &self, + e: impl Quoted<'a, E>, + ) -> Stream + where + Self: Sized + NoTick, + { + let e = e.splice_untyped(); + + Stream::new( + self.clone(), + HfPlusNode::Persist(Box::new(HfPlusNode::Source { + source: HfPlusSource::Iter(e.into()), + location_kind: self.id(), + })), + ) + } + + fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton + where + Self: Sized + NoTick, + { + let e_arr = q!([e]); + let e = e_arr.splice_untyped(); + + // we do a double persist here because if the singleton shows up on every tick, + // we first persist the source so that we store that value and then persist again + // so that it grows every tick + Singleton::new( + self.clone(), + HfPlusNode::Persist(Box::new(HfPlusNode::Persist(Box::new( + HfPlusNode::Source { + source: HfPlusSource::Iter(e.into()), + location_kind: self.id(), + }, + )))), + ) + } + + fn singleton_each_tick( + &self, + e: impl Quoted<'a, T>, + ) -> Singleton> + where + Self: Sized + NoTick, + { + self.singleton(e).latest_tick() + } + + fn singleton_first_tick( + &self, + e: impl Quoted<'a, T>, + ) -> Optional> + where + Self: Sized + NoTick, + { + let e_arr = q!([e]); + let e = e_arr.splice_untyped(); + + Optional::new( + self.clone().nest(), + HfPlusNode::Source { + source: HfPlusSource::Iter(e.into()), + location_kind: self.id(), + }, + ) + } + + fn source_interval( + &self, + interval: impl Quoted<'a, Duration> + Copy + 'a, + ) -> Stream + where + Self: Sized + NoTick, + { + self.source_stream(q!(tokio_stream::wrappers::IntervalStream::new( + tokio::time::interval(interval) + ))) + } + + fn source_interval_delayed( + &self, + delay: impl Quoted<'a, Duration> + Copy + 'a, + interval: impl Quoted<'a, Duration> + Copy + 'a, + ) -> Stream + where + Self: Sized + NoTick, + { + self.source_stream(q!(tokio_stream::wrappers::IntervalStream::new( + tokio::time::interval_at(tokio::time::Instant::now() + delay, interval) + ))) + } + + fn forward_ref>( + &self, + ) -> (HfForwardRef<'a, (), S>, S) + where + Self: NoTick, + { + let next_id = { + let on_id = match self.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + HfForwardRef { + ident: ident.clone(), + _phantom: PhantomData, + }, + S::create_source(ident, self.clone()), + ) + } + + fn tick_forward_ref>>( + &self, + ) -> (HfForwardRef<'a, TickCycle, S>, S) + where + Self: NoTick, + { + let next_id = { + let on_id = match self.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + HfForwardRef { + ident: ident.clone(), + _phantom: PhantomData, + }, + S::create_source(ident, self.nest().clone()), + ) + } + + fn tick_cycle> + DeferTick>( + &self, + ) -> (HfCycle<'a, S>, S) + where + Self: NoTick, + { + let next_id = { + let on_id = match self.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + HfCycle { + ident: ident.clone(), + _phantom: PhantomData, + }, + S::create_source(ident, self.nest().clone()), + ) + } + + fn tick_cycle_with_initial< + S: CycleCollectionWithInitial<'a, TickCycle, Location = Tick> + DeferTick, + >( + &self, + initial: S, + ) -> (HfCycle<'a, S>, S) + where + Self: NoTick, + { + let next_id = { + let on_id = match self.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + HfCycle { + ident: ident.clone(), + _phantom: PhantomData, + }, + S::create_source(ident, initial, self.nest().clone()), + ) + } +} diff --git a/hydroflow_plus/src/location/process.rs b/hydroflow_plus/src/location/process.rs new file mode 100644 index 000000000000..dadda1ce8f3c --- /dev/null +++ b/hydroflow_plus/src/location/process.rs @@ -0,0 +1,41 @@ +use std::marker::PhantomData; + +use super::{Location, LocationId}; +use crate::builder::FlowState; + +pub struct Process<'a, P> { + pub(crate) id: usize, + pub(crate) flow_state: FlowState, + pub(crate) _phantom: PhantomData<&'a &'a mut P>, +} + +impl

Clone for Process<'_, P> { + fn clone(&self) -> Self { + Process { + id: self.id, + flow_state: self.flow_state.clone(), + _phantom: PhantomData, + } + } +} + +impl<'a, P> Location<'a> for Process<'a, P> { + fn id(&self) -> LocationId { + LocationId::Process(self.id) + } + + fn flow_state(&self) -> &FlowState { + &self.flow_state + } + + fn make_from(id: LocationId, flow_state: FlowState) -> Self { + match id { + LocationId::Process(id) => Process { + id, + flow_state, + _phantom: PhantomData, + }, + _ => panic!(), + } + } +} diff --git a/hydroflow_plus/src/location/tick.rs b/hydroflow_plus/src/location/tick.rs new file mode 100644 index 000000000000..48a27e8bbf04 --- /dev/null +++ b/hydroflow_plus/src/location/tick.rs @@ -0,0 +1,34 @@ +use super::{Cluster, Location, LocationId, Process}; +use crate::builder::FlowState; + +pub trait NoTick {} +impl NoTick for Process<'_, T> {} +impl NoTick for Cluster<'_, T> {} + +/// Marks the stream as being inside the single global clock domain. +#[derive(Clone)] +pub struct Tick { + pub(crate) l: L, +} + +impl<'a, L: Location<'a>> Tick { + pub fn outer(&self) -> &L { + &self.l + } +} + +impl<'a, L: Location<'a>> Location<'a> for Tick { + fn id(&self) -> LocationId { + self.l.id() + } + + fn flow_state(&self) -> &FlowState { + self.l.flow_state() + } + + fn make_from(id: LocationId, flow_state: FlowState) -> Self { + Tick { + l: L::make_from(id, flow_state), + } + } +} diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 855e5860cad4..c7c1a73349f3 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -9,34 +9,37 @@ use crate::builder::FlowState; use crate::cycle::{ CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick, TickCycle, }; -use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; -use crate::location::{Location, LocationId}; -use crate::stream::{Bounded, NoTick, Tick, Unbounded}; +use crate::ir::{HfPlusLeaf, HfPlusNode, TeeNode}; +use crate::location::{check_matching_location, Location, LocationId, NoTick, Tick}; +use crate::stream::{Bounded, Unbounded}; use crate::Stream; pub trait CrossResult<'a, Other> { type Out; - fn other_location(other: &Other) -> LocationId; + type Location; + + fn other_location(other: &Other) -> Self::Location; fn other_ir_node(other: Other) -> HfPlusNode; - fn make(location_kind: LocationId, flow_state: FlowState, ir_node: HfPlusNode) -> Self::Out; + fn make(location: Self::Location, ir_node: HfPlusNode) -> Self::Out; } impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Singleton> for Singleton { type Out = Singleton<(T, U), W, N>; + type Location = N; - fn other_location(other: &Singleton) -> LocationId { - other.location_kind + fn other_location(other: &Singleton) -> N { + other.location.clone() } fn other_ir_node(other: Singleton) -> HfPlusNode { other.ir_node.into_inner() } - fn make(location_kind: LocationId, flow_state: FlowState, ir_node: HfPlusNode) -> Self::Out { - Singleton::new(location_kind, flow_state, ir_node) + fn make(location: N, ir_node: HfPlusNode) -> Self::Out { + Singleton::new(location, ir_node) } } @@ -44,33 +47,35 @@ impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Optional> for Singleton { type Out = Optional<(T, U), W, N>; + type Location = N; - fn other_location(other: &Optional) -> LocationId { - other.location_kind + fn other_location(other: &Optional) -> N { + other.location.clone() } fn other_ir_node(other: Optional) -> HfPlusNode { other.ir_node.into_inner() } - fn make(location_kind: LocationId, flow_state: FlowState, ir_node: HfPlusNode) -> Self::Out { - Optional::new(location_kind, flow_state, ir_node) + fn make(location: N, ir_node: HfPlusNode) -> Self::Out { + Optional::new(location, ir_node) } } impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Optional> for Optional { type Out = Optional<(T, U), W, N>; + type Location = N; - fn other_location(other: &Optional) -> LocationId { - other.location_kind + fn other_location(other: &Optional) -> N { + other.location.clone() } fn other_ir_node(other: Optional) -> HfPlusNode { other.ir_node.into_inner() } - fn make(location_kind: LocationId, flow_state: FlowState, ir_node: HfPlusNode) -> Self::Out { - Optional::new(location_kind, flow_state, ir_node) + fn make(location: N, ir_node: HfPlusNode) -> Self::Out { + Optional::new(location, ir_node) } } @@ -78,51 +83,49 @@ impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Singleton> for Optional { type Out = Optional<(T, U), W, N>; + type Location = N; - fn other_location(other: &Singleton) -> LocationId { - other.location_kind + fn other_location(other: &Singleton) -> N { + other.location.clone() } fn other_ir_node(other: Singleton) -> HfPlusNode { other.ir_node.into_inner() } - fn make(location_kind: LocationId, flow_state: FlowState, ir_node: HfPlusNode) -> Self::Out { - Optional::new(location_kind, flow_state, ir_node) + fn make(location: N, ir_node: HfPlusNode) -> Self::Out { + Optional::new(location, ir_node) } } pub struct Singleton { - pub(crate) location_kind: LocationId, - - flow_state: FlowState, + location: N, pub(crate) ir_node: RefCell, _phantom: PhantomData<(T, N, W)>, } impl<'a, T, W, N: Location<'a>> Singleton { - pub(crate) fn new( - location_kind: LocationId, - flow_state: FlowState, - ir_node: HfPlusNode, - ) -> Self { + pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { Singleton { - location_kind, - flow_state, + location, ir_node: RefCell::new(ir_node), _phantom: PhantomData, } } + + fn location_kind(&self) -> LocationId { + self.location.id() + } + + fn flow_state(&self) -> &FlowState { + self.location.flow_state() + } } impl<'a, T, N: Location<'a>> From> for Singleton { fn from(singleton: Singleton) -> Self { - Singleton::new( - singleton.location_kind, - singleton.flow_state, - singleton.ir_node.into_inner(), - ) + Singleton::new(singleton.location, singleton.ir_node.into_inner()) } } @@ -134,24 +137,24 @@ impl<'a, T, N: Location<'a>> DeferTick for Singleton> { impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> { fn complete(self, ident: syn::Ident) { - self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, - location_kind: self.location_kind, + location_kind: self.location_kind(), input: Box::new(self.ir_node.into_inner()), }); } } impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Singleton> { - type Location = N; + type Location = Tick; - fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { + fn create_source(ident: syn::Ident, location: Tick) -> Self { + let location_id = location.id(); Singleton::new( - l, - flow_state, + location, HfPlusNode::CycleSource { ident, - location_kind: l, + location_kind: location_id, }, ) } @@ -160,21 +163,16 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Singleton> CycleCollectionWithInitial<'a, TickCycle> for Singleton> { - type Location = N; + type Location = Tick; - fn create_source( - ident: syn::Ident, - flow_state: FlowState, - initial: Self, - l: LocationId, - ) -> Self { + fn create_source(ident: syn::Ident, initial: Self, location: Tick) -> Self { + let location_id = location.id(); Singleton::new( - l, - flow_state, + location, HfPlusNode::Union( Box::new(HfPlusNode::CycleSource { ident, - location_kind: l, + location_kind: location_id, }), initial.ir_node.into_inner().into(), ), @@ -193,8 +191,7 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Singleton { if let HfPlusNode::Tee { inner } = self.ir_node.borrow().deref() { Singleton { - location_kind: self.location_kind, - flow_state: self.flow_state.clone(), + location: self.location.clone(), ir_node: HfPlusNode::Tee { inner: TeeNode(inner.0.clone()), } @@ -210,8 +207,7 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Singleton { impl<'a, T, W, N: Location<'a>> Singleton { pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Singleton { Singleton::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Map { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -224,8 +220,7 @@ impl<'a, T, W, N: Location<'a>> Singleton { f: impl IntoQuotedMut<'a, F>, ) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::FlatMap { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -235,8 +230,7 @@ impl<'a, T, W, N: Location<'a>> Singleton { pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Filter { f: f.splice_fn1_borrow().into(), input: Box::new(self.ir_node.into_inner()), @@ -249,8 +243,7 @@ impl<'a, T, W, N: Location<'a>> Singleton { f: impl IntoQuotedMut<'a, F>, ) -> Optional { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::FilterMap { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -262,24 +255,17 @@ impl<'a, T, W, N: Location<'a>> Singleton { impl<'a, T, N: Location<'a>> Singleton> { // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream pub fn into_stream(self) -> Stream> { - Stream::new( - self.location_kind, - self.flow_state, - self.ir_node.into_inner(), - ) + Stream::new(self.location, self.ir_node.into_inner()) } pub fn cross_singleton(self, other: Other) -> >::Out where - Self: CrossResult<'a, Other>, + Self: CrossResult<'a, Other, Location = Tick>, { - if self.location_kind != Self::other_location(&other) { - panic!("cross_singleton must be called on streams on the same node"); - } + check_matching_location(&self.location, &Self::other_location(&other)); Self::make( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::CrossSingleton( Box::new(self.ir_node.into_inner()), Box::new(Self::other_ir_node(other)), @@ -306,40 +292,35 @@ impl<'a, T, N: Location<'a>> Singleton> { impl<'a, T, N: Location<'a>> Singleton> { pub fn all_ticks(self) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } pub fn latest(self) -> Singleton { Singleton::new( - self.location_kind, - self.flow_state, + self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } pub fn defer_tick(self) -> Singleton> { Singleton::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) } pub fn persist(self) -> Stream> { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } pub fn delta(self) -> Optional> { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), ) } @@ -348,26 +329,16 @@ impl<'a, T, N: Location<'a>> Singleton> { impl<'a, T, B, N: Location<'a> + NoTick> Singleton { pub fn latest_tick(self) -> Singleton> { Singleton::new( - self.location_kind, - self.flow_state, + self.location.nest(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } pub fn sample_every( self, - duration: impl Quoted<'a, std::time::Duration> + Copy + 'a, + interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, ) -> Stream { - let interval = duration.splice_typed(); - - let samples = Stream::<(), Bounded, Tick>::new( - self.location_kind, - self.flow_state.clone(), - HfPlusNode::Source { - source: HfPlusSource::Interval(interval.into()), - location_kind: self.location_kind, - }, - ); + let samples = self.location.source_interval(interval).tick_batch(); self.latest_tick() .continue_if(samples.first()) @@ -379,15 +350,12 @@ impl<'a, T, B, N: Location<'a> + NoTick> Singleton { impl<'a, T, N: Location<'a> + NoTick> Singleton { pub fn cross_singleton(self, other: Other) -> >::Out where - Self: CrossResult<'a, Other>, + Self: CrossResult<'a, Other, Location = N>, { - if self.location_kind != Self::other_location(&other) { - panic!("cross_singleton must be called on streams on the same node"); - } + check_matching_location(&self.location, &Self::other_location(&other)); Self::make( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Persist(Box::new(HfPlusNode::CrossSingleton( Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), Box::new(HfPlusNode::Unpersist(Box::new(Self::other_ir_node(other)))), @@ -397,34 +365,31 @@ impl<'a, T, N: Location<'a> + NoTick> Singleton { } pub struct Optional { - pub(crate) location_kind: LocationId, - - flow_state: FlowState, + pub(crate) location: N, pub(crate) ir_node: RefCell, _phantom: PhantomData<(T, N, W)>, } impl<'a, T, W, N: Location<'a>> Optional { - pub(crate) fn new( - location_kind: LocationId, - flow_state: FlowState, - ir_node: HfPlusNode, - ) -> Self { + pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { Optional { - location_kind, - flow_state, + location, ir_node: RefCell::new(ir_node), _phantom: PhantomData, } } pub fn some(singleton: Singleton) -> Self { - Optional::new( - singleton.location_kind, - singleton.flow_state, - singleton.ir_node.into_inner(), - ) + Optional::new(singleton.location, singleton.ir_node.into_inner()) + } + + fn location_kind(&self) -> LocationId { + self.location.id() + } + + fn flow_state(&self) -> &FlowState { + self.location.flow_state() } } @@ -436,24 +401,24 @@ impl<'a, T, N: Location<'a>> DeferTick for Optional> { impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Optional> { fn complete(self, ident: syn::Ident) { - self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, - location_kind: self.location_kind, + location_kind: self.location_kind(), input: Box::new(self.ir_node.into_inner()), }); } } impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> { - type Location = N; + type Location = Tick; - fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { + fn create_source(ident: syn::Ident, location: Tick) -> Self { + let location_id = location.id(); Optional::new( - l, - flow_state, + location, HfPlusNode::CycleSource { ident, - location_kind: l, + location_kind: location_id, }, ) } @@ -461,9 +426,9 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional + NoTick> CycleComplete<'a, ()> for Optional { fn complete(self, ident: syn::Ident) { - self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, - location_kind: self.location_kind, + location_kind: self.location_kind(), input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), }); } @@ -472,13 +437,13 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ()> for Optional + NoTick> CycleCollection<'a, ()> for Optional { type Location = N; - fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { + fn create_source(ident: syn::Ident, location: N) -> Self { + let location_id = location.id(); Optional::new( - l, - flow_state, + location, HfPlusNode::Persist(Box::new(HfPlusNode::CycleSource { ident, - location_kind: l, + location_kind: location_id, })), ) } @@ -501,8 +466,7 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Optional { if let HfPlusNode::Tee { inner } = self.ir_node.borrow().deref() { Optional { - location_kind: self.location_kind, - flow_state: self.flow_state.clone(), + location: self.location.clone(), ir_node: HfPlusNode::Tee { inner: TeeNode(inner.0.clone()), } @@ -518,8 +482,7 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Optional { impl<'a, T, W, N: Location<'a>> Optional { pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Map { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -532,8 +495,7 @@ impl<'a, T, W, N: Location<'a>> Optional { f: impl IntoQuotedMut<'a, F>, ) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::FlatMap { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -543,8 +505,7 @@ impl<'a, T, W, N: Location<'a>> Optional { pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Filter { f: f.splice_fn1_borrow().into(), input: Box::new(self.ir_node.into_inner()), @@ -557,8 +518,7 @@ impl<'a, T, W, N: Location<'a>> Optional { f: impl IntoQuotedMut<'a, F>, ) -> Optional { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::FilterMap { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -570,11 +530,7 @@ impl<'a, T, W, N: Location<'a>> Optional { impl<'a, T, N: Location<'a>> Optional> { // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream pub fn into_stream(self) -> Stream> { - Stream::new( - self.location_kind, - self.flow_state, - self.ir_node.into_inner(), - ) + Stream::new(self.location, self.ir_node.into_inner()) } pub fn cross_singleton( @@ -585,13 +541,10 @@ impl<'a, T, N: Location<'a>> Optional> { O: Clone, { let other: Optional> = other.into(); - if self.location_kind != other.location_kind { - panic!("cross_singleton must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::CrossSingleton( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), @@ -619,13 +572,10 @@ impl<'a, T, N: Location<'a>> Optional> { } pub fn union(self, other: Optional>) -> Optional> { - if self.location_kind != other.location_kind { - panic!("union must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Union( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), @@ -637,13 +587,10 @@ impl<'a, T, N: Location<'a>> Optional> { self, other: Singleton>, ) -> Singleton> { - if self.location_kind != other.location_kind { - panic!("or_else must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); Singleton::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Union( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), @@ -656,49 +603,43 @@ impl<'a, T, N: Location<'a>> Optional> { T: Clone, N: NoTick, { - let self_location = N::make_from(self.location_kind, self.flow_state.clone()); - self.map(q!(|v| Some(v))) - .unwrap_or(self_location.singleton_each_tick(q!(None))) + let none_singleton = self.location.outer().singleton_each_tick(q!(None)); + self.map(q!(|v| Some(v))).unwrap_or(none_singleton) } } impl<'a, T, N: Location<'a>> Optional> { pub fn all_ticks(self) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } pub fn latest(self) -> Optional { Optional::new( - self.location_kind, - self.flow_state, + self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } pub fn defer_tick(self) -> Optional> { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) } pub fn persist(self) -> Stream> { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } pub fn delta(self) -> Optional> { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), ) } @@ -707,8 +648,7 @@ impl<'a, T, N: Location<'a>> Optional> { impl<'a, T, B, N: Location<'a> + NoTick> Optional { pub fn latest_tick(self) -> Optional> { Optional::new( - self.location_kind, - self.flow_state, + self.location.nest(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } @@ -719,18 +659,9 @@ impl<'a, T, B, N: Location<'a> + NoTick> Optional { pub fn sample_every( self, - duration: impl Quoted<'a, std::time::Duration> + Copy + 'a, + interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, ) -> Stream { - let interval = duration.splice_typed(); - - let samples = Stream::<(), Bounded, Tick>::new( - self.location_kind, - self.flow_state.clone(), - HfPlusNode::Source { - source: HfPlusSource::Interval(interval.into()), - location_kind: self.location_kind, - }, - ); + let samples = self.location.source_interval(interval).tick_batch(); self.latest_tick() .continue_if(samples.first()) @@ -743,9 +674,7 @@ impl<'a, T, B, N: Location<'a> + NoTick> Optional { other: impl Into>, ) -> Singleton { let other = other.into(); - if self.location_kind != other.location_kind { - panic!("or_else must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); self.latest_tick().unwrap_or(other.latest_tick()).latest() } @@ -754,9 +683,8 @@ impl<'a, T, B, N: Location<'a> + NoTick> Optional { where T: Clone, { - let self_location = N::make_from(self.location_kind, self.flow_state.clone()); - self.map(q!(|v| Some(v))) - .unwrap_or(self_location.singleton(q!(None))) + let none_singleton = self.location.singleton(q!(None)); + self.map(q!(|v| Some(v))).unwrap_or(none_singleton) } } @@ -769,9 +697,7 @@ impl<'a, T, N: Location<'a> + NoTick> Optional { O: Clone, { let other: Optional = other.into(); - if self.location_kind != other.location_kind { - panic!("cross_singleton must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); self.latest_tick() .cross_singleton(other.latest_tick()) diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 39f664d889d9..4a6a1e3a6108 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -12,12 +12,15 @@ use serde::Serialize; use stageleft::{q, IntoQuotedMut, Quoted}; use syn::parse_quote; +// TODO(shadaj): have to uses super due to stageleft limitations use super::staging_util::get_this_crate; -use crate::builder::{self, FlowState}; +use crate::builder::FlowState; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, TickCycle}; -use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; +use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, TeeNode}; +use crate::location::cluster::ClusterSelfId; +use crate::location::external_process::{ExternalBincodeStream, ExternalBytesPort}; use crate::location::{ - CanSend, ExternalBincodeStream, ExternalBytesPort, ExternalProcess, Location, LocationId, + check_matching_location, CanSend, ExternalProcess, Location, LocationId, NoTick, Tick, }; use crate::{Cluster, ClusterId, Optional, Process, Singleton}; @@ -29,31 +32,6 @@ pub enum Unbounded {} /// to be complete in finite time. pub enum Bounded {} -pub trait NoTick {} -impl NoTick for Process<'_, T> {} -impl NoTick for Cluster<'_, T> {} - -/// Marks the stream as being inside the single global clock domain. -pub struct Tick { - l: L, -} - -impl<'a, L: Location<'a>> Location<'a> for Tick { - fn id(&self) -> LocationId { - self.l.id() - } - - fn flow_state(&self) -> &FlowState { - self.l.flow_state() - } - - fn make_from(id: LocationId, flow_state: FlowState) -> Self { - Tick { - l: L::make_from(id, flow_state), - } - } -} - /// An infinite stream of elements of type `T`. /// /// Type Parameters: @@ -64,14 +42,22 @@ impl<'a, L: Location<'a>> Location<'a> for Tick { /// or [`Unbounded`] /// - `N`: the type of the node that the stream is materialized on pub struct Stream { - location_kind: LocationId, - - flow_state: FlowState, + location: N, pub(crate) ir_node: RefCell, _phantom: PhantomData<(T, N, W)>, } +impl<'a, T, W, N: Location<'a>> Stream { + fn flow_state(&self) -> &FlowState { + self.location.flow_state() + } + + fn location_kind(&self) -> LocationId { + self.location.id() + } +} + impl<'a, T, N: Location<'a>> DeferTick for Stream> { fn defer_tick(self) -> Self { Stream::defer_tick(self) @@ -80,24 +66,24 @@ impl<'a, T, N: Location<'a>> DeferTick for Stream> { impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Stream> { fn complete(self, ident: syn::Ident) { - self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, - location_kind: self.location_kind, + location_kind: self.location_kind(), input: Box::new(self.ir_node.into_inner()), }); } } impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream> { - type Location = N; + type Location = Tick; - fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { + fn create_source(ident: syn::Ident, location: Tick) -> Self { + let location_id = location.id(); Stream::new( - l, - flow_state, + location, HfPlusNode::CycleSource { ident, - location_kind: l, + location_kind: location_id, }, ) } @@ -105,9 +91,9 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream + NoTick> CycleComplete<'a, ()> for Stream { fn complete(self, ident: syn::Ident) { - self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, - location_kind: self.location_kind, + location_kind: self.location_kind(), input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), }); } @@ -116,27 +102,22 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ()> for Stream + NoTick> CycleCollection<'a, ()> for Stream { type Location = N; - fn create_source(ident: syn::Ident, flow_state: FlowState, l: LocationId) -> Self { + fn create_source(ident: syn::Ident, location: N) -> Self { + let location_id = location.id(); Stream::new( - l, - flow_state, + location, HfPlusNode::Persist(Box::new(HfPlusNode::CycleSource { ident, - location_kind: l, + location_kind: location_id, })), ) } } impl<'a, T, W, N: Location<'a>> Stream { - pub(crate) fn new( - location_kind: LocationId, - flow_state: FlowState, - ir_node: HfPlusNode, - ) -> Self { + pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { Stream { - location_kind, - flow_state, + location, ir_node: RefCell::new(ir_node), _phantom: PhantomData, } @@ -154,8 +135,7 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Stream { if let HfPlusNode::Tee { inner } = self.ir_node.borrow().deref() { Stream { - location_kind: self.location_kind, - flow_state: self.flow_state.clone(), + location: self.location.clone(), ir_node: HfPlusNode::Tee { inner: TeeNode(inner.0.clone()), } @@ -171,8 +151,7 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Stream { impl<'a, T, W, N: Location<'a>> Stream { pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Map { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -185,8 +164,7 @@ impl<'a, T, W, N: Location<'a>> Stream { f: impl IntoQuotedMut<'a, F>, ) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::FlatMap { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -203,8 +181,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Filter { f: f.splice_fn1_borrow().into(), input: Box::new(self.ir_node.into_inner()), @@ -217,8 +194,7 @@ impl<'a, T, W, N: Location<'a>> Stream { f: impl IntoQuotedMut<'a, F>, ) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::FilterMap { f: f.splice_fn1().into(), input: Box::new(self.ir_node.into_inner()), @@ -234,13 +210,10 @@ impl<'a, T, W, N: Location<'a>> Stream { O: Clone, { let other: Optional = other.into(); - if self.location_kind != other.location_kind { - panic!("cross_singleton must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::CrossSingleton( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), @@ -254,13 +227,10 @@ impl<'a, T, W, N: Location<'a>> Stream { T: Clone, O: Clone, { - if self.location_kind != other.location_kind { - panic!("cross_product must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::CrossProduct( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), @@ -269,13 +239,10 @@ impl<'a, T, W, N: Location<'a>> Stream { } pub fn union(self, other: Stream) -> Stream { - if self.location_kind != other.location_kind { - panic!("union must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Union( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), @@ -288,14 +255,13 @@ impl<'a, T, W, N: Location<'a>> Stream { T: Eq + Hash, { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Unique(Box::new(self.ir_node.into_inner())), ) } pub fn dest_sink + 'a>(self, sink: impl Quoted<'a, S>) { - self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::DestSink { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::DestSink { sink: sink.splice_typed().into(), input: Box::new(self.ir_node.into_inner()), }); @@ -305,8 +271,7 @@ impl<'a, T, W, N: Location<'a>> Stream { impl<'a, T, N: Location<'a>> Stream> { pub fn all_ticks(self) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } @@ -316,16 +281,14 @@ impl<'a, T, N: Location<'a>> Stream> { T: Clone, { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } pub fn defer_tick(self) -> Stream> { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) } @@ -335,8 +298,7 @@ impl<'a, T, N: Location<'a>> Stream> { f: impl IntoQuotedMut<'a, F>, ) -> Stream> { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Inspect { f: f.splice_fn1_borrow().into(), input: Box::new(self.ir_node.into_inner()), @@ -345,11 +307,7 @@ impl<'a, T, N: Location<'a>> Stream> { } pub fn first(self) -> Optional> { - Optional::new( - self.location_kind, - self.flow_state, - self.ir_node.into_inner(), - ) + Optional::new(self.location, self.ir_node.into_inner()) } /// Allow this stream through if the other stream has elements, otherwise the output is empty. @@ -371,8 +329,7 @@ impl<'a, T, N: Location<'a>> Stream> { pub fn enumerate(self) -> Stream<(usize, T), Bounded, Tick> { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Enumerate(Box::new(self.ir_node.into_inner())), ) } @@ -383,8 +340,7 @@ impl<'a, T, N: Location<'a>> Stream> { comb: impl IntoQuotedMut<'a, F>, ) -> Singleton> { Singleton::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Fold { init: init.splice_fn0().into(), acc: comb.splice_fn2_borrow_mut().into(), @@ -398,8 +354,7 @@ impl<'a, T, N: Location<'a>> Stream> { comb: impl IntoQuotedMut<'a, F>, ) -> Optional> { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Reduce { f: comb.splice_fn2_borrow_mut().into(), input: Box::new(self.ir_node.into_inner()), @@ -434,8 +389,7 @@ impl<'a, T, N: Location<'a>> Stream> { T: Ord, { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Sort(Box::new(self.ir_node.into_inner())), ) } @@ -446,8 +400,7 @@ impl<'a, T, N: Location<'a>> Stream> { pub fn delta(self) -> Stream> { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), ) } @@ -456,8 +409,7 @@ impl<'a, T, N: Location<'a>> Stream> { impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn tick_batch(self) -> Stream> { Stream::new( - self.location_kind, - self.flow_state, + self.location.nest(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } @@ -471,8 +423,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Persist(Box::new(HfPlusNode::Inspect { f: f.splice_fn1_borrow().into(), input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), @@ -483,7 +434,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn for_each(self, f: impl IntoQuotedMut<'a, F>) { - self.flow_state.borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::ForEach { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::ForEach { input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), f: f.splice_fn1().into(), }); @@ -493,19 +444,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { impl<'a, T, N: Location<'a> + NoTick> Stream { pub fn sample_every( self, - duration: impl Quoted<'a, std::time::Duration> + Copy + 'a, + interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, ) -> Stream { - let interval = duration.splice_typed(); - - let samples = Stream::>::new( - self.location_kind, - self.flow_state.clone(), - HfPlusNode::Source { - source: HfPlusSource::Interval(interval.into()), - location_kind: self.location_kind, - }, - ); - + let samples = self.location.source_interval(interval).tick_batch(); self.tick_batch().continue_if(samples.first()).all_ticks() } @@ -518,8 +459,7 @@ impl<'a, T, N: Location<'a> + NoTick> Stream { // which produces all values from all ticks every tick, // so delta will always give the lastest aggregation Singleton::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Persist(Box::new(HfPlusNode::Fold { init: init.splice_fn0().into(), acc: comb.splice_fn2_borrow_mut().into(), @@ -533,8 +473,7 @@ impl<'a, T, N: Location<'a> + NoTick> Stream { comb: impl IntoQuotedMut<'a, F>, ) -> Optional { Optional::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Persist(Box::new(HfPlusNode::Reduce { f: comb.splice_fn2_borrow_mut().into(), input: Box::new(self.ir_node.into_inner()), @@ -570,13 +509,10 @@ impl<'a, T, N: Location<'a>> Stream { where T: Eq + Hash, { - if self.location_kind != other.location_kind { - panic!("union must be called on streams on the same node"); - } + check_matching_location(&self.location, &other.location); Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Difference( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), @@ -597,13 +533,10 @@ impl<'a, K, V1, W, N: Location<'a>> Stream<(K, V1), W, N> { where K: Eq + Hash, { - if self.location_kind != n.location_kind { - panic!("join must be called on streams on the same node"); - } + check_matching_location(&self.location, &n.location); Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::Join( Box::new(self.ir_node.into_inner()), Box::new(n.ir_node.into_inner()), @@ -615,13 +548,10 @@ impl<'a, K, V1, W, N: Location<'a>> Stream<(K, V1), W, N> { where K: Eq + Hash, { - if self.location_kind != n.location_kind { - panic!("anti_join must be called on streams on the same node"); - } + check_matching_location(&self.location, &n.location); Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::AntiJoin( Box::new(self.ir_node.into_inner()), Box::new(n.ir_node.into_inner()), @@ -637,8 +567,7 @@ impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { comb: impl IntoQuotedMut<'a, F>, ) -> Stream<(K, A), Bounded, Tick> { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::FoldKeyed { init: init.splice_fn0().into(), acc: comb.splice_fn2_borrow_mut().into(), @@ -652,8 +581,7 @@ impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { comb: impl IntoQuotedMut<'a, F>, ) -> Stream<(K, V), Bounded, Tick> { Stream::new( - self.location_kind, - self.flow_state, + self.location, HfPlusNode::ReduceKeyed { f: comb.splice_fn2_borrow_mut().into(), input: Box::new(self.ir_node.into_inner()), @@ -723,8 +651,8 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)>, T: Clone + Serialize + DeserializeOwned, { - let self_node_id = match self.location_kind { - LocationId::Cluster(cluster_id) => builder::ClusterSelfId { + let self_node_id = match self.location_kind() { + LocationId::Cluster(cluster_id) => ClusterSelfId { id: cluster_id, _phantom: PhantomData, }, @@ -748,10 +676,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { let deserialize_pipeline = Some(deserialize_bincode::(N::tagged_type())); Stream::new( - other.id(), - self.flow_state, + other.clone(), HfPlusNode::Network { - from_location: self.location_kind, + from_location: self.location_kind(), from_key: None, to_location: other.id(), to_key: None, @@ -774,7 +701,8 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { { let serialize_pipeline = Some(serialize_bincode::(N::is_demux())); - let mut flow_state_borrow = self.flow_state.borrow_mut(); + let flow_state = self.flow_state().clone(); + let mut flow_state_borrow = flow_state.borrow_mut(); let external_key = flow_state_borrow.next_external_out; flow_state_borrow.next_external_out += 1; @@ -786,7 +714,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { leaves.push(HfPlusLeaf::ForEach { f: dummy_f.into(), input: Box::new(HfPlusNode::Network { - from_location: self.location_kind, + from_location: self.location_kind(), from_key: None, to_location: other.id(), to_key: Some(external_key), @@ -810,10 +738,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { { let root = get_this_crate(); Stream::new( - other.id(), - self.flow_state, + other.clone(), HfPlusNode::Network { - from_location: self.location_kind, + from_location: self.location_kind(), from_key: None, to_location: other.id(), to_key: None, @@ -835,7 +762,8 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { where N: CanSend<'a, ExternalProcess<'a, N2>, In = T, Out = Bytes>, { - let mut flow_state_borrow = self.flow_state.borrow_mut(); + let flow_state = self.flow_state().clone(); + let mut flow_state_borrow = flow_state.borrow_mut(); let external_key = flow_state_borrow.next_external_out; flow_state_borrow.next_external_out += 1; @@ -846,7 +774,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { leaves.push(HfPlusLeaf::ForEach { f: dummy_f.into(), input: Box::new(HfPlusNode::Network { - from_location: self.location_kind, + from_location: self.location_kind(), from_key: None, to_location: other.id(), to_key: Some(external_key), diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap index 70a8dd42e91b..370f07676be9 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap @@ -12,7 +12,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u64 , u64) , (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) , (inside_batch , total_batch) | { * inside += inside_batch ; * total += total_batch ; } }), input: Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -70,10 +70,10 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Source { - source: Interval( - stageleft :: runtime_support :: type_hint :: < core :: time :: Duration > ({ use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) }), + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, ), location_kind: Process( 1, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap index c73fdb714a8e..73ad09b5e521 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap @@ -4,10 +4,10 @@ expression: ir.surface_syntax_string() --- 1v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env = FAKE ; let p2_port = "port_0" ; { env . port (p2_port) . connect_local_blocking :: < ConnectedTagged < ConnectedDirect > > () . into_source () } }); 2v1 = map (| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: compute_pi :: Worker > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (u64 , u64) > (& b) . unwrap ()) }); -3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); +3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); 4v1 = reduce :: < 'static > (stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u64 , u64) , (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) , (inside_batch , total_batch) | { * inside += inside_batch ; * total += total_batch ; } })); -5v1 = source_interval (stageleft :: runtime_support :: type_hint :: < core :: time :: Duration > ({ use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) })); -6v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () })); +5v1 = source_stream ({ use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }); +6v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () })); 7v1 = cross_singleton (); 8v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < ((u64 , u64) , ()) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d })); 9v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) | { println ! ("pi: {} ({} trials)" , 4.0 * inside as f64 / total as f64 , total) ; } })); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__many_to_many__tests__many_to_many.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__many_to_many__tests__many_to_many.snap index d58ad11b7168..e10ab16062d6 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__many_to_many__tests__many_to_many.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__many_to_many__tests__many_to_many.snap @@ -4,7 +4,7 @@ expression: built.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < () > , i32) , () > ({ use crate :: __staged :: cluster :: many_to_many :: * ; | n | println ! ("cluster received: {:?}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) , () > ({ use crate :: __staged :: cluster :: many_to_many :: * ; | n | println ! ("cluster received: {:?}" , n) }), input: Network { from_location: Cluster( 0, @@ -36,7 +36,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < () > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < () > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < () > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < () > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Source { source: Iter( { use crate :: __staged :: cluster :: many_to_many :: * ; 0 .. 2 }, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap index c895c3413a7c..5daa117e821c 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap @@ -9,7 +9,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | total , count | * total += count }), input: Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , (std :: string :: String , i32)) , (std :: string :: String , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , (std :: string :: String , i32)) , (std :: string :: String , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 1, @@ -78,7 +78,7 @@ expression: built.ir() ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use crate :: __staged :: cluster :: map_reduce :: * ; let all_ids_vec = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ClusterId :: from_raw ((i % all_ids_vec . len ()) as u32) , w) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use crate :: __staged :: cluster :: map_reduce :: * ; let all_ids_vec = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ClusterId :: from_raw ((i % all_ids_vec . len ()) as u32) , w) }), input: Enumerate( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < & str , std :: string :: String > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | s | s . to_string () }), diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap index 06befe0c8323..a45a39badd06 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap @@ -5,12 +5,12 @@ expression: ir.surface_syntax_string() 1v1 = source_iter ({ use crate :: __staged :: cluster :: map_reduce :: * ; vec ! ["abc" , "abc" , "xyz" , "abc"] }); 2v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < & str , std :: string :: String > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | s | s . to_string () })); 3v1 = enumerate (); -4v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use crate :: __staged :: cluster :: map_reduce :: * ; let all_ids_vec = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ClusterId :: from_raw ((i % all_ids_vec . len ()) as u32) , w) })); +4v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use crate :: __staged :: cluster :: map_reduce :: * ; let all_ids_vec = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ClusterId :: from_raw ((i % all_ids_vec . len ()) as u32) , w) })); 5v1 = map (| (id , data) : (hydroflow_plus :: ClusterId < _ > , std :: string :: String) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < std :: string :: String > (& data) . unwrap () . into ()) }); 6v1 = dest_sink ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env = FAKE ; let p1_port = "port_0" ; { env . port (p1_port) . connect_local_blocking :: < ConnectedDemux < ConnectedDirect > > () . into_sink () } }); 7v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env = FAKE ; let p2_port = "port_1" ; { env . port (p2_port) . connect_local_blocking :: < ConnectedTagged < ConnectedDirect > > () . into_source () } }); 8v1 = map (| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: map_reduce :: Worker > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (std :: string :: String , i32) > (& b) . unwrap ()) }); -9v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , (std :: string :: String , i32)) , (std :: string :: String , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); +9v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , (std :: string :: String , i32)) , (std :: string :: String , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); 10v1 = reduce_keyed :: < 'static > (stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | total , count | * total += count })); 11v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < (std :: string :: String , i32) , () > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | (string , count) | println ! ("{}: {}" , string , count) })); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 2a93b6e9c6f4..a96b377e89c1 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -44,7 +44,7 @@ expression: built.ir() Union( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_1, @@ -55,7 +55,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -124,7 +124,7 @@ expression: built.ir() ), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -156,7 +156,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ballot_num | Ballot { num : ballot_num , proposer_id : p_id } }), input: Map { @@ -184,10 +184,10 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Source { - source: Interval( - stageleft :: runtime_support :: type_hint :: < core :: time :: Duration > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) }), + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, ), location_kind: Cluster( 0, @@ -211,7 +211,7 @@ expression: built.ir() ), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 1, @@ -226,7 +226,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -237,18 +237,18 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), input: CrossSingleton( CrossSingleton( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -280,7 +280,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Inspect { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), input: Map { @@ -378,7 +378,7 @@ expression: built.ir() }, ), Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -409,16 +409,16 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), input: CrossSingleton( Persist( Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), input: Tee { inner: , }, @@ -475,13 +475,13 @@ expression: built.ir() inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { inner: , }, @@ -531,15 +531,15 @@ expression: built.ir() Tee { inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index , value : Some (payload) } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index , value : Some (payload) } }), input: CrossSingleton( CrossSingleton( Enumerate( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 2, @@ -554,7 +554,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -565,7 +565,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), @@ -619,23 +619,23 @@ expression: built.ir() input: DeferTick( Difference( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), input: FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = Some (value) ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = Some (value) ; } }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { inner: : Union( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 1, @@ -650,7 +650,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -661,17 +661,17 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), input: CrossSingleton( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -686,7 +686,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -697,23 +697,23 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } } }), input: CrossSingleton( Tee { inner: , @@ -724,7 +724,7 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : None } }), input: CrossSingleton( Difference( FlatMap { @@ -734,7 +734,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: , }, @@ -819,7 +819,7 @@ expression: built.ir() }, Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { inner: , }, @@ -837,10 +837,10 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), input: AntiJoin( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { inner: , }, @@ -860,12 +860,12 @@ expression: built.ir() 1, ), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in (prev_checkpoint . unwrap_or (0)) .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = Some (new_checkpoint) ; } CheckpointOrP2a :: P2a (p2a) => { if prev_checkpoint . map (| prev | p2a . slot > prev) . unwrap_or (true) && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in (prev_checkpoint . unwrap_or (0)) .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = Some (new_checkpoint) ; } CheckpointOrP2a :: P2a (p2a) => { if prev_checkpoint . map (| prev | p2a . slot > prev) . unwrap_or (true) && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), input: Persist( Union( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( Tee { inner: , @@ -876,14 +876,14 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), input: Delta( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , ()) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , ()) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { inner: : ReduceKeyed { @@ -920,7 +920,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -940,7 +940,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: , }, @@ -977,15 +977,15 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { inner: : Sort( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -1000,7 +1000,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -1011,15 +1011,15 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), input: AntiJoin( Tee { inner: , @@ -1053,7 +1053,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < usize > , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | v | v }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < usize > , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let expected_next_slot = std :: cmp :: max (filled_slot . map (| v | v + 1) . unwrap_or (0) , highest_seq . map (| v | v + 1) . unwrap_or (0) ,) ; if sorted_payload . seq == expected_next_slot { * filled_slot = Some (sorted_payload . seq) ; } } }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < usize > , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let expected_next_slot = std :: cmp :: max (filled_slot . map (| v | v + 1) . unwrap_or (0) , highest_seq . map (| v | v + 1) . unwrap_or (0) ,) ; if sorted_payload . seq == expected_next_slot { * filled_slot = Some (sorted_payload . seq) ; } } }), input: CrossSingleton( Tee { inner: , @@ -1100,16 +1100,16 @@ expression: built.ir() input: DeferTick( Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == (last_seq . map (| s | s + 1) . unwrap_or (0)) , "Hole in log between seq {:?} and {}" , * last_seq , payload . seq) ; * last_seq = Some (payload . seq) ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == (last_seq . map (| s | s + 1) . unwrap_or (0)) , "Hole in log between seq {:?} and {}" , * last_seq , payload . seq) ; * last_seq = Some (payload . seq) ; } }), input: Persist( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { inner: , @@ -1194,7 +1194,7 @@ expression: built.ir() 2, ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , ()) , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (leader_id , _) | leader_id }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , ()) , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (leader_id , _) | leader_id }), input: Network { from_location: Cluster( 0, @@ -1226,7 +1226,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use crate :: __staged :: cluster :: paxos :: * ; move | _ | () }), input: Tee { @@ -1249,7 +1249,7 @@ expression: built.ir() Tee { inner: : Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , (u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { from_location: Cluster( 3, @@ -1264,7 +1264,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -1275,15 +1275,15 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), input: Tee { inner: , }, @@ -1306,7 +1306,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { inner: , }, @@ -1325,15 +1325,15 @@ expression: built.ir() ), input: Union( FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , KvPayload { key : i as u32 , value : c_id })) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , KvPayload { key : i as u32 , value : c_id })) }), input: Tee { inner: : Delta( Tee { inner: : Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -1350,7 +1350,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , cur_leader) | (cur_leader , KvPayload { key , value : c_id }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , cur_leader) | (cur_leader , KvPayload { key , value : c_id }) }), input: CrossSingleton( Tee { inner: , @@ -1387,7 +1387,7 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < std :: time :: SystemTime , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node = 1usize ; move | now | (0 .. num_clients_per_node) . map (move | virtual_id | (virtual_id , now)) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { inner: , }, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__simple_cluster__tests__simple_cluster.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__simple_cluster__tests__simple_cluster.snap index 8fd05b1df972..a011fb746712 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__simple_cluster__tests__simple_cluster.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__simple_cluster__tests__simple_cluster.snap @@ -4,7 +4,7 @@ expression: built.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < () > , (hydroflow_plus :: location :: ClusterId < () > , i32)) , () > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | (id , d) | println ! ("node received: ({}, {:?})" , id , d) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32)) , () > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | (id , d) | println ! ("node received: ({}, {:?})" , id , d) }), input: Network { from_location: Cluster( 1, @@ -19,7 +19,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| data | { hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus :: location :: ClusterId < () > , i32) > (& data) . unwrap () . into () }", + "| data | { hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) > (& data) . unwrap () . into () }", ], }, ), @@ -30,13 +30,13 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < () > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus :: location :: ClusterId < () > , i32) > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < () > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) > (& b) . unwrap ()) }", ], }, ), ), input: Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus :: location :: ClusterId < () > , i32) , () > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; let cluster_self_id = hydroflow_plus :: ClusterId :: < () > :: from_raw (__hydroflow_plus_cluster_self_id_1) ; move | n | println ! ("cluster received: {:?} (self cluster id: {})" , n , cluster_self_id) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) , () > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; let cluster_self_id = hydroflow_plus :: ClusterId :: < () > :: from_raw (__hydroflow_plus_cluster_self_id_1) ; move | n | println ! ("cluster received: {:?} (self cluster id: {})" , n , cluster_self_id) }), input: Network { from_location: Process( 0, @@ -51,7 +51,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , (hydroflow_plus :: location :: ClusterId < () > , i32)) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus :: location :: ClusterId < () > , i32) > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32)) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) > (& data) . unwrap () . into ()) }", ], }, ), @@ -62,18 +62,18 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus :: location :: ClusterId < () > , i32) > (& res . unwrap ()) . unwrap () }", + "| res | { hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) > (& res . unwrap ()) . unwrap () }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: ClusterId < () > , i32) , (hydroflow_plus :: location :: ClusterId < () > , (hydroflow_plus :: location :: ClusterId < () > , i32)) > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | (id , n) | (id , (id , n)) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) , (hydroflow_plus :: location :: cluster :: ClusterId < () > , (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32)) > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | (id , n) | (id , (id , n)) }), input: Delta( CrossProduct( Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < & hydroflow_plus :: location :: ClusterId < () > , hydroflow_plus :: location :: ClusterId < () > > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | & id | id }), + f: stageleft :: runtime_support :: fn1_type_hint :: < & hydroflow_plus :: location :: cluster :: ClusterId < () > , hydroflow_plus :: location :: cluster :: ClusterId < () > > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | & id | id }), input: Source { source: Iter( unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < () > > > (__hydroflow_plus_cluster_ids_1) }, diff --git a/hydroflow_plus_test/src/distributed/first_ten.rs b/hydroflow_plus_test/src/distributed/first_ten.rs index 18b7703f8ab4..cead08979018 100644 --- a/hydroflow_plus_test/src/distributed/first_ten.rs +++ b/hydroflow_plus_test/src/distributed/first_ten.rs @@ -1,5 +1,6 @@ use hydroflow_plus::*; -use location::{ExternalBincodeSink, ExternalProcess}; +use location::external_process::ExternalBincodeSink; +use location::ExternalProcess; use serde::{Deserialize, Serialize}; use stageleft::*; From 3143bf5010600c0825db5f9348c03883cb49fdc0 Mon Sep 17 00:00:00 2001 From: Rohit Kulshreshtha Date: Tue, 5 Nov 2024 11:11:09 -0800 Subject: [PATCH 24/74] feat(Gossip KV): Merge Gossip KV into `main`. (#1535) There are some TODOs in the code for which I have created issues for 0.10 milestone. [P0 - Must Have for 0.10](https://github.com/hydro-project/hydroflow/issues?q=is:open+label:Datastores/Gossip-KV+milestone:0.10+label:P0) [P1 - Good to have / stretch goals](https://github.com/hydro-project/hydroflow/issues?q=is:open+label:Datastores/Gossip-KV+milestone:0.10+label:P1) [P2 - Low Priority](https://github.com/hydro-project/hydroflow/issues?q=is:open+label:Datastores/Gossip-KV+milestone:0.10+label:P2) Closes #1530. --------- Co-authored-by: Mingwei Samuel --- .idea/hydroflow.iml | 3 + Cargo.lock | 741 ++++++++++++++++- Cargo.toml | 1 + datastores/gossip_kv/Cargo.toml | 45 ++ datastores/gossip_kv/Makefile | 83 ++ datastores/gossip_kv/README.md | 83 ++ datastores/gossip_kv/cli/Dockerfile | 12 + datastores/gossip_kv/cli/main.rs | 119 +++ .../deployment/aws/terraform/.gitignore | 1 + .../deployment/aws/terraform/main.tf | 561 +++++++++++++ .../deployment/aws/terraform/outputs.tf | 34 + .../deployment/aws/terraform/terraform.tf | 30 + .../deployment/aws/terraform/variables.tf | 29 + .../gossip_kv/deployment/local/objects.yaml | 90 +++ .../local/updated_seed_node_config.yaml | 17 + datastores/gossip_kv/kv/lattices/mod.rs | 248 ++++++ datastores/gossip_kv/kv/lib.rs | 273 +++++++ datastores/gossip_kv/kv/membership.rs | 85 ++ datastores/gossip_kv/kv/model.rs | 166 ++++ datastores/gossip_kv/kv/server.rs | 763 ++++++++++++++++++ datastores/gossip_kv/kv/util.rs | 87 ++ .../gossip_kv/load_test_server/Dockerfile | 11 + .../gossip_kv/load_test_server/server.rs | 226 ++++++ datastores/gossip_kv/server/.gitignore | 1 + datastores/gossip_kv/server/Dockerfile | 13 + datastores/gossip_kv/server/README.md | 44 + .../gossip_kv/server/baseimage.Dockerfile | 23 + datastores/gossip_kv/server/config/mod.rs | 130 +++ .../server/config/static/default.toml | 5 + .../server/config/static/development.toml | 0 datastores/gossip_kv/server/main.rs | 179 ++++ datastores/gossip_kv/server/membership.rs | 37 + 32 files changed, 4119 insertions(+), 21 deletions(-) create mode 100644 datastores/gossip_kv/Cargo.toml create mode 100644 datastores/gossip_kv/Makefile create mode 100644 datastores/gossip_kv/README.md create mode 100644 datastores/gossip_kv/cli/Dockerfile create mode 100644 datastores/gossip_kv/cli/main.rs create mode 100644 datastores/gossip_kv/deployment/aws/terraform/.gitignore create mode 100644 datastores/gossip_kv/deployment/aws/terraform/main.tf create mode 100644 datastores/gossip_kv/deployment/aws/terraform/outputs.tf create mode 100644 datastores/gossip_kv/deployment/aws/terraform/terraform.tf create mode 100644 datastores/gossip_kv/deployment/aws/terraform/variables.tf create mode 100644 datastores/gossip_kv/deployment/local/objects.yaml create mode 100644 datastores/gossip_kv/deployment/local/updated_seed_node_config.yaml create mode 100644 datastores/gossip_kv/kv/lattices/mod.rs create mode 100644 datastores/gossip_kv/kv/lib.rs create mode 100644 datastores/gossip_kv/kv/membership.rs create mode 100644 datastores/gossip_kv/kv/model.rs create mode 100644 datastores/gossip_kv/kv/server.rs create mode 100644 datastores/gossip_kv/kv/util.rs create mode 100644 datastores/gossip_kv/load_test_server/Dockerfile create mode 100644 datastores/gossip_kv/load_test_server/server.rs create mode 100644 datastores/gossip_kv/server/.gitignore create mode 100644 datastores/gossip_kv/server/Dockerfile create mode 100644 datastores/gossip_kv/server/README.md create mode 100644 datastores/gossip_kv/server/baseimage.Dockerfile create mode 100644 datastores/gossip_kv/server/config/mod.rs create mode 100644 datastores/gossip_kv/server/config/static/default.toml create mode 100644 datastores/gossip_kv/server/config/static/development.toml create mode 100644 datastores/gossip_kv/server/main.rs create mode 100644 datastores/gossip_kv/server/membership.rs diff --git a/.idea/hydroflow.iml b/.idea/hydroflow.iml index fc353ac331a9..03944299ddf0 100644 --- a/.idea/hydroflow.iml +++ b/.idea/hydroflow.iml @@ -36,6 +36,9 @@ + + + diff --git a/Cargo.lock b/Cargo.lock index a53092a9c76c..d4d39f542cae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -306,6 +306,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "benches" version = "0.0.0" @@ -342,6 +348,9 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "block-buffer" @@ -628,6 +637,26 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +dependencies = [ + "async-trait", + "convert_case", + "json5", + "lazy_static", + "nom 7.1.3", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust", +] + [[package]] name = "console" version = "0.15.8" @@ -651,6 +680,35 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -776,9 +834,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "6.0.1" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", @@ -836,6 +894,15 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + [[package]] name = "dunce" version = "1.0.5" @@ -860,6 +927,15 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_filter" version = "0.1.2" @@ -926,6 +1002,18 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +[[package]] +name = "filetime" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "fnv" version = "1.0.7" @@ -958,9 +1046,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -968,9 +1056,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" @@ -985,9 +1073,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -1004,9 +1092,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1015,21 +1103,27 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1087,6 +1181,70 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gossip_kv" +version = "0.1.0" +dependencies = [ + "clap", + "config", + "governor", + "hostname", + "hydroflow", + "lattices", + "lazy_static", + "notify", + "prometheus", + "rand", + "serde", + "serde_json", + "shlex", + "tokio", + "tracing", + "tracing-subscriber", + "uuid", + "warp", +] + +[[package]] +name = "governor" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0746aa765db78b521451ef74221663b57ba595bf83f75d0ce23cc09447c8139f" +dependencies = [ + "cfg-if", + "dashmap", + "futures-sink", + "futures-timer", + "futures-util", + "no-std-compat", + "nonzero_ext", + "parking_lot 0.12.3", + "portable-atomic", + "quanta", + "rand", + "smallvec", + "spinning_top", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -1097,6 +1255,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "hashbrown" version = "0.14.5" @@ -1113,6 +1277,30 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +[[package]] +name = "headers" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +dependencies = [ + "base64", + "bytes", + "headers-core", + "http 0.2.12", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http 0.2.12", +] + [[package]] name = "heck" version = "0.4.1" @@ -1152,6 +1340,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hostname" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" +dependencies = [ + "cfg-if", + "libc", + "windows", +] + [[package]] name = "http" version = "0.2.12" @@ -1163,12 +1362,40 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.1.0" @@ -1203,7 +1430,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.20.1", ] [[package]] @@ -1424,6 +1651,30 @@ dependencies = [ "stageleft_tool", ] +[[package]] +name = "hyper" +version = "0.14.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1509,6 +1760,26 @@ dependencies = [ "str_stack", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "insta" version = "1.39.0" @@ -1593,6 +1864,37 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lattices" version = "0.5.7" @@ -1637,6 +1939,17 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.3", +] + [[package]] name = "libssh2-sys" version = "0.3.0" @@ -1721,6 +2034,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minicov" version = "0.3.5" @@ -1731,6 +2060,12 @@ dependencies = [ "walkdir", ] +[[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.4" @@ -1740,6 +2075,18 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "mio" version = "1.0.2" @@ -1752,6 +2099,24 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 0.2.12", + "httparse", + "log", + "memchr", + "mime", + "spin", + "version_check", +] + [[package]] name = "multiplatform_test" version = "0.2.0" @@ -1798,12 +2163,51 @@ dependencies = [ "libc", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "nom" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff" +[[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 = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.6.0", + "filetime", + "inotify", + "kqueue", + "libc", + "log", + "mio 0.8.11", + "walkdir", + "windows-sys 0.48.0", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1923,6 +2327,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-multimap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" +dependencies = [ + "dlv-list", + "hashbrown 0.13.2", +] + [[package]] name = "overload" version = "0.1.1" @@ -1989,12 +2403,63 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.75", +] + +[[package]] +name = "pest_meta" +version = "2.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project" version = "1.1.5" @@ -2145,10 +2610,31 @@ checksum = "6ab1427f3d2635891f842892dda177883dca0639e05fe66796a62c9d2f23b49c" dependencies = [ "byteorder", "libc", - "nom", + "nom 2.2.1", "rustc_version", ] +[[package]] +name = "prometheus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.3", + "protobuf", + "thiserror", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + [[package]] name = "pusherator" version = "0.0.8" @@ -2255,6 +2741,21 @@ dependencies = [ "serde", ] +[[package]] +name = "quanta" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + [[package]] name = "quick-xml" version = "0.26.0" @@ -2313,6 +2814,15 @@ dependencies = [ "rand", ] +[[package]] +name = "raw-cpuid" +version = "11.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "rayon" version = "1.10.0" @@ -2437,6 +2947,28 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags 2.6.0", + "serde", + "serde_derive", +] + +[[package]] +name = "rust-ini" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rust-sitter" version = "0.4.3" @@ -2648,6 +3180,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2760,6 +3304,21 @@ 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 = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "ssh2" version = "0.9.4" @@ -3036,6 +3595,15 @@ dependencies = [ "timely-logging-master", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -3070,7 +3638,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 1.0.2", "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", @@ -3111,7 +3679,19 @@ dependencies = [ "futures-util", "log", "tokio", - "tungstenite", + "tungstenite 0.20.1", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.21.0", ] [[package]] @@ -3187,12 +3767,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3304,6 +3891,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8ddffe35a0e5eeeadf13ff7350af564c6e73993a24db62caee1822b185c2600" +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "try_match" version = "0.4.2" @@ -3361,7 +3954,26 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 0.2.12", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", "httparse", "log", "rand", @@ -3377,6 +3989,21 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -3398,6 +4025,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.13" @@ -3440,6 +4073,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" @@ -3490,6 +4132,44 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "headers", + "http 0.2.12", + "hyper", + "log", + "mime", + "mime_guess", + "multer", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-tungstenite 0.21.0", + "tokio-util", + "tower-service", + "tracing", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3679,6 +4359,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -3920,6 +4610,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 8b9deaa71ad9..ab8fef76bc85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ # See "Adding new crates" and "Moving crates" addendum sections in `RELEASING.md` members = [ "benches", + "datastores/gossip_kv", "hydro_deploy/core", "hydro_deploy/hydro_cli", "hydro_deploy/hydro_cli_examples", diff --git a/datastores/gossip_kv/Cargo.toml b/datastores/gossip_kv/Cargo.toml new file mode 100644 index 000000000000..b59765578698 --- /dev/null +++ b/datastores/gossip_kv/Cargo.toml @@ -0,0 +1,45 @@ +[package] +description = "Gossip KV Library" +name = "gossip_kv" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +publish = false + +[dependencies] +clap = { version = "4.5.4", features = ["derive"] } +config = "0.14.0" +governor = "0.7.0" +hostname = "0.4.0" +hydroflow = { path="../../hydroflow" } +lattices = { path = '../../lattices'} +lazy_static = "1.5.0" +# The specific set of features for Notify are picked to disable the default cross-beam channels (cause problems with +# tokio) and use std channels. See docs for more information: https://docs.rs/notify/6.1.1/notify/ +notify = { version = "6.1.1", default-features = false, features = ["macos_kqueue"] } +prometheus = "0.13.4" +rand = "0.8.5" +serde = "1.0.203" +serde_json = "1.0.117" +shlex = "1.3.0" +tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros"] } +tracing = "0.1.40" +tracing-subscriber = {version = "0.3.18", features = ["env-filter"]} +uuid = { version = "1.9.1", features = ["v4"] } +warp = "0.3.7" + +[[bin]] +name = "gossip_server" +path = "server/main.rs" + +[[bin]] +name = "load_test_server" +path = "load_test_server/server.rs" + +[[bin]] +name = "gossip_cli" +path = "cli/main.rs" + +[lib] +name = "gossip_kv" +path = "kv/lib.rs" \ No newline at end of file diff --git a/datastores/gossip_kv/Makefile b/datastores/gossip_kv/Makefile new file mode 100644 index 000000000000..dbfe144f0d78 --- /dev/null +++ b/datastores/gossip_kv/Makefile @@ -0,0 +1,83 @@ +# Makefile + +## Minikube Options. +MINIKUBE_DISK_SIZE:=100g +MINIKUBE_CPUS:=16 +MINIKUBE_MEMORY:=32768 + +BASE_IMAGE_VERSION:=latest +SERVER_IMAGE_VERSION:=latest +CLI_IMAGE_VERSION:=latest +LOAD_TEST_IMAGE_VERSION:=latest + +# Docker Image Tags +BASE_IMAGE_TAG:=hydroflow-gossip-kv-base-image:$(BASE_IMAGE_VERSION) +SERVER_IMAGE_TAG:=hydroflow-gossip-kv-server:$(SERVER_IMAGE_VERSION) +CLI_IMAGE_TAG:=hydroflow-gossip-kv-cli:$(CLI_IMAGE_VERSION) +LOAD_TEST_IMAGE_TAG:=hydroflow-gossip-kv-load-test:$(LOAD_TEST_IMAGE_VERSION) + +AWS_TERRAFORM_PATH=../../datastores/gossip_kv/deployment/aws/terraform + +# Target to start Minikube with specific options +start_minikube: + minikube start --disk-size=$(MINIKUBE_DISK_SIZE) --cpus=$(MINIKUBE_CPUS) --memory=$(MINIKUBE_MEMORY) + @echo "Please run 'eval \$$(minikube docker-env)' to use the Minikube Docker daemon" + +# Target to build the Docker images +build_docker_images: build_base_image build_server_image build_cli_image build_load_test_image + +build_base_image: + docker build -t "$(BASE_IMAGE_TAG)" -f ../../datastores/gossip_kv/server/baseimage.Dockerfile ../.. + +build_server_image: + docker build -t "$(SERVER_IMAGE_TAG)" -f ../../datastores/gossip_kv/server/Dockerfile ../.. + +build_cli_image: + docker build -t "$(CLI_IMAGE_TAG)" -f ../../datastores/gossip_kv/cli/Dockerfile ../.. + +build_load_test_image: + docker build -t "$(LOAD_TEST_IMAGE_TAG)" -f ../../datastores/gossip_kv/load_test_server/Dockerfile ../.. + +# Target to clean up the Minikube cluster +clean_local: + minikube delete + +# Target to deploy the Gossip KV Server to the Minikube cluster +deploy_local: + kubectl apply -f ../../datastores/gossip_kv/server/local + +# Target to delete the Minikube cluster and build again +rebuild_local: clean_local start_minikube build_docker_images + +aws_terraform_init: + terraform -chdir="$(AWS_TERRAFORM_PATH)" init + +aws_terraform_apply: + terraform -chdir="$(AWS_TERRAFORM_PATH)" apply + +aws_setup_kubectl: + @echo "Setting up kubectl to work with AWS EKS Cluster" + aws eks update-kubeconfig --region $$(terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw region) --name $$(terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw cluster_name) + +aws_upload_docker_images: build_docker_images + $(eval SERVER_REPO_URL := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -json repository_urls | jq -r '.["gossip_kv_server"]')) + $(eval CLI_REPO_URL := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -json repository_urls | jq -r '.["gossip_kv_cli"]')) + $(eval LOAD_TEST_REPO_URL := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -json repository_urls | jq -r '.["gossip_kv_load_test"]')) + $(eval REGION := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw region)) + docker tag $(SERVER_IMAGE_TAG) $(SERVER_REPO_URL):$(SERVER_IMAGE_VERSION) + docker tag $(CLI_IMAGE_TAG) $(CLI_REPO_URL):$(CLI_IMAGE_VERSION) + docker tag $(LOAD_TEST_IMAGE_TAG) $(LOAD_TEST_REPO_URL):$(LOAD_TEST_IMAGE_VERSION) + aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(SERVER_REPO_URL) + docker push $(SERVER_REPO_URL):$(SERVER_IMAGE_VERSION) + aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(CLI_REPO_URL) + docker push $(CLI_REPO_URL):$(CLI_IMAGE_VERSION) + aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(LOAD_TEST_REPO_URL) + docker push $(LOAD_TEST_REPO_URL):$(LOAD_TEST_IMAGE_VERSION) + +aws_tunnel_grafana: + $(eval GRAFANA_PORT := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw grafana_port)) + kubectl port-forward svc/grafana $(GRAFANA_PORT):$(GRAFANA_PORT) + +aws_tunnel_prometheus: + $(eval PROMETHEUS_PORT := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw prometheus_port)) + kubectl port-forward svc/prometheus $(PROMETHEUS_PORT):$(PROMETHEUS_PORT) \ No newline at end of file diff --git a/datastores/gossip_kv/README.md b/datastores/gossip_kv/README.md new file mode 100644 index 000000000000..b282dafe0f1b --- /dev/null +++ b/datastores/gossip_kv/README.md @@ -0,0 +1,83 @@ +# Gossip Key-Value Store + +# Architecture +A gossip-based key-value store library. + +``` +┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐ +│ Process │ │ Process │ │ Process │ │ Process │ +│ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │ +│ │ User Application │ │ │ │ User Application │ │ │ │ User Application │ │ │ │ User Application │ │ +│ └────────▲──┬─────────┘ │ │ └─────────▲─┬─────────┘ │ │ └─────────▲─┬─────────┘ │ │ └─────────▲─┬─────────┘ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ ┌────────┴──▼─────────┐ │ │ ┌─────────┴─▼─────────┐ │ │ ┌─────────┴─▼─────────┐ │ │ ┌─────────┴─▼─────────┐ │ +│ │ Gossip KV Store │ │◄──►│ │ Gossip KV Store │ │◄──►│ │ Gossip KV Store │ │◄──►│ │ Gossip KV Store │ │ +│ └─────────────────────┘ │ │ └─────────────────────┘ │ │ └─────────────────────┘ │ │ └─────────────────────┘ │ +└─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘ +``` + +## Data Model +TODO: Elaborate +* User Application manipulate data using client library +* Replicated to all members of the gossip cluster +* Eventually consistent + +```json +{ + "sys": { + "members": { + "member_id": { + "port": 1234, + "protocol": "v1" + } + } + }, + "usr": { + "key 1": "value 1", + "key 2": "value 2" + } +} +``` +Data in divided into two sections: A `sys` section that contains system data used by the key-value store itself. The +`usr` section contains user-defined data. + +### `sys` Data +The `sys` data section contains system data / state that is required by the key-value store to do it's work. + +#### Fields + +### `usr` Data + +## Protocol + +## Checkpoints + +# Running Locally Using Minikube +## Install Docker Desktop +```shell +brew install --cask docker +``` +### Run docker (macOS) +``` +open -a Docker +``` + +## Install Minikube +Read more [here](https://minikube.sigs.k8s.io/docs/start/) +```shell +brew install minikube +``` + +## Start Minikube +```shell +minikube start +``` + +## Install `kubectl` +```shell +brew install kubectl +``` +## Configure Minikube to use your Docker Environment +```shell +eval $(minikube -p minikube docker-env) +``` \ No newline at end of file diff --git a/datastores/gossip_kv/cli/Dockerfile b/datastores/gossip_kv/cli/Dockerfile new file mode 100644 index 000000000000..208948c50766 --- /dev/null +++ b/datastores/gossip_kv/cli/Dockerfile @@ -0,0 +1,12 @@ +FROM "hydroflow-gossip-kv-base-image:latest" AS builder +WORKDIR /usr/src/gossip-kv-server +COPY . . +RUN find . +RUN cargo build --release --workspace -p gossip_kv + +FROM rustlang/rust:nightly-slim +COPY --from=builder /usr/src/gossip-kv-server/target/release/gossip_cli /usr/local/bin/gossip_cli + +RUN apt-get update && apt-get install -y \ + dnsutils \ + && rm -rf /var/lib/apt/lists/* diff --git a/datastores/gossip_kv/cli/main.rs b/datastores/gossip_kv/cli/main.rs new file mode 100644 index 000000000000..8834da9ce459 --- /dev/null +++ b/datastores/gossip_kv/cli/main.rs @@ -0,0 +1,119 @@ +use std::net::SocketAddr; + +use clap::{CommandFactory, Parser, Subcommand}; +use gossip_kv::{ClientRequest, ClientResponse, Key}; +use hydroflow::util::{bind_udp_bytes, ipv4_resolve}; +use hydroflow::{hydroflow_syntax, tokio, DemuxEnum}; +use tracing::error; + +/// CLI program to interact with Layer 0 gossip store. +#[derive(Debug, Parser)] +struct Opts { + #[clap(short, long, help = "Server address to connect to.")] + server_address: Option, +} + +/// Dummy app for using clap to process commands for interactive CLI. +#[derive(Debug, Parser)] +#[command(multicall = true)] +struct InteractiveApp { + #[clap(subcommand)] + commands: InteractiveCommands, +} + +#[derive(Debug, Subcommand, DemuxEnum)] +enum InteractiveCommands { + /// Get a value from the store. + Get { + #[arg(value_parser = parse_key, required = true, help = "Key to get")] + key: Key, + }, + /// Upsert a value in the store. + Set { + #[arg(value_parser = parse_key, required = true, help = "Key to set")] + key: Key, + value: String, + }, + /// Delete a value from the store. + Delete { + #[arg(value_parser = parse_key, required = true, help = "Key to delete")] + key: Key, + }, + /// Exit the application. + Exit, +} + +/// Allows clap to parse Keys from user input. +fn parse_key(s: &str) -> Result { + s.parse::().map_err(|e| e.to_string()) +} + +/// Parse a command from a line of input. +fn parse_command(line: String) -> Option { + // Override how help is handled. + if line.trim() == "help" { + InteractiveApp::command() + .help_template("\nAvailable Commands: \n{subcommands}") + .print_help() + .unwrap(); + return None; + } + + // Split quoted string into parts. + let line_parts = shlex::split(&line); + + if line_parts.is_none() { + error!("\nUnable to parse command."); + return None; + } + + // Provide split parts to clap to process. + let maybe_parsed = InteractiveApp::try_parse_from(line_parts.unwrap()); + + match maybe_parsed { + Err(e) => { + // Problem with the parsed result. This displays some help. + error!("\n{}", e); + None + } + Ok(cli) => Some(cli.commands), + } +} + +#[hydroflow::main] +async fn main() { + tracing_subscriber::fmt::init(); + + let opts = Opts::parse(); + + // Bind to OS-assigned port on localhost. + let address = ipv4_resolve("0.0.0.0:0").unwrap(); + + // Default to localhost:3000 if not provided. + let server_address = opts.server_address.map_or_else( + || ipv4_resolve("localhost:3001").unwrap(), + |s| ipv4_resolve(&s).unwrap(), + ); + + // Setup UDP sockets for communication. + let (outbound, inbound, _) = bind_udp_bytes(address).await; + + let mut cli = hydroflow_syntax! { + inbound_messages = source_stream_serde(inbound) -> map(Result::unwrap) -> for_each(|(response, _addr): (ClientResponse, SocketAddr)| println!("{:?}", response)); + + outbound_messages = union() -> dest_sink_serde(outbound); + + // Parse commands from stdin. + commands = source_stdin() + -> filter_map(|line| parse_command(line.unwrap())) + -> demux_enum::(); + + commands[Get] -> map(|(key,)| (ClientRequest::Get {key}, server_address)) -> outbound_messages; + commands[Set] -> map(|(key, value)| (ClientRequest::Set {key, value}, server_address)) -> outbound_messages; + commands[Delete] -> map(|(key,)| (ClientRequest::Delete {key}, server_address)) -> outbound_messages; + commands[Exit] -> for_each(|()| std::process::exit(0)); // TODO: Graceful shutdown https://github.com/hydro-project/hydroflow/issues/1253 + + }; + + cli.run_async().await; +} diff --git a/datastores/gossip_kv/deployment/aws/terraform/.gitignore b/datastores/gossip_kv/deployment/aws/terraform/.gitignore new file mode 100644 index 000000000000..3fa8c86b7b04 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/terraform/.gitignore @@ -0,0 +1 @@ +.terraform diff --git a/datastores/gossip_kv/deployment/aws/terraform/main.tf b/datastores/gossip_kv/deployment/aws/terraform/main.tf new file mode 100644 index 000000000000..47799f259c45 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/terraform/main.tf @@ -0,0 +1,561 @@ +provider "aws" { + region = var.region +} + +# Filter out local zones, which are not currently supported +# with managed node groups +data "aws_availability_zones" "available" { + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +data "aws_caller_identity" "current" {} + +locals { + cluster_name = "anna-load-test-${random_string.suffix.result}" + account_id = data.aws_caller_identity.current.account_id +} + +resource "random_string" "suffix" { + length = 8 + special = false +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "5.8.1" + + name = "anna-load-test-vpc" + + cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + + map_public_ip_on_launch = true + public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] + + enable_dns_hostnames = true + + public_subnet_tags = { + "kubernetes.io/role/elb" = 1 + } +} + +module "eks_cluster" { + source = "terraform-aws-modules/eks/aws" + version = "20.24.3" + + cluster_name = local.cluster_name + cluster_version = "1.31" + + cluster_endpoint_public_access = true + enable_cluster_creator_admin_permissions = true + + cluster_addons = { + aws-ebs-csi-driver = { + service_account_role_arn = module.irsa-ebs-csi.iam_role_arn + } + } + + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.public_subnets + + eks_managed_node_group_defaults = { + ami_type = "AL2_x86_64" + } + + eks_managed_node_groups = { + one = { + name = "servers" + + instance_types = [var.instance_type] + + min_size = 1 + max_size = 3 + desired_size = 2 + } + } +} + +# https://aws.amazon.com/blogs/containers/amazon-ebs-csi-driver-is-now-generally-available-in-amazon-eks-add-ons/ +data "aws_iam_policy" "ebs_csi_policy" { + arn = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy" +} + +module "irsa-ebs-csi" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" + version = "5.39.0" + + create_role = true + role_name = "AmazonEKSTFEBSCSIRole-${module.eks_cluster.cluster_name}" + provider_url = module.eks_cluster.oidc_provider + role_policy_arns = [data.aws_iam_policy.ebs_csi_policy.arn] + oidc_fully_qualified_subjects = ["system:serviceaccount:kube-system:ebs-csi-controller-sa"] +} + +variable "ecr_repositories" { + description = "List of ECR repository names" + type = list(string) + default = ["gossip_kv_server", "gossip_kv_cli", "gossip_kv_load_test"] +} + +module "ecr" { + source = "terraform-aws-modules/ecr/aws" + version = "2.3.0" + + for_each = {for repo in var.ecr_repositories : repo => repo} + repository_name = each.value + + repository_read_write_access_arns = [data.aws_caller_identity.current.arn] + repository_lifecycle_policy = jsonencode({ + rules = [ + { + rulePriority = 1, + description = "Keep last 30 images", + selection = { + tagStatus = "tagged", + tagPrefixList = ["v"], + countType = "imageCountMoreThan", + countNumber = 30 + }, + action = { + type = "expire" + } + } + ] + }) + + repository_image_tag_mutability = "MUTABLE" + tags = { + Terraform = "true" + Environment = "dev" + } +} + +provider "kubernetes" { + host = module.eks_cluster.cluster_endpoint + cluster_ca_certificate = base64decode(module.eks_cluster.cluster_certificate_authority_data) + exec { + api_version = "client.authentication.k8s.io/v1beta1" + command = "aws" + args = [ + "eks", + "get-token", + "--cluster-name", + module.eks_cluster.cluster_name, + ] + } +} + +resource "kubernetes_stateful_set" "gossip_kv_seed_nodes" { + metadata { + name = "gossip-kv-seed-nodes" + labels = { + app = "gossip-kv-seed-nodes" + } + } + + spec { + service_name = "gossip-kv-seed-nodes" + replicas = 1 + + selector { + match_labels = { + app = "gossip-kv-seed-nodes" + } + } + + template { + metadata { + labels = { + app = "gossip-kv-seed-nodes" + } + annotations = { + "prometheus.io/scrape" : "true" + "prometheus.io/port" : var.pod_monitoring_port + } + } + + spec { + termination_grace_period_seconds = 5 + + container { + name = "gossip-kv-server" + image = "${module.ecr.gossip_kv_load_test.repository_url}:latest" + image_pull_policy = "Always" + + env { + name = "RUST_LOG" + value = "trace" + } + + env { + name = "RUST_BACKTRACE" + value = "full" + } + + port { + container_port = 3001 + protocol = "UDP" + } + + port { + container_port = var.pod_monitoring_port + protocol = "TCP" + } + + volume_mount { + name = "gossip-kv-dynamic-config" + mount_path = "/config/dynamic" + } + } + + volume { + name = "gossip-kv-dynamic-config" + + config_map { + name = "gossip-kv-dynamic-config" + } + } + } + } + } +} + +resource "kubernetes_deployment" "gossip_kv_cli" { + metadata { + name = "gossip-kv-cli" + labels = { + app = "gossip-kv-cli" + } + } + + spec { + replicas = 1 + + selector { + match_labels = { + app = "gossip-kv-cli" + } + } + + template { + metadata { + labels = { + app = "gossip-kv-cli" + } + } + + spec { + termination_grace_period_seconds = 5 + + container { + name = "gossip-kv-cli" + image = "${module.ecr.gossip_kv_cli.repository_url}:latest" + image_pull_policy = "Always" + command = ["/bin/sh"] + args = ["-c", "while true; do sleep 3600; done"] + tty = true + + env { + name = "RUST_LOG" + value = "info" + } + } + } + } + } +} + +resource "kubernetes_service" "gossip_kv_seed_nodes" { + metadata { + name = "gossip-kv-seed-nodes" + labels = { + app = "gossip-kv-seed-nodes" + } + } + + spec { + cluster_ip = "None" + selector = { + app = "gossip-kv-seed-nodes" + } + + port { + port = 3001 + target_port = 3001 + protocol = "UDP" + } + } +} + +resource "kubernetes_config_map" "gossip_kv_dynamic_config" { + metadata { + name = "gossip-kv-dynamic-config" + } + + data = { + "dynamic.toml" = < details.repository_url } +} + +output "grafana_port" { + description = "Port for Grafana UI" + value = var.grafana_port +} + +output "prometheus_port" { + description = "Port for Prometheus UI" + value = var.prometheus_port +} \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/aws/terraform/terraform.tf b/datastores/gossip_kv/deployment/aws/terraform/terraform.tf new file mode 100644 index 000000000000..6b54c550a221 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/terraform/terraform.tf @@ -0,0 +1,30 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.70.0" + } + + random = { + source = "hashicorp/random" + version = "~> 3.6.1" + } + + tls = { + source = "hashicorp/tls" + version = "~> 4.0.5" + } + + cloudinit = { + source = "hashicorp/cloudinit" + version = "~> 2.3.4" + } + + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.32.0" + } + } + + required_version = "~> 1.3" +} \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/aws/terraform/variables.tf b/datastores/gossip_kv/deployment/aws/terraform/variables.tf new file mode 100644 index 000000000000..fb8ff7995be5 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/terraform/variables.tf @@ -0,0 +1,29 @@ +variable "region" { + description = "AWS region where the resources will be created" + type = string + default = "us-east-2" +} + +variable "instance_type" { + description = "Instance type for the EC2 instances" + type = string + default = "t3.small" +} + +variable "grafana_port" { + description = "Port for Grafana UI" + type = number + default = 4001 +} + +variable "prometheus_port" { + description = "Port for Prometheus UI" + type = number + default = 4002 +} + +variable "pod_monitoring_port" { + description = "Port for monitoring pods using prometheus. Every pod runs a prometheus exporter on this port." + type = number + default = 4003 +} \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/local/objects.yaml b/datastores/gossip_kv/deployment/local/objects.yaml new file mode 100644 index 000000000000..7421df268e1e --- /dev/null +++ b/datastores/gossip_kv/deployment/local/objects.yaml @@ -0,0 +1,90 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: gossip-kv-seed-nodes + labels: + app: gossip-kv-seed-nodes +spec: + replicas: 3 + serviceName: gossip-kv-seed-nodes + selector: + matchLabels: + app: gossip-kv-seed-nodes + template: + metadata: + labels: + app: gossip-kv-seed-nodes + spec: + terminationGracePeriodSeconds: 5 # Really aggressive, but makes teardown faster. Not recommended beyond benchmarking. + containers: + - name: gossip-kv-server + image: docker.io/hydroflow/gossip-kv-server:latest + imagePullPolicy: IfNotPresent +# Uncomment the following for debugging +# command: [ "/bin/sh" ] +# args: [ "-c", "while true; do sleep 3600; done" ] + env: + - name: RUST_LOG + value: "trace" + - name: RUST_BACKTRACE + value: "full" + ports: + - containerPort: 3001 + protocol: UDP + volumeMounts: + - name: gossip-kv-dynamic-config + mountPath: /config/dynamic + volumes: + - name: gossip-kv-dynamic-config + configMap: + name: gossip-kv-dynamic-config +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gossip-kv-cli + labels: + app: gossip-kv-cli +spec: + replicas: 1 + selector: + matchLabels: + app: gossip-kv-cli + template: + metadata: + labels: + app: gossip-kv-cli + spec: + terminationGracePeriodSeconds: 5 # Really aggressive, but makes teardown faster. Not recommended beyond benchmarking. + containers: + - name: gossip-kv-cli + image: docker.io/hydroflow/gossip-kv-cli:latest + imagePullPolicy: IfNotPresent + command: ["/bin/sh"] + args: ["-c", "while true; do sleep 3600; done"] + tty: true + env: + - name: RUST_LOG + value: "info" +--- +apiVersion: v1 +kind: Service +metadata: + name: gossip-kv-seed-nodes + labels: + app: gossip-kv-seed-nodes +spec: + ports: + - port: 3001 + targetPort: 3001 + protocol: UDP + clusterIP: None + selector: + app: gossip-kv-seed-nodes +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: gossip-kv-dynamic-config +data: + dynamic.toml: | \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/local/updated_seed_node_config.yaml b/datastores/gossip_kv/deployment/local/updated_seed_node_config.yaml new file mode 100644 index 000000000000..bebeda8cb5e4 --- /dev/null +++ b/datastores/gossip_kv/deployment/local/updated_seed_node_config.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: gossip-kv-dynamic-config +data: + dynamic.toml: | + [[seed_nodes]] + id = "gossip-kv-seed-nodes-0" + address = "gossip-kv-seed-nodes-0.gossip-kv-seed-nodes.default.svc.cluster.local:3000" + + [[seed_nodes]] + id = "gossip-kv-seed-nodes-1" + address = "gossip-kv-seed-nodes-1.gossip-kv-seed-nodes.default.svc.cluster.local:3000" + + [[seed_nodes]] + id = "gossip-kv-seed-nodes-2" + address = "gossip-kv-seed-nodes-2.gossip-kv-seed-nodes.default.svc.cluster.local:3000" \ No newline at end of file diff --git a/datastores/gossip_kv/kv/lattices/mod.rs b/datastores/gossip_kv/kv/lattices/mod.rs new file mode 100644 index 000000000000..1aa5883b4bda --- /dev/null +++ b/datastores/gossip_kv/kv/lattices/mod.rs @@ -0,0 +1,248 @@ +use std::cmp::Ordering; +use std::collections::HashSet; +use std::hash::Hash; + +use hydroflow::lattices::{IsBot, IsTop, LatticeFrom, LatticeOrd, Merge}; +use serde::{Deserialize, Serialize}; + +/// A bounded set union lattice with a fixed size N. +/// +/// Once the set reaches size N, it becomes top. The items in the set are no longer tracked to +/// reclaim associated memory. +#[derive(Debug, Clone, Eq, Serialize, Deserialize)] + +pub struct BoundedSetLattice +where + T: Eq + Hash, +{ + // The set of items in the lattice with invariant: + // is_top => items.is_empty() ... i.e. the items are dropped when the lattice reaches top. + items: HashSet, + is_top: bool, +} + +impl LatticeFrom> for BoundedSetLattice +where + T: Eq + Hash, +{ + fn lattice_from(other: BoundedSetLattice) -> Self { + other + } +} + +impl Default for BoundedSetLattice +where + T: Eq + Hash, +{ + fn default() -> Self { + Self { + items: HashSet::new(), + is_top: N == 0, // This lattice is effectively a unit lattice `()`, if N == 0 + } + } +} + +impl From<()> for BoundedSetLattice +where + T: Eq + Hash, +{ + fn from(_: ()) -> Self { + Default::default() + } +} + +impl From> for () +where + T: Eq + Hash, +{ + fn from(_: BoundedSetLattice) -> Self {} +} + +impl BoundedSetLattice +where + T: Eq + Hash, +{ + pub fn new() -> Self { + Default::default() + } + + pub fn new_from(items: U) -> Self + where + U: IntoIterator, + { + let mut lattice = Self::new(); + lattice.merge(items); + lattice + } +} + +impl IsBot for BoundedSetLattice +where + T: Eq + Hash, +{ + fn is_bot(&self) -> bool { + match N { + 0 => true, + _ => self.items.is_empty() && !self.is_top, + } + } +} + +impl IsTop for BoundedSetLattice +where + T: Eq + Hash, +{ + fn is_top(&self) -> bool { + self.is_top + } +} + +impl Merge for BoundedSetLattice +where + U: IntoIterator, + T: Eq + Hash, +{ + fn merge(&mut self, other: U) -> bool { + if self.is_top { + return false; + } + + let old_len = self.items.len(); + self.items.extend(other); + let new_len = self.items.len(); + + if new_len >= N { + self.is_top = true; + self.items.clear(); + } + + new_len != old_len + } +} + +impl PartialOrd for BoundedSetLattice +where + T: Eq + Hash, +{ + fn partial_cmp(&self, other: &Self) -> Option { + match (self.is_top, other.is_top) { + (true, true) => Some(Ordering::Equal), + (true, false) => Some(Ordering::Greater), + (false, true) => Some(Ordering::Less), + (false, false) => match self.items.len().cmp(&other.items.len()) { + Ordering::Greater => { + if other.items.iter().all(|key| self.items.contains(key)) { + Some(Ordering::Greater) + } else { + None + } + } + Ordering::Less => { + if self.items.iter().all(|key| other.items.contains(key)) { + Some(Ordering::Less) + } else { + None + } + } + Ordering::Equal => { + if self.items.iter().all(|key| other.items.contains(key)) { + Some(Ordering::Equal) + } else { + None + } + } + }, + } + } +} + +impl PartialEq for BoundedSetLattice +where + T: Eq + Hash, +{ + fn eq(&self, other: &Self) -> bool { + match (self.is_top, other.is_top) { + (true, true) => true, + (true, false) => false, + (false, true) => false, + (false, false) => self.items == other.items, + } + } +} + +impl LatticeOrd for BoundedSetLattice where T: Eq + Hash {} + +impl Merge> for BoundedSetLattice +where + T: Eq + Hash, +{ + fn merge(&mut self, other: BoundedSetLattice) -> bool { + match (self.is_top, other.is_top) { + (true, _) => false, + (false, true) => { + self.is_top = true; + self.items.clear(); + true + } + (false, false) => self.merge(other.items), + } + } +} + +#[cfg(test)] +mod tests { + use hydroflow::lattices::test::check_all; + + use super::*; + + #[test] + fn test_0_bounded_set_lattice() { + let mut lat: BoundedSetLattice = ().into(); + assert!(lat.is_bot() && lat.is_top()); + + // Merges should always return false. + assert!(!lat.merge([1])); + + // No changes to top/bot status. + assert!(lat.is_bot() && lat.is_top()); + } + + #[test] + fn test_1_bounded_set_lattice() { + // The bounded lattice with N = 1 is effectively a WithBottom lattice. + let mut lat = BoundedSetLattice::::new(); + assert!(lat.is_bot() && !lat.is_top()); + assert!(lat.items.is_empty()); + + assert!(lat.merge([1])); + assert!(!lat.is_bot() && lat.is_top()); + assert!(lat.items.is_empty()); // Check that the items were dropped. + + assert!(!lat.merge([2])); + } + + #[test] + fn test_2_bounded_set_lattice() { + let mut a = BoundedSetLattice::::new(); + let b: BoundedSetLattice = BoundedSetLattice::new_from([1, 2]); + + assert!(a.is_bot() && !a.is_top()); + assert!(!b.is_bot() && b.is_top()); + + assert!(a.merge(b)); + assert!(!a.is_bot() && a.is_top()); + + assert!(!a.merge([3])); + } + + #[test] + fn test_lattice_properties() { + check_all(&[ + Default::default(), + BoundedSetLattice::::new_from([1]), + BoundedSetLattice::::new_from([1, 2]), + BoundedSetLattice::::new_from([1, 2, 3]), + BoundedSetLattice::::new_from([1, 2, 3, 4]), + ]); + } +} diff --git a/datastores/gossip_kv/kv/lib.rs b/datastores/gossip_kv/kv/lib.rs new file mode 100644 index 000000000000..15badc703be3 --- /dev/null +++ b/datastores/gossip_kv/kv/lib.rs @@ -0,0 +1,273 @@ +pub mod membership; +pub mod model; + +pub mod server; + +pub mod lattices; + +pub mod util; + +use std::collections::HashSet; +use std::fmt::Display; +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +use crate::model::{Clock, Namespaces}; +use crate::KeyParseError::InvalidNamespace; + +/// The namespace of the key of an entry in the key-value store. +#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize, Hash)] +pub enum Namespace { + /// User namespace is for use by the user of the key-value store. + User, + + /// System namespace is reserved for use by the key-value store itself. + System, +} + +/// Error that can occur when parsing a key from a string. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum KeyParseError { + /// The namespace in the key is invalid. Namespaces must be either `usr` or `sys`. + InvalidNamespace, + + /// The key is in an invalid format. Keys must be of the form `/namespace/table/row`. + InvalidFormat, +} + +impl Display for KeyParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InvalidNamespace => write!(f, "Invalid namespace"), + KeyParseError::InvalidFormat => write!(f, "Invalid key format"), + } + } +} + +impl FromStr for Namespace { + type Err = KeyParseError; + fn from_str(s: &str) -> Result { + match s { + "usr" => Ok(Namespace::User), + "sys" => Ok(Namespace::System), + _ => Err(InvalidNamespace), + } + } +} + +/// The name of a table in the key-value store. +pub type TableName = String; + +/// The key of a row in a table in the key-value store. +pub type RowKey = String; + +/// A key of an entry in the key-value store. +/// +/// Data in the key-value store is organized into namespaces, tables, and rows. Namespaces are +/// either `usr` for user data or `sys` for system data. Namespaces contain tables, which contain +/// rows. Each row has a row key and a row value. +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Hash)] +pub struct Key { + /// The namespace of the key. + pub namespace: Namespace, + /// The name of the table in the key. + pub table: TableName, + /// The key of the row in the table. + pub row_key: RowKey, +} + +impl FromStr for Key { + type Err = KeyParseError; + fn from_str(s: &str) -> Result { + let mut parts: Vec = vec![]; + let mut current_part = String::new(); + let mut escaping = false; + + for c in s.chars() { + match (escaping, c) { + (true, '\\') | (true, '/') => { + current_part.push(c); + escaping = false; + } + (true, _) => return Err(KeyParseError::InvalidFormat), + (false, '\\') => { + escaping = true; + } + (false, '/') => { + parts.push(current_part); + current_part = String::new(); + } + (false, _) => { + current_part.push(c); + } + } + } + + if escaping { + return Err(KeyParseError::InvalidFormat); + } + + if !current_part.is_empty() { + parts.push(current_part); + } + + if parts.len() != 4 { + return Err(KeyParseError::InvalidFormat); + } + + if !parts[0].is_empty() { + return Err(KeyParseError::InvalidFormat); + } + + if parts[2].is_empty() || parts[3].is_empty() { + return Err(KeyParseError::InvalidFormat); + } + + let namespace = parts[1].parse()?; + Ok(Key { + namespace, + table: parts[2].to_string(), + row_key: parts[3].to_string(), + }) + } +} + +/// A request from a client to the key-value store. +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] +pub enum ClientRequest { + /// A request to get the value of a key. + Get { key: Key }, + /// A request to set the value of a key. + Set { key: Key, value: String }, + /// A request to delete the value of a key. + Delete { key: Key }, +} + +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] +pub enum ClientResponse { + /// A response for a get request. The key is echoed back along with the value, if it exists. + /// Multiple values are returned if there were concurrent writes to the key. + Get { key: Key, value: HashSet }, + /// A response for a set request. The success field is true if the set was successful. + Set { success: bool }, + /// A response for a delete request. The success field is true if delete was successful. + Delete { success: bool }, +} + +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] +pub enum GossipMessage { + /// An "infecting message" to share updates with a peer. + Gossip { + message_id: String, + member_id: String, + writes: Namespaces, + }, + /// An acknowledgement message sent by a peer in response to a Gossip message, to indicate + /// that it hasn't seen some of the writes in the Gossip message before. + Ack { + message_id: String, + member_id: String, + }, + /// A negative acknowledgement sent by a peer in response to a Gossip message, to indicate + /// that it has seen all of the writes in the Gossip message before. + Nack { + message_id: String, + member_id: String, + }, +} + +#[cfg(test)] +mod tests { + use super::{Key, Namespace}; + + #[test] + fn test_key_parsing_sys_namespace() { + // Sys namespace + let first = "/sys/sys_table/sys_row".parse::().unwrap(); + assert_eq!(first.namespace, Namespace::System); + assert_eq!(first.table, "sys_table"); + assert_eq!(first.row_key, "sys_row"); + } + #[test] + fn test_key_parsing_user_namespace() { + // User namespace + let second = "/usr/usr_table/usr_row".parse::().unwrap(); + assert_eq!(second.namespace, Namespace::User); + assert_eq!(second.table, "usr_table"); + assert_eq!(second.row_key, "usr_row"); + } + + #[test] + fn test_key_empty_table() { + // Empty table + let empty_table = "/usr//usr_row".parse::(); + assert!(empty_table.is_err()); + assert_eq!( + empty_table.unwrap_err(), + super::KeyParseError::InvalidFormat + ); + } + + #[test] + fn test_key_empty_row() { + // Empty row + let empty_row = "/usr/usr_table/".parse::(); + assert!(empty_row.is_err()); + assert_eq!(empty_row.unwrap_err(), super::KeyParseError::InvalidFormat); + } + + #[test] + fn test_key_parsing_invalid_namespace() { + // Invalid namespace + let non_existent_namespace = "/ne_namespace/ne_table/ne_row".parse::(); + assert!(non_existent_namespace.is_err()); + assert_eq!( + non_existent_namespace.unwrap_err(), + super::KeyParseError::InvalidNamespace + ); + } + + #[test] + fn test_key_parsing_invalid_format() { + // Invalid format + let invalid_format = "/not_even_a_key".parse::(); + assert!(invalid_format.is_err()); + assert_eq!( + invalid_format.unwrap_err(), + super::KeyParseError::InvalidFormat + ); + + let invalid_format = "abcd/sys/sys_table/sys_row".parse::(); + assert!(invalid_format.is_err()); + assert_eq!( + invalid_format.unwrap_err(), + super::KeyParseError::InvalidFormat + ); + } + + #[test] + fn test_key_parsing_escaping() { + // Escape \ + let key = r"/usr/usr\/table/usr\/row".parse::().unwrap(); + assert_eq!(key.namespace, Namespace::User); + assert_eq!(key.table, r"usr/table"); + assert_eq!(key.row_key, r"usr/row"); + + // Escaping / + let key = r"/usr/usr\\table/usr\\row".parse::().unwrap(); + assert_eq!(key.namespace, Namespace::User); + assert_eq!(key.table, r"usr\table"); + assert_eq!(key.row_key, r"usr\row"); + + // Escaping any character + let key = r"/usr/usr\table/usr\row".parse::(); + assert!(key.is_err()); + assert_eq!(key.unwrap_err(), super::KeyParseError::InvalidFormat); + + // Dangling escape + let key = r"/usr/usr_table/usr_row\".parse::(); + assert!(key.is_err()); + assert_eq!(key.unwrap_err(), super::KeyParseError::InvalidFormat); + } +} diff --git a/datastores/gossip_kv/kv/membership.rs b/datastores/gossip_kv/kv/membership.rs new file mode 100644 index 000000000000..3657c87d3cd2 --- /dev/null +++ b/datastores/gossip_kv/kv/membership.rs @@ -0,0 +1,85 @@ +use std::fmt::Debug; +use std::hash::Hash; + +use serde::{Deserialize, Serialize}; + +pub type MemberId = String; + +/// Information about a member in the cluster. +/// +/// A member is a transducer that is part of the cluster. Leaving or failing is a terminal +/// state for a member. When a transducer restarts and rejoins the cluster, it is considered a +/// new member. +/// +/// # Generic Parameters +/// -- `A`: The transport of the endpoint on which the protocol is running. In production, this will +/// likely be a `SocketAddr`. +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] +pub struct MemberData +where + A: Debug + Clone + Eq + Hash + Serialize, +{ + /// The name of the member. Usually, this is a randomly generated identifier, based on the + /// hostname on which the member is running. + pub id: MemberId, + + /// The protocols that the member supports. + pub protocols: Vec>, +} + +/// A builder for `MemberData`. +pub struct MemberDataBuilder +where + A: Debug + Clone + Eq + Hash + Serialize, +{ + id: MemberId, + protocols: Vec>, +} + +impl MemberDataBuilder +where + A: Debug + Clone + Eq + Hash + Serialize, +{ + /// Creates a new `MemberDataBuilder`. + pub fn new(id: MemberId) -> Self { + MemberDataBuilder { + id, + protocols: Vec::new(), + } + } + + /// Adds a protocol to the member. + pub fn add_protocol(mut self, protocol: Protocol) -> Self { + self.protocols.push(protocol); + self + } + + /// Builds the `MemberData`. + pub fn build(self) -> MemberData { + MemberData { + id: self.id, + protocols: self.protocols, + } + } +} + +/// A protocol supported by a member. +/// +/// # Generic Parameters +/// -- `A`: The transport of the endpoint on which the protocol is running. In production, this will +/// likely be a `SocketAddr`. +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] +pub struct Protocol { + /// The name of the protocol. + pub name: String, + + /// The endpoint on which the protocol is running. + pub endpoint: A, +} + +impl Protocol { + /// Creates a new `Protocol`. + pub fn new(name: String, endpoint: A) -> Self { + Protocol { name, endpoint } + } +} diff --git a/datastores/gossip_kv/kv/model.rs b/datastores/gossip_kv/kv/model.rs new file mode 100644 index 000000000000..3d17d78d3b68 --- /dev/null +++ b/datastores/gossip_kv/kv/model.rs @@ -0,0 +1,166 @@ +use hydroflow::lattices::map_union::MapUnionHashMap; +use hydroflow::lattices::set_union::SetUnionHashSet; +use hydroflow::lattices::{DomPair, Max}; + +use crate::Namespace; + +/// Primary key for entries in a table. +pub type RowKey = String; + +/// Value stored in a table. Modelled as a timestamped set of strings. +/// +/// Each value is timestamped with the time at which it was last updated. Concurrent updates at +/// the same timestamp are stored as a set. +pub type RowValue = DomPair>; + +/// A map from row keys to values in a table. +pub type Table = MapUnionHashMap; + +/// Name of a table in the data store. +pub type TableName = String; + +/// A map from table names to tables. +pub type TableMap = MapUnionHashMap>; + +pub type NamespaceMap = MapUnionHashMap>; + +pub type Namespaces = NamespaceMap>; + +/// Timestamps used in the model. +// TODO: This will be updated to use a more sophisticated clock type with https://github.com/hydro-project/hydroflow/issues/1207. +pub type Clock = Max; + +/// TableMap element to upsert a row in an existing TableMap. +/// +/// Merge this into an existing TableMap to upsert a row in a table. If the table does not exist, +/// it gets created. There's no explicit "create table" operation. +/// +/// Parameters: +/// - `row_ts`: New timestamp of the row being upserted. +/// - `table_name`: Name of the table. +/// - `key`: Primary key of the row. +/// - `val`: Row value. +pub fn upsert_row( + row_ts: C, + ns: Namespace, + table_name: TableName, + key: RowKey, + val: String, +) -> Namespaces { + let value: RowValue = RowValue::new_from(row_ts, SetUnionHashSet::new_from([val])); + let row: Table> = Table::new_from([(key, value)]); + let table: TableMap> = TableMap::new_from([(table_name, row)]); + Namespaces::new_from([(ns, table)]) +} + +/// TableMap element to delete a row from an existing TableMap. +/// +/// Merge this into an existing TableMap to delete a row from a table. +/// +/// Parameters: +/// - `row_ts`: New timestamp of the row being deleted. +/// - `table_name`: Name of the table. +/// - `key`: Primary key of the row. +pub fn delete_row( + row_ts: C, + ns: Namespace, + table_name: TableName, + key: RowKey, +) -> Namespaces { + let value: RowValue = RowValue::new_from(row_ts, SetUnionHashSet::new_from([])); + let row: Table> = Table::new_from([(key, value)]); + let table = TableMap::new_from([(table_name, row)]); + Namespaces::new_from([(ns, table)]) +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use hydroflow::lattices::Merge; + + use crate::model::{delete_row, upsert_row, Clock, Namespaces, RowKey, TableName}; + use crate::Namespace::System; + + #[test] + fn test_table_map() { + let mut namespaces: Namespaces = Namespaces::default(); + + let first_tick: Clock = Clock::new(0); + let second_tick: Clock = Clock::new(1); + + let members_table = TableName::from("members"); + let key_1 = RowKey::from("key1"); + let value_1: String = "value1".to_string(); + + // Table starts out empty. + assert_eq!( + namespaces.as_reveal_ref().len(), + 0, + "Expected no namespaces." + ); + + let insert = upsert_row( + first_tick, + System, + members_table.clone(), + key_1.clone(), + value_1.clone(), + ); + Merge::merge(&mut namespaces, insert); + { + let table = namespaces + .as_reveal_ref() + .get(&System) + .unwrap() + .as_reveal_ref() + .get(&members_table) + .unwrap(); + + let row = table.as_reveal_ref().get(&key_1); + assert!(row.is_some(), "Row should exist"); + assert_eq!( + *row.unwrap().as_reveal_ref().0, + first_tick, + "Unexpected row timestamp" + ); + + let value = row.unwrap().as_reveal_ref().1.as_reveal_ref(); + assert_eq!( + value, + &HashSet::from([value_1.to_string()]), + "Unexpected row value" + ); + } + + let delete_row = delete_row( + second_tick, + System, + members_table.clone(), + key_1.to_string(), + ); + Merge::merge(&mut namespaces, delete_row); + { + let table = namespaces + .as_reveal_ref() + .get(&System) + .unwrap() + .as_reveal_ref() + .get(&members_table) + .unwrap(); + + // Deletion in this case leaves a "tombstone" + let row = table.as_reveal_ref().get(&key_1); + + assert!(row.is_some(), "Row should exist"); + assert_eq!( + *row.unwrap().as_reveal_ref().0, + second_tick, + "Unexpected row timestamp" + ); + + let value = row.unwrap().as_reveal_ref().1.as_reveal_ref(); + assert_eq!(value, &HashSet::from([]), "Row should be empty"); + } + } +} diff --git a/datastores/gossip_kv/kv/server.rs b/datastores/gossip_kv/kv/server.rs new file mode 100644 index 000000000000..a36d89013c40 --- /dev/null +++ b/datastores/gossip_kv/kv/server.rs @@ -0,0 +1,763 @@ +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; +use std::hash::Hash; + +use hydroflow::futures::{Sink, Stream}; +use hydroflow::hydroflow_syntax; +use hydroflow::itertools::Itertools; +use hydroflow::lattices::map_union::{KeyedBimorphism, MapUnionHashMap, MapUnionSingletonMap}; +use hydroflow::lattices::set_union::SetUnionHashSet; +use hydroflow::lattices::{Lattice, PairBimorphism}; +use hydroflow::scheduled::graph::Hydroflow; +use lattices::set_union::SetUnion; +use lattices::{IsTop, Max, Pair}; +use lazy_static::lazy_static; +use prometheus::{register_int_counter, IntCounter}; +use rand::seq::IteratorRandom; +use rand::thread_rng; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use tracing::{info, trace}; + +use crate::lattices::BoundedSetLattice; +use crate::membership::{MemberData, MemberId}; +use crate::model::{ + delete_row, upsert_row, Clock, NamespaceMap, Namespaces, RowKey, RowValue, TableMap, TableName, +}; +use crate::util::{ClientRequestWithAddress, GossipRequestWithAddress}; +use crate::GossipMessage::{Ack, Nack}; +use crate::{ClientRequest, ClientResponse, GossipMessage, Key, Namespace}; + +/// A trait that represents an abstract network address. In production, this will typically be +/// SocketAddr. +pub trait Address: Hash + Debug + Clone + Eq + Serialize {} +impl Address for A where A: Hash + Debug + Clone + Eq + Serialize {} + +#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)] +pub struct SeedNode +where + A: Address, +{ + pub id: MemberId, + pub address: A, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Lattice)] +pub struct InfectingWrite { + write: Namespaces, + members: BoundedSetLattice, +} + +pub type MessageId = String; + +lazy_static! { + pub static ref SETS_COUNTER: IntCounter = + register_int_counter!("sets", "Counts the number of SET requests processed.").unwrap(); +} + +/// Creates a L0 key-value store server using Hydroflow. +/// +/// # Arguments +/// -- `client_inputs`: The input stream of client requests for the client protocol. +/// -- `client_outputs`: The output sink of client responses for the client protocol. +/// -- `member_info`: The membership information of the server. +/// -- `seed_nodes`: A list of seed nodes that can be used to bootstrap the gossip cluster. +#[expect(clippy::too_many_arguments)] +pub fn server< + ClientInput, + ClientOutput, + ClientOutputError, + GossipInput, + GossipOutput, + GossipOutputError, + GossipTrigger, + SeedNodeStream, + Addr, +>( + client_inputs: ClientInput, + client_outputs: ClientOutput, + gossip_inputs: GossipInput, + gossip_outputs: GossipOutput, + gossip_trigger: GossipTrigger, + member_info: MemberData, + seed_nodes: Vec>, + seed_node_stream: SeedNodeStream, +) -> Hydroflow<'static> +where + ClientInput: Stream + Unpin + 'static, + ClientOutput: Sink<(ClientResponse, Addr), Error = ClientOutputError> + Unpin + 'static, + GossipInput: Stream + Unpin + 'static, + GossipOutput: Sink<(GossipMessage, Addr), Error = GossipOutputError> + Unpin + 'static, + GossipTrigger: Stream + Unpin + 'static, + SeedNodeStream: Stream>> + Unpin + 'static, + Addr: Address + DeserializeOwned + 'static, + ClientOutputError: Debug + 'static, + GossipOutputError: Debug + 'static, +{ + let my_member_id = member_info.id.clone(); + // TODO: This is ugly, but the only way this works at the moment. + let member_id_2 = my_member_id.clone(); + let member_id_3 = my_member_id.clone(); + let member_id_4 = my_member_id.clone(); + let member_id_5 = my_member_id.clone(); + let member_id_6 = my_member_id.clone(); + + hydroflow_syntax! { + + on_start = initialize() -> tee(); + on_start -> for_each(|_| info!("{:?}: Transducer {} started.", context.current_tick(), member_id_6)); + + seed_nodes = source_stream(seed_node_stream) + -> fold::<'static>(|| Box::new(seed_nodes), |last_seed_nodes, new_seed_nodes: Vec>| { + **last_seed_nodes = new_seed_nodes; + info!("Updated seed nodes: {:?}", **last_seed_nodes); + }); + + // Setup member metadata for this process. + on_start -> map(|_| upsert_row(Clock::new(0), Namespace::System, "members".to_string(), my_member_id.clone(), serde_json::to_string(&member_info).unwrap())) + -> writes; + + client_out = + inspect(|(resp, addr)| trace!("{:?}: Sending response: {:?} to {:?}.", context.current_tick(), resp, addr)) + -> dest_sink(client_outputs); + + client_in = source_stream(client_inputs) + -> map(|(msg, addr)| ClientRequestWithAddress::from_request_and_address(msg, addr)) + -> demux_enum::>(); + + client_in[Get] + -> inspect(|req| trace!("{:?}: Received Get request: {:?}.", context.current_tick(), req)) + -> map(|(key, addr) : (Key, Addr)| { + let row = MapUnionHashMap::new_from([ + ( + key.row_key, + SetUnionHashSet::new_from([addr /* to respond with the result later*/]) + ), + ]); + let table = MapUnionHashMap::new_from([(key.table, row)]); + MapUnionHashMap::new_from([(key.namespace, table)]) + }) + -> reads; + + client_in[Set] + -> inspect(|request| trace!("{:?}: Received Set request: {:?}.", context.current_tick(), request)) + -> map(|(key, value, _addr) : (Key, String, Addr)| upsert_row(Clock::new(context.current_tick().0), key.namespace, key.table, key.row_key, value)) + -> inspect(|_| { + SETS_COUNTER.inc(); // Bump SET metrics + }) + -> writes; + + client_in[Delete] + -> inspect(|req| trace!("{:?}: Received Delete request: {:?}.", context.current_tick(), req)) + -> map(|(key, _addr) : (Key, Addr)| delete_row(Clock::new(context.current_tick().0), key.namespace, key.table, key.row_key)) + -> writes; + + gossip_in = source_stream(gossip_inputs) + -> map(|(msg, addr)| GossipRequestWithAddress::from_request_and_address(msg, addr)) + -> demux_enum::>(); + + incoming_gossip_messages = gossip_in[Gossip] + -> inspect(|request| trace!("{:?}: Received gossip request: {:?}.", context.current_tick(), request)) + -> tee(); + + gossip_in[Ack] + -> inspect(|request| trace!("{:?}: Received gossip ack: {:?}.", context.current_tick(), request)) + -> null(); + + gossip_in[Nack] + -> inspect(|request| trace!("{:?}: Received gossip nack: {:?}.", context.current_tick(), request)) + -> map( |(message_id, member_id, _addr)| { + MapUnionSingletonMap::new_from((message_id, InfectingWrite { write: Default::default(), members: BoundedSetLattice::new_from([member_id]) })) + }) + -> infecting_writes; + + gossip_out = union() -> dest_sink(gossip_outputs); + + incoming_gossip_messages + -> map(|(_msg_id, _member_id, writes, _addr)| writes ) + -> writes; + + gossip_processing_pipeline = incoming_gossip_messages + -> map(|(msg_id, _member_id, writes, sender_address) : (String, MemberId, Namespaces>, Addr)| { + let namespaces = &#namespaces; + let all_data: &HashMap>> = namespaces.as_reveal_ref(); + let possible_new_data: &HashMap>>>= writes.as_reveal_ref(); + + // Check if any of the data is new + /* TODO: This logic is duplicated in MapUnion::Merge and ideally should be accessed + from the pass-through streaming output from `state`. See + https://www.notion.so/hydro-project/Proposal-for-State-API-10a2a586262f8080b981d1a2948a69ac + for more. */ + let gossip_has_new_data = possible_new_data.iter() + .flat_map(|(namespace, tables)| { + tables.as_reveal_ref().iter().flat_map(move |(table, rows)|{ + rows.as_reveal_ref().iter().map(move |(row_key, row_value)| (namespace, table, row_key, row_value.as_reveal_ref().0.as_reveal_ref())) + }) + }) + .any(|(ns,table, row_key, new_ts)| { + let existing_tables = all_data.get(ns); + let existing_rows = existing_tables.and_then(|tables| tables.as_reveal_ref().get(table)); + let existing_row = existing_rows.and_then(|rows| rows.as_reveal_ref().get(row_key)); + let existing_ts = existing_row.map(|row| row.as_reveal_ref().0.as_reveal_ref()); + + if let Some(existing_ts) = existing_ts { + trace!("Comparing timestamps: {:?} vs {:?}", new_ts, existing_ts); + new_ts > existing_ts + } else { + true + } + }); + + if gossip_has_new_data { + (Ack { message_id: msg_id, member_id: member_id_2.clone()}, sender_address, Some(writes)) + } else { + (Nack { message_id: msg_id, member_id: member_id_3.clone()}, sender_address, None) + } + }) + -> tee(); + + gossip_processing_pipeline + -> map(|(response, address, _writes)| (response, address)) + -> inspect( |(msg, addr)| trace!("{:?}: Sending gossip response: {:?} to {:?}.", context.current_tick(), msg, addr)) + -> gossip_out; + + gossip_processing_pipeline + -> filter(|(_, _, writes)| writes.is_some()) + -> map(|(_, _, writes)| writes.unwrap()) + -> writes; + + writes = union(); + + writes -> namespaces; + + namespaces = state::<'static, Namespaces::>(); + new_writes = namespaces -> tee(); // TODO: Use the output from here to generate NACKs / ACKs + + reads = state::<'tick, MapUnionHashMap>>>>(); + + new_writes -> [0]process_system_table_reads; + reads -> [1]process_system_table_reads; + + process_system_table_reads = lattice_bimorphism(KeyedBimorphism::, _>::new(KeyedBimorphism::, _>::new(KeyedBimorphism::, _>::new(PairBimorphism))), #namespaces, #reads) + -> lattice_reduce::<'tick>() // TODO: This can be removed if we fix https://github.com/hydro-project/hydroflow/issues/1401. Otherwise the result can be returned twice if get & gossip arrive in the same tick. + -> flat_map(|result: NamespaceMap, SetUnion>>>| { + + let mut response: Vec<(ClientResponse, Addr)> = vec![]; + + let result = result.as_reveal_ref(); + + for (namespace, tables) in result.iter() { + for (table_name, table) in tables.as_reveal_ref().iter() { + for (row_key, join_results) in table.as_reveal_ref().iter() { + let key = Key { + namespace: *namespace, + table: table_name.clone(), + row_key: row_key.clone(), + }; + + let timestamped_values = join_results.as_reveal_ref().0; + let all_values = timestamped_values.as_reveal_ref().1.as_reveal_ref(); + + let all_addresses = join_results.as_reveal_ref().1.as_reveal_ref(); + let socket_addr = all_addresses.iter().find_or_first(|_| true).unwrap(); + + response.push(( + ClientResponse::Get {key, value: all_values.clone()}, + socket_addr.clone(), + )); + } + } + } + response + }) -> client_out; + + new_writes -> for_each(|x| trace!("NEW WRITE: {:?}", x)); + + // Step 1: Put the new writes in a map, with the write as the key and a SetBoundedLattice as the value. + infecting_writes = union() -> state::<'static, MapUnionHashMap>(); + + new_writes -> map(|write| { + // Ideally, the write itself is the key, but writes are a hashmap and hashmaps don't + // have a hash implementation. So we just generate a GUID identifier for the write + // for now. + let id = uuid::Uuid::new_v4().to_string(); + MapUnionSingletonMap::new_from((id, InfectingWrite { write, members: BoundedSetLattice::new() })) + }) -> infecting_writes; + + gossip_trigger = source_stream(gossip_trigger); + + gossip_messages = gossip_trigger + -> flat_map( |_| + { + let infecting_writes = #infecting_writes.as_reveal_ref().clone(); + trace!("{:?}: Currently gossipping {} infecting writes.", context.current_tick(), infecting_writes.iter().filter(|(_, write)| !write.members.is_top()).count()); + infecting_writes + } + ) + -> filter(|(_id, infecting_write)| !infecting_write.members.is_top()) + -> map(|(id, infecting_write)| { + trace!("{:?}: Choosing a peer to gossip to. {:?}:{:?}", context.current_tick(), id, infecting_write); + let peers = #namespaces.as_reveal_ref().get(&Namespace::System).unwrap().as_reveal_ref().get("members").unwrap().as_reveal_ref().clone(); + + let mut peer_names = HashSet::new(); + peers.iter().for_each(|(row_key, _)| { + peer_names.insert(row_key.clone()); + }); + + let seed_nodes = &#seed_nodes; + seed_nodes.iter().for_each(|seed_node| { + peer_names.insert(seed_node.id.clone()); + }); + + // Exclude self from the list of peers. + peer_names.remove(&member_id_5); + + trace!("{:?}: Peers: {:?}", context.current_tick(), peer_names); + + let chosen_peer_name = peer_names.iter().choose(&mut thread_rng()); + + if chosen_peer_name.is_none() { + trace!("{:?}: No peers to gossip to.", context.current_tick()); + return None; + } + + let chosen_peer_name = chosen_peer_name.unwrap(); + let gossip_address = if peers.contains_key(chosen_peer_name) { + let peer_info_value = peers.get(chosen_peer_name).unwrap().as_reveal_ref().1.as_reveal_ref().iter().next().unwrap().clone(); + let peer_info_deserialized = serde_json::from_str::>(&peer_info_value).unwrap(); + peer_info_deserialized.protocols.iter().find(|protocol| protocol.name == "gossip").unwrap().clone().endpoint + } else { + seed_nodes.iter().find(|seed_node| seed_node.id == *chosen_peer_name).unwrap().address.clone() + }; + + trace!("Chosen peer: {:?}:{:?}", chosen_peer_name, gossip_address); + Some((id, infecting_write, gossip_address)) + }) + -> flatten() + -> inspect(|(message_id, infecting_write, peer_gossip_address)| trace!("{:?}: Sending write:\nMessageId:{:?}\nWrite:{:?}\nPeer Address:{:?}", context.current_tick(), message_id, infecting_write, peer_gossip_address)) + -> map(|(message_id, infecting_write, peer_gossip_address): (String, InfectingWrite, Addr)| { + let gossip_request = GossipMessage::Gossip { + message_id: message_id.clone(), + member_id: member_id_4.clone(), + writes: infecting_write.write.clone(), + }; + (gossip_request, peer_gossip_address) + }) + -> gossip_out; + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use hydroflow::tokio_stream::empty; + use hydroflow::util::simulation::{Address, Fleet, Hostname}; + + use super::*; + use crate::membership::{MemberDataBuilder, Protocol}; + + #[hydroflow::test] + async fn test_member_init() { + let mut fleet = Fleet::new(); + + let server_name: Hostname = "server".to_string(); + + let server_client_address = Address::new(server_name.clone(), "client".to_string()); + let server_gossip_address = Address::new(server_name.clone(), "gossip".to_string()); + + let (_, gossip_trigger_rx) = hydroflow::util::unbounded_channel::<()>(); + + // Create the kv server + fleet.add_host(server_name.clone(), |ctx| { + let client_input = ctx.new_inbox::("client".to_string()); + let client_output = ctx.new_outbox::("client".to_string()); + + let gossip_input = ctx.new_inbox::("gossip".to_string()); + let gossip_output = ctx.new_outbox::("gossip".to_string()); + + let member_data = MemberDataBuilder::new(server_name.clone()) + .add_protocol(Protocol::new( + "client".into(), + server_client_address.clone(), + )) + .add_protocol(Protocol::new( + "gossip".into(), + server_gossip_address.clone(), + )) + .build(); + + server( + client_input, + client_output, + gossip_input, + gossip_output, + gossip_trigger_rx, + member_data, + vec![], + empty(), + ) + }); + + let client_name: Hostname = "client".to_string(); + + let key = "/sys/members/server".parse::().unwrap(); + + let (trigger_tx, trigger_rx) = hydroflow::util::unbounded_channel::<()>(); + let (response_tx, mut response_rx) = hydroflow::util::unbounded_channel::(); + + let key_clone = key.clone(); + let server_client_address_clone = server_client_address.clone(); + + fleet.add_host(client_name.clone(), |ctx| { + let client_tx = ctx.new_outbox::("client".to_string()); + let client_rx = ctx.new_inbox::("client".to_string()); + + hydroflow_syntax! { + + client_output = dest_sink(client_tx); + + source_stream(trigger_rx) + -> map(|_| (ClientRequest::Get { key: key_clone.clone() }, server_client_address_clone.clone()) ) + -> client_output; + + client_input = source_stream(client_rx) + -> for_each(|(resp, _addr)| response_tx.send(resp).unwrap()); + + } + }); + + // Send a trigger to the client to send a get request. + trigger_tx.send(()).unwrap(); + + let expected_member_data = MemberDataBuilder::new(server_name.clone()) + .add_protocol(Protocol::new( + "client".to_string(), + server_client_address.clone(), + )) + .add_protocol(Protocol::new( + "gossip".to_string(), + server_gossip_address.clone(), + )) + .build(); + + loop { + fleet.run_single_tick_all_hosts().await; + + let responses = + hydroflow::util::collect_ready_async::, _>(&mut response_rx).await; + + if !responses.is_empty() { + assert_eq!( + responses, + &[(ClientResponse::Get { + key: key.clone(), + value: HashSet::from([ + serde_json::to_string(&expected_member_data).unwrap() + ]) + })] + ); + break; + } + } + } + + #[hydroflow::test] + async fn test_multiple_values_same_tick() { + let mut fleet = Fleet::new(); + + let server_name: Hostname = "server".to_string(); + + let server_client_address = Address::new(server_name.clone(), "client".to_string()); + + let (_, gossip_trigger_rx) = hydroflow::util::unbounded_channel::<()>(); + + // Create the kv server + fleet.add_host(server_name.clone(), |ctx| { + let client_input = ctx.new_inbox::("client".to_string()); + let client_output = ctx.new_outbox::("client".to_string()); + + let gossip_input = ctx.new_inbox::("gossip".to_string()); + let gossip_output = ctx.new_outbox::("gossip".to_string()); + let server_gossip_address = Address::new(server_name.clone(), "gossip".to_string()); + + let member_data = MemberDataBuilder::new(server_name.clone()) + .add_protocol(Protocol::new( + "client".into(), + server_client_address.clone(), + )) + .add_protocol(Protocol::new( + "gossip".into(), + server_gossip_address.clone(), + )) + .build(); + + server( + client_input, + client_output, + gossip_input, + gossip_output, + gossip_trigger_rx, + member_data, + vec![], + empty(), + ) + }); + + let key = Key { + namespace: Namespace::System, + table: "table".to_string(), + row_key: "row".to_string(), + }; + let val_a = "A".to_string(); + let val_b = "B".to_string(); + + let writer_name: Hostname = "writer".to_string(); + + let (writer_trigger_tx, writer_trigger_rx) = hydroflow::util::unbounded_channel::(); + let key_clone = key.clone(); + let server_client_address_clone = server_client_address.clone(); + + fleet.add_host(writer_name.clone(), |ctx| { + let client_tx = ctx.new_outbox::("client".to_string()); + hydroflow_syntax! { + client_output = dest_sink(client_tx); + + source_stream(writer_trigger_rx) + -> map(|value| (ClientRequest::Set { key: key_clone.clone(), value: value.clone()}, server_client_address_clone.clone()) ) + -> client_output; + } + }); + + // Send two messages from the writer. + let writer = fleet.get_host_mut(&writer_name).unwrap(); + writer_trigger_tx.send(val_a.clone()).unwrap(); + writer.run_tick(); + + writer_trigger_tx.send(val_b.clone()).unwrap(); + writer.run_tick(); + + // Transmit messages across the network. + fleet.process_network().await; + + // Run the server. + let server = fleet.get_host_mut(&server_name).unwrap(); + server.run_tick(); + + // Read the value back. + let reader_name: Hostname = "reader".to_string(); + + let (reader_trigger_tx, reader_trigger_rx) = hydroflow::util::unbounded_channel::<()>(); + let (response_tx, mut response_rx) = hydroflow::util::unbounded_channel::(); + + let key_clone = key.clone(); + let server_client_address_clone = server_client_address.clone(); + + fleet.add_host(reader_name.clone(), |ctx| { + let client_tx = ctx.new_outbox::("client".to_string()); + let client_rx = ctx.new_inbox::("client".to_string()); + + hydroflow_syntax! { + client_output = dest_sink(client_tx); + + source_stream(reader_trigger_rx) + -> map(|_| (ClientRequest::Get { key: key_clone.clone() }, server_client_address_clone.clone()) ) + -> client_output; + + client_input = source_stream(client_rx) + -> for_each(|(resp, _addr)| response_tx.send(resp).unwrap()); + + } + }); + + reader_trigger_tx.send(()).unwrap(); + + loop { + fleet.run_single_tick_all_hosts().await; + + let responses = + hydroflow::util::collect_ready_async::, _>(&mut response_rx).await; + + if !responses.is_empty() { + assert_eq!( + responses, + &[ClientResponse::Get { + key, + value: HashSet::from([val_a, val_b]) + }] + ); + break; + } + } + } + + #[hydroflow::test] + async fn test_gossip() { + let subscriber = tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_test_writer() + .finish(); + + let _ = tracing::subscriber::set_global_default(subscriber); + + let mut fleet = Fleet::new(); + + let server_a: Hostname = "server_a".to_string(); + let server_b: Hostname = "server_b".to_string(); + + let server_a_client_address = Address::new(server_a.clone(), "client".to_string()); + let server_b_client_address = Address::new(server_b.clone(), "client".to_string()); + + let server_a_gossip_address = Address::new(server_a.clone(), "gossip".to_string()); + let server_b_gossip_address = Address::new(server_b.clone(), "gossip".to_string()); + + let seed_nodes = vec![ + SeedNode { + id: server_a.clone(), + address: server_a_gossip_address.clone(), + }, + SeedNode { + id: server_b.clone(), + address: server_b_gossip_address.clone(), + }, + ]; + + let (gossip_trigger_tx_a, gossip_trigger_rx_a) = hydroflow::util::unbounded_channel::<()>(); + + let seed_nodes_clone = seed_nodes.clone(); + fleet.add_host(server_a.clone(), |ctx| { + let client_input = ctx.new_inbox::("client".to_string()); + let client_output = ctx.new_outbox::("client".to_string()); + + let gossip_input = ctx.new_inbox::("gossip".to_string()); + let gossip_output = ctx.new_outbox::("gossip".to_string()); + + let member_data = MemberDataBuilder::new(server_a.clone()) + .add_protocol(Protocol::new( + "client".into(), + server_a_client_address.clone(), + )) + .add_protocol(Protocol::new( + "gossip".into(), + server_a_gossip_address.clone(), + )) + .build(); + + server( + client_input, + client_output, + gossip_input, + gossip_output, + gossip_trigger_rx_a, + member_data, + seed_nodes_clone, + empty(), + ) + }); + + let (_, gossip_trigger_rx_b) = hydroflow::util::unbounded_channel::<()>(); + + let seed_nodes_clone = seed_nodes.clone(); + fleet.add_host(server_b.clone(), |ctx| { + let client_input = ctx.new_inbox::("client".to_string()); + let client_output = ctx.new_outbox::("client".to_string()); + + let gossip_input = ctx.new_inbox::("gossip".to_string()); + let gossip_output = ctx.new_outbox::("gossip".to_string()); + + let member_data = MemberDataBuilder::new(server_b.clone()) + .add_protocol(Protocol::new( + "client".into(), + server_b_client_address.clone(), + )) + .add_protocol(Protocol::new( + "gossip".into(), + server_b_gossip_address.clone(), + )) + .build(); + + server( + client_input, + client_output, + gossip_input, + gossip_output, + gossip_trigger_rx_b, + member_data, + seed_nodes_clone, + empty(), + ) + }); + + let key = Key { + namespace: Namespace::User, + table: "table".to_string(), + row_key: "row".to_string(), + }; + + let writer_name: Hostname = "writer".to_string(); + + let (writer_trigger_tx, writer_trigger_rx) = hydroflow::util::unbounded_channel::(); + + let key_clone = key.clone(); + let server_a_client_address_clone = server_a_client_address.clone(); + + fleet.add_host(writer_name.clone(), |ctx| { + let client_tx = ctx.new_outbox::("client".to_string()); + hydroflow_syntax! { + client_output = dest_sink(client_tx); + + source_stream(writer_trigger_rx) + -> map(|value| (ClientRequest::Set { key: key_clone.clone(), value: value.clone()}, server_a_client_address_clone.clone()) ) + -> client_output; + } + }); + + let reader_name: Hostname = "reader".to_string(); + + let (reader_trigger_tx, reader_trigger_rx) = hydroflow::util::unbounded_channel::<()>(); + let (response_tx, mut response_rx) = hydroflow::util::unbounded_channel::(); + + let key_clone = key.clone(); + let server_b_client_address_clone = server_b_client_address.clone(); + + fleet.add_host(reader_name.clone(), |ctx| { + let client_tx = ctx.new_outbox::("client".to_string()); + let client_rx = ctx.new_inbox::("client".to_string()); + + hydroflow_syntax! { + client_output = dest_sink(client_tx); + + source_stream(reader_trigger_rx) + -> map(|_| (ClientRequest::Get { key: key_clone.clone() }, server_b_client_address_clone.clone()) ) + -> client_output; + + client_input = source_stream(client_rx) + -> for_each(|(resp, _addr)| response_tx.send(resp).unwrap()); + + } + }); + + let value = "VALUE".to_string(); + writer_trigger_tx.send(value.clone()).unwrap(); + + loop { + reader_trigger_tx.send(()).unwrap(); + fleet.run_single_tick_all_hosts().await; + let responses = + hydroflow::util::collect_ready_async::, _>(&mut response_rx).await; + + if !responses.is_empty() { + assert_eq!( + responses, + &[ClientResponse::Get { + key, + value: HashSet::from([value.clone()]) + }] + ); + break; + } + + gossip_trigger_tx_a.send(()).unwrap(); + } + } +} diff --git a/datastores/gossip_kv/kv/util.rs b/datastores/gossip_kv/kv/util.rs new file mode 100644 index 000000000000..4cc391da6e7a --- /dev/null +++ b/datastores/gossip_kv/kv/util.rs @@ -0,0 +1,87 @@ +use hydroflow::DemuxEnum; + +use crate::model::{Clock, Namespaces}; +use crate::{ClientRequest, GossipMessage, Key}; + +/// Convenience enum to represent a client request with the address of the client. Makes it +/// possible to use `demux_enum` in the surface syntax. +#[derive(Debug, DemuxEnum)] +pub enum ClientRequestWithAddress { + /// A get request with the key and the address of the client. + Get { key: Key, addr: A }, + /// A set request with the key, value and the address of the client. + Set { key: Key, value: String, addr: A }, + /// A delete request with the key and the address of the client. + Delete { key: Key, addr: A }, +} + +impl ClientRequestWithAddress { + /// Create a `ClientRequestWithAddress` from a `ClientRequest` and an address. + pub fn from_request_and_address(request: ClientRequest, addr: A) -> Self { + match request { + ClientRequest::Get { key } => Self::Get { key, addr }, + ClientRequest::Set { key, value } => Self::Set { key, value, addr }, + ClientRequest::Delete { key } => Self::Delete { key, addr }, + } + } +} + +/// Convenience enum to represent a gossip request with the address of the client. Makes it +/// possible to use `demux_enum` in the surface syntax. +#[derive(Debug, DemuxEnum)] +pub enum GossipRequestWithAddress { + /// A gossip request with the message id, writes and the address of the client. + Gossip { + message_id: String, + member_id: String, + writes: Namespaces, + addr: A, + }, + /// An ack request with the message id and the address of the client. + Ack { + message_id: String, + member_id: String, + addr: A, + }, + /// A nack request with the message id and the address of the client. + Nack { + message_id: String, + member_id: String, + addr: A, + }, +} + +impl GossipRequestWithAddress { + /// Create a `GossipRequestWithAddress` from a `GossipMessage` and an address. + pub fn from_request_and_address(request: GossipMessage, addr: A) -> Self { + match request { + GossipMessage::Gossip { + message_id, + member_id, + writes, + } => Self::Gossip { + message_id, + member_id, + writes, + addr, + }, + + GossipMessage::Ack { + message_id, + member_id, + } => Self::Ack { + message_id, + addr, + member_id, + }, + GossipMessage::Nack { + message_id, + member_id, + } => Self::Nack { + message_id, + addr, + member_id, + }, + } + } +} diff --git a/datastores/gossip_kv/load_test_server/Dockerfile b/datastores/gossip_kv/load_test_server/Dockerfile new file mode 100644 index 000000000000..91f27fdd22d5 --- /dev/null +++ b/datastores/gossip_kv/load_test_server/Dockerfile @@ -0,0 +1,11 @@ +FROM "hydroflow-gossip-kv-base-image:latest" AS builder +WORKDIR /usr/src/gossip-kv-server +COPY . . +RUN find . +RUN cargo build --release --workspace -p gossip_kv + +FROM rustlang/rust:nightly-slim +COPY --from=builder /usr/src/gossip-kv-server/target/release/load_test_server /usr/local/bin/load_test_server + +# Don't skip the trailing slash in the destination directory +CMD ["load_test_server"] diff --git a/datastores/gossip_kv/load_test_server/server.rs b/datastores/gossip_kv/load_test_server/server.rs new file mode 100644 index 000000000000..88bd40e3fb32 --- /dev/null +++ b/datastores/gossip_kv/load_test_server/server.rs @@ -0,0 +1,226 @@ +use std::convert::Infallible; +use std::num::{NonZeroU32, ParseFloatError}; +use std::thread::sleep; +use std::time::Duration; + +use clap::Parser; +use gossip_kv::membership::{MemberDataBuilder, Protocol}; +use gossip_kv::{ClientRequest, GossipMessage}; +use governor::{Quota, RateLimiter}; +use hydroflow::util::{unbounded_channel, unsync_channel}; +use prometheus::{gather, Encoder, TextEncoder}; +use tokio::sync::mpsc::UnboundedSender; +use tokio::task; +use tracing::{error, info, trace}; +use warp::Filter; + +type LoadTestAddress = u64; + +use gossip_kv::server::{server, SeedNode}; +use hydroflow::futures::sink::drain; +use hydroflow::futures::stream; +use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; +use hydroflow::tokio_stream::StreamExt; +use lattices::cc_traits::Iter; + +const UNKNOWN_ADDRESS: LoadTestAddress = 9999999999; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Parser)] +struct Opts { + /// Number of threads to run. Each thread will run an instance of the gossip-kv server transducer. + #[clap(short, long, default_value = "5")] + thread_count: usize, + + /// Frequency (in seconds) at which to send gossip messages. + #[clap(short, long, default_value = "10", value_parser = clap_duration_from_secs)] + gossip_frequency: Duration, + + /// Maximum number of SET requests to send per second. + #[clap(short, long, default_value = "1")] + max_set_throughput: u32, +} + +/// Parse duration from float string for clap args. +fn clap_duration_from_secs(arg: &str) -> Result { + arg.parse().map(Duration::from_secs_f32) +} + +fn run_server( + server_name: String, + gossip_address: LoadTestAddress, + gossip_input_rx: UnboundedReceiverStream<(GossipMessage, LoadTestAddress)>, + switchboard: Switchboard, + seed_nodes: Vec>, + opts: Opts, +) { + std::thread::spawn(move || { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + let (gossip_output_tx, mut gossip_output_rx) = unsync_channel(None); + + let (gossip_trigger_tx, gossip_trigger_rx) = unbounded_channel(); + + let member_data = MemberDataBuilder::new(server_name.clone()) + .add_protocol(Protocol::new("gossip".into(), gossip_address)) + .build(); + + rt.block_on(async { + let local = task::LocalSet::new(); + + let (client_input_tx, client_input_rx) = unbounded_channel(); + + let put_throughput = opts.max_set_throughput; + local.spawn_local(async move { + let rate_limiter = RateLimiter::direct(Quota::per_second( + NonZeroU32::new(put_throughput).unwrap(), + )); + loop { + rate_limiter.until_ready().await; + let key = "/usr/table/key".parse().unwrap(); + let request = ClientRequest::Set { + key, + value: "FOOBAR".to_string(), + }; + client_input_tx.send((request, UNKNOWN_ADDRESS)).unwrap(); + } + }); + + let gossip_frequency = opts.gossip_frequency; + local.spawn_local(async move { + loop { + tokio::time::sleep(gossip_frequency).await; + gossip_trigger_tx.send(()).unwrap(); + } + }); + + // Networking + local.spawn_local(async move { + while let Some((msg, addr)) = gossip_output_rx.next().await { + trace!("Sending gossip message: {:?} to {}", msg, addr); + let outbox = switchboard.gossip_outboxes.get(addr as usize).unwrap(); + if let Err(e) = outbox.send((msg, gossip_address)) { + error!("Failed to send gossip message: {:?}", e); + } + } + }); + + local.spawn_local(async { + let mut server = server( + client_input_rx, + drain(), // Ignoring client responses for now. + gossip_input_rx, + gossip_output_tx, + gossip_trigger_rx, + member_data, + seed_nodes, + stream::empty(), + ); + + server.run_async().await + }); + + local.await + }); + }); +} + +struct Switchboard { + gossip_outboxes: Vec>, +} + +impl Clone for Switchboard { + fn clone(&self) -> Self { + Self { + gossip_outboxes: self.gossip_outboxes.clone(), + } + } +} + +impl Switchboard { + fn new() -> Self { + Self { + gossip_outboxes: Vec::new(), + } + } + fn new_outbox( + &mut self, + ) -> ( + LoadTestAddress, + UnboundedReceiverStream<(GossipMessage, LoadTestAddress)>, + ) { + let addr: LoadTestAddress = self.gossip_outboxes.len() as LoadTestAddress; + let (tx, rx) = unbounded_channel(); + self.gossip_outboxes.push(tx); + (addr, rx) + } +} + +async fn metrics_handler() -> Result { + let encoder = TextEncoder::new(); + let metric_families = gather(); + let mut buffer = Vec::new(); + encoder.encode(&metric_families, &mut buffer).unwrap(); + + Ok(warp::reply::with_header( + buffer, + "Content-Type", + encoder.format_type(), + )) +} + +fn main() { + tracing_subscriber::fmt::init(); + + let opts: Opts = Opts::parse(); + + std::thread::spawn(move || { + let metrics_route = warp::path("metrics").and_then(metrics_handler); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + rt.block_on(async move { + info!("Starting metrics server on port 4003"); + warp::serve(metrics_route).run(([0, 0, 0, 0], 4003)).await; + }); + }); + + info!("Starting load test with with {} threads", opts.thread_count); + + let mut switchboard = Switchboard::new(); + + let outboxes: Vec<_> = (0..opts.thread_count) + .map(|_| { + let (addr, rx) = switchboard.new_outbox(); + (format!("SERVER-{}", addr), addr, rx) + }) + .collect(); + + let seed_nodes: Vec<_> = outboxes + .iter() + .map(|(name, addr, _)| SeedNode { + id: name.clone(), + address: *addr, + }) + .collect(); + + outboxes.into_iter().for_each(|(name, addr, outbox)| { + run_server( + name, + addr, + outbox, + switchboard.clone(), + seed_nodes.clone(), + opts, + ); + }); + + loop { + sleep(Duration::from_secs(1)); + } +} diff --git a/datastores/gossip_kv/server/.gitignore b/datastores/gossip_kv/server/.gitignore new file mode 100644 index 000000000000..9d1173a06139 --- /dev/null +++ b/datastores/gossip_kv/server/.gitignore @@ -0,0 +1 @@ +config/local.toml \ No newline at end of file diff --git a/datastores/gossip_kv/server/Dockerfile b/datastores/gossip_kv/server/Dockerfile new file mode 100644 index 000000000000..4032b4056fbe --- /dev/null +++ b/datastores/gossip_kv/server/Dockerfile @@ -0,0 +1,13 @@ +FROM "hydroflow-gossip-kv-base-image:latest" AS builder +WORKDIR /usr/src/gossip-kv-server +COPY . . +RUN find . +RUN cargo build --release --workspace -p gossip_kv + +FROM rustlang/rust:nightly-slim +COPY --from=builder /usr/src/gossip-kv-server/target/release/gossip_server /usr/local/bin/gossip_server + +RUN mkdir -p /config/static +# Don't skip the trailing slash in the destination directory +COPY datastores/gossip_kv/server/config/static/*.toml /config/static/ +CMD ["gossip_server"] diff --git a/datastores/gossip_kv/server/README.md b/datastores/gossip_kv/server/README.md new file mode 100644 index 000000000000..434315e29f4f --- /dev/null +++ b/datastores/gossip_kv/server/README.md @@ -0,0 +1,44 @@ +From the `hydroflow` directory, run + +## Minikube + +### Start Minikube +Disk allocation is done by the driver used to create the VM. Setting this to a high value will do nothing if the +driver isn't correctly configured. You'll only notice that hydroflow runs out of disk space while compiling. +For Docker, the disk size is set in the Docker Desktop settings. Also, provide as many CPUs here as possible, since +building the code is CPU-intensive. +```shell +minikube start --disk-size=100g --cpus=16 --memory=32768 +``` + +### Use the Docker daemon from minikube +```shell +eval $(minikube docker-env) +``` + +## Build Docker Base Image +Speeds up code changes by caching build dependencies. +```shell +docker build -t "hydroflow/gossip-kv-server-base-image:latest" -f datastores/gossip_kv/server/baseimage.Dockerfile . +``` + +## Build Docker Image for Gossip Server +```shell +docker build -t "hydroflow/gossip-kv-server:latest" -f datastores/gossip_kv/server/Dockerfile . +``` + +## Build Docker Image for Gossip CLI +```shell +docker build -t "hydroflow/gossip-kv-cli:latest" -f datastores/gossip_kv/cli/Dockerfile . +``` + +## Check if minikube has the image +You should see "hydroflow/gossip-kv" +```shell +minikube image ls --format tablemin +``` + +## Deploy to Minikube +```shell +kubectl apply -f datastores/gossip_kv/server/deployment/local/objects.yaml +``` \ No newline at end of file diff --git a/datastores/gossip_kv/server/baseimage.Dockerfile b/datastores/gossip_kv/server/baseimage.Dockerfile new file mode 100644 index 000000000000..911de64a7f75 --- /dev/null +++ b/datastores/gossip_kv/server/baseimage.Dockerfile @@ -0,0 +1,23 @@ +FROM rustlang/rust:nightly AS builder +WORKDIR /usr/src/gossip-kv-server-base-image +COPY . . + +RUN apt-get update && apt-get install -y \ + python3 \ + python3.11-dev \ + libpython3.11 \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +## Build everything, including dependencies. The built dependencies will be cached, so only changing the server +## code requires lesser build time. +RUN cargo build --release --workspace -p gossip_kv + +## Copy the built file to where the actual app will be built subsequently. +RUN mkdir -p /usr/src/gossip-kv-server/target/release +RUN mv /usr/src/gossip-kv-server-base-image/target/release/build/ /usr/src/gossip-kv-server/target/release/build/ +RUN mv /usr/src/gossip-kv-server-base-image/target/release/deps/ /usr/src/gossip-kv-server/target/release/deps/ +RUN mv /usr/src/gossip-kv-server-base-image/target/release/.fingerprint/ /usr/src/gossip-kv-server/target/release/.fingerprint/ + +## Delete all the source code +RUN rm -rf /usr/src/gossip-kv-server-base-image \ No newline at end of file diff --git a/datastores/gossip_kv/server/config/mod.rs b/datastores/gossip_kv/server/config/mod.rs new file mode 100644 index 000000000000..13ff54873ad2 --- /dev/null +++ b/datastores/gossip_kv/server/config/mod.rs @@ -0,0 +1,130 @@ +use std::path::PathBuf; + +use config::{Config, ConfigError, File}; +use hydroflow::futures::future::ready; +use hydroflow::futures::{Stream, StreamExt}; +use notify::{Event, EventHandler, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; +use serde::{Deserialize, Serialize}; +use tokio::sync::mpsc::UnboundedSender; +use tracing::trace; + +/// L0 Data Store settings. +#[derive(Debug, Deserialize, Serialize)] +pub struct ServerSettings { + /// An initial set of "seed nodes" that can be used to bootstrap the gossip cluster. These + /// won't be the only nodes in the cluster, but they can be used to discover other nodes. + pub seed_nodes: Vec, +} + +const CONFIG_ROOT: &str = "config"; +const STATIC_CONFIG_PATH: &str = "static"; +const DYNAMIC_CONFIG_PATH: &str = "dynamic"; + +fn static_config_path(subpath: &str) -> PathBuf { + PathBuf::from(CONFIG_ROOT) + .join(STATIC_CONFIG_PATH) + .join(subpath) +} + +fn dynamic_config_path(subpath: &str) -> PathBuf { + PathBuf::from(CONFIG_ROOT) + .join(DYNAMIC_CONFIG_PATH) + .join(subpath) +} + +impl ServerSettings { + /// Load the settings from the configuration files. + pub fn new() -> Result { + let run_mode = std::env::var("RUN_MODE").unwrap_or_else(|_| "development".into()); + + let settings = Config::builder() + /* Load the default settings from the `config/default.toml` file. */ + .add_source(File::from(static_config_path("default.toml")).required(false)) + + /* Load additional overrides based on context (alpha, beta, production, etc.), if they exist. */ + .add_source(File::from(static_config_path(&run_mode)).required(false)) + + /* Load the local settings, if they exist. These are .gitignored to prevent accidental + check-in. */ + .add_source(File::from(static_config_path("local")).required(false)) + + /* Load the dynamic settings, if they exist. These always override any static configuration*/ + .add_source(File::from(dynamic_config_path("dynamic.toml")).required(false)) + .build()?; + + settings.try_deserialize() + } +} + +/// A seed node that can be used to bootstrap the gossip cluster. +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash)] +pub struct SeedNodeSettings { + /// The ID of the seed node. + pub id: String, + + /// The address on which the seed node is listening for gossip messages. + pub address: String, +} + +/// Setup a watcher for the settings files and return a stream of settings changes. +/// +/// Returns the watcher, the initial settings, and a stream of settings changes. The watcher is +/// returned so that it can be kept alive for the lifetime of the application. Also returns a +/// snapshot of the current settings. +pub fn setup_settings_watch() -> ( + RecommendedWatcher, + ServerSettings, + impl Stream, +) { + let (tx, rx) = hydroflow::util::unbounded_channel(); + + // Setup the watcher + let mut watcher = notify::RecommendedWatcher::new( + UnboundedSenderEventHandler::new(tx), + notify::Config::default(), + ) + .unwrap(); + watcher + .watch(&PathBuf::from(CONFIG_ROOT), RecursiveMode::Recursive) + .unwrap(); + + // Read initial settings + let initial_settings = ServerSettings::new().unwrap(); + + let change_stream = rx + .map(Result::unwrap) + .map(|event| { + trace!("Event: {:?}", event); + match event.kind { + EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) => { + Some(ServerSettings::new().unwrap()) + } + _ => { + trace!("Unhandled event: {:?}", event); + None + } + } + }) + .filter_map(ready); + + // If the watcher is dropped, the stream will stop producing events. So, returning the watcher. + (watcher, initial_settings, change_stream) +} + +/// Wraps an UnboundedSender to implement the notify::EventHandler trait. This allows sending +/// file notification evnts to UnboundedSender instances. +struct UnboundedSenderEventHandler { + tx: UnboundedSender>, +} + +impl UnboundedSenderEventHandler { + fn new(tx: UnboundedSender>) -> Self { + Self { tx } + } +} + +impl EventHandler for UnboundedSenderEventHandler { + fn handle_event(&mut self, event: notify::Result) { + self.tx.send(event).unwrap(); + } +} diff --git a/datastores/gossip_kv/server/config/static/default.toml b/datastores/gossip_kv/server/config/static/default.toml new file mode 100644 index 000000000000..b651079e8aa8 --- /dev/null +++ b/datastores/gossip_kv/server/config/static/default.toml @@ -0,0 +1,5 @@ +seed_nodes = [] + +#[[seed_nodes]] +#id = "gossip-kv-seed-nodes-0" +#address = "gossip-kv-seed-nodes-0.gossip-kv-seed-nodes.default.svc.cluster.local:3000" \ No newline at end of file diff --git a/datastores/gossip_kv/server/config/static/development.toml b/datastores/gossip_kv/server/config/static/development.toml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/datastores/gossip_kv/server/main.rs b/datastores/gossip_kv/server/main.rs new file mode 100644 index 000000000000..cb7dac2c8b39 --- /dev/null +++ b/datastores/gossip_kv/server/main.rs @@ -0,0 +1,179 @@ +use std::convert::Infallible; +use std::fmt::Debug; +use std::future::ready; +use std::hash::Hash; +use std::io::Error; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + +use clap::Parser; +use gossip_kv::membership::{MemberDataBuilder, Protocol}; +use gossip_kv::server::{server, SeedNode}; +use gossip_kv::{ClientRequest, GossipMessage}; +use hydroflow::futures::{SinkExt, StreamExt}; +use hydroflow::tokio_stream::wrappers::IntervalStream; +use hydroflow::util::{bind_udp_bytes, ipv4_resolve}; +use hydroflow::{bincode, tokio}; +use prometheus::{gather, Encoder, TextEncoder}; +use tracing::{error, info, trace}; +use warp::Filter; + +use crate::config::{setup_settings_watch, SeedNodeSettings}; +use crate::membership::member_name; + +mod config; + +mod membership; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Parser)] +struct Opts { + /// Port to listen for gossip messages. + #[clap(short, long, default_value = "3000")] + gossip_port: u16, + + /// Port to listen for client requests. + #[clap(short, long, default_value = "3001")] + client_port: u16, +} + +fn make_seed_node(settings: &SeedNodeSettings) -> SeedNode { + SeedNode { + id: settings.id.clone(), + address: ipv4_resolve(&settings.address).unwrap(), + } +} + +async fn metrics_handler() -> Result { + let encoder = TextEncoder::new(); + let metric_families = gather(); + let mut buffer = Vec::new(); + encoder.encode(&metric_families, &mut buffer).unwrap(); + + Ok(warp::reply::with_header( + buffer, + "Content-Type", + encoder.format_type(), + )) +} + +#[hydroflow::main] +async fn main() { + tracing_subscriber::fmt::init(); + + let opts: Opts = Opts::parse(); + + let metrics_route = warp::path("metrics").and_then(metrics_handler); + tokio::spawn(async move { + info!("Starting metrics server on port 4003"); + warp::serve(metrics_route).run(([0, 0, 0, 0], 4003)).await; + }); + + // Setup protocol information in the member metadata. + let client_protocol_address = + SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), opts.client_port); + let gossip_protocol_address = + SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), opts.gossip_port); + + let member_data = MemberDataBuilder::new(member_name().clone()) + .add_protocol(Protocol::new("gossip".into(), gossip_protocol_address)) + .add_protocol(Protocol::new("client".into(), client_protocol_address)) + .build(); + + let (client_outbound, client_inbound, _) = bind_udp_bytes(client_protocol_address).await; + let (gossip_outbound, gossip_inbound, _) = bind_udp_bytes(gossip_protocol_address).await; + + info!( + "Server {:?} listening for client requests on: {:?}", + member_data.id, client_protocol_address + ); + + // TODO: Remove code duplication here. + // Setup message serialization for outbound client responses. + let client_ob = client_outbound.with(|(msg, addr)| { + ready(Ok::<(hydroflow::bytes::Bytes, SocketAddr), Error>(( + hydroflow::util::serialize_to_bytes(msg), + addr, + ))) + }); + + // Setup message deserialization for inbound client requests. + let client_ib = client_inbound.filter_map(|input| { + let mapped = match input { + Ok((bytes, addr)) => { + let msg: bincode::Result = + hydroflow::util::deserialize_from_bytes(&bytes); + match msg { + Ok(msg) => Some((msg, addr)), + Err(e) => { + error!("Error deserializing message: {:?}", e); + None + } + } + } + Err(e) => { + error!("Error receiving message: {:?}", e); + None + } + }; + ready(mapped) + }); + + // Setup message serialization for outbound client responses. + let gossip_ob = gossip_outbound.with(|(msg, addr)| { + ready(Ok::<(hydroflow::bytes::Bytes, SocketAddr), Error>(( + hydroflow::util::serialize_to_bytes(msg), + addr, + ))) + }); + + // Setup message deserialization for inbound client requests. + let gossip_ib = gossip_inbound.filter_map(|input| { + let mapped = match input { + Ok((bytes, addr)) => { + let msg: bincode::Result = + hydroflow::util::deserialize_from_bytes(&bytes); + match msg { + Ok(msg) => Some((msg, addr)), + Err(e) => { + error!("Error deserializing message: {:?}", e); + None + } + } + } + Err(e) => { + error!("Error receiving message: {:?}", e); + None + } + }; + ready(mapped) + }); + + let gossip_rx = + IntervalStream::new(tokio::time::interval(tokio::time::Duration::from_secs(5))).map(|_| ()); + + let (_watcher, server_settings, settings_stream) = setup_settings_watch(); + + let seed_nodes = server_settings + .seed_nodes + .iter() + .map(make_seed_node) + .collect::>(); + + let seed_node_stream = settings_stream.map(|settings| { + trace!("Settings updated. Reloading seed nodes"); + settings.seed_nodes.iter().map(make_seed_node).collect() + }); + + // Create and run the server + let mut server = server( + client_ib, + client_ob, + gossip_ib, + gossip_ob, + gossip_rx, + member_data, + seed_nodes, + seed_node_stream, + ); + + server.run_async().await; +} diff --git a/datastores/gossip_kv/server/membership.rs b/datastores/gossip_kv/server/membership.rs new file mode 100644 index 000000000000..2e20ab534bf0 --- /dev/null +++ b/datastores/gossip_kv/server/membership.rs @@ -0,0 +1,37 @@ +use std::sync::OnceLock; + +use gossip_kv::membership::MemberId; +// use rand::distributions::Distribution; +// use rand::{Rng}; + +/// This is a simple distribution that generates a random lower-case alphanumeric +// struct LowercaseAlphanumeric; +// +// impl Distribution for LowercaseAlphanumeric { +// fn sample(&self, rng: &mut R) -> char { +// let choices = b"abcdefghijklmnopqrstuvwxyz0123456789"; +// choices[rng.gen_range(0..choices.len())] as char +// } +// } + +/// Gets a name for the current process. +pub fn member_name() -> &'static MemberId { + static MEMBER_NAME: OnceLock = OnceLock::new(); + MEMBER_NAME.get_or_init(|| { + // Generate a lower-case alphanumeric suffix of length 4 + + // TODO: Random suffixes are good, but make benchmarking a pain. For now, we'll just use + // the hostname as-is. + + // let suffix: String = thread_rng() + // .sample_iter(&LowercaseAlphanumeric) + // .take(4) + // .map(char::from) + // .collect(); + + // Retrieve hostname + hostname::get().unwrap().to_str().unwrap().to_string() + + // format!("{}-{}", hostname, suffix) + }) +} From bf9dcd5a923dd4b5efa337a9127086e5609a1722 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Tue, 5 Nov 2024 12:06:45 -0800 Subject: [PATCH 25/74] refactor(hydroflow_plus): clean up traits for cycles and forward references (#1524) --- hydroflow_plus/src/cycle.rs | 9 ++-- hydroflow_plus/src/location/mod.rs | 12 +++-- hydroflow_plus/src/singleton.rs | 81 +++++++++++++++++++++--------- hydroflow_plus/src/stream.rs | 28 +++++------ 4 files changed, 84 insertions(+), 46 deletions(-) diff --git a/hydroflow_plus/src/cycle.rs b/hydroflow_plus/src/cycle.rs index 5da633542875..5f6bb98dd55a 100644 --- a/hydroflow_plus/src/cycle.rs +++ b/hydroflow_plus/src/cycle.rs @@ -2,7 +2,8 @@ use std::marker::PhantomData; use crate::location::Location; -pub struct TickCycle {} +pub enum ForwardRef {} +pub enum TickCycle {} pub trait DeferTick { fn defer_tick(self) -> Self; @@ -28,12 +29,12 @@ pub trait CycleCollectionWithInitial<'a, T>: CycleComplete<'a, T> { /// by a stream that is not yet known. /// /// See [`crate::FlowBuilder`] for an explainer on the type parameters. -pub struct HfForwardRef<'a, T, S: CycleComplete<'a, T>> { +pub struct HfForwardRef<'a, S: CycleComplete<'a, ForwardRef>> { pub(crate) ident: syn::Ident, - pub(crate) _phantom: PhantomData<(&'a mut &'a (), T, S)>, + pub(crate) _phantom: PhantomData<(&'a mut &'a (), S)>, } -impl<'a, T, S: CycleComplete<'a, T>> HfForwardRef<'a, T, S> { +impl<'a, S: CycleComplete<'a, ForwardRef>> HfForwardRef<'a, S> { pub fn complete(self, stream: S) { let ident = self.ident; S::complete(stream, ident) diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index be906087291e..6a7ff92797a8 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -8,7 +8,9 @@ use proc_macro2::Span; use stageleft::{q, Quoted}; use super::builder::FlowState; -use crate::cycle::{CycleCollection, CycleCollectionWithInitial, DeferTick, HfCycle, TickCycle}; +use crate::cycle::{ + CycleCollection, CycleCollectionWithInitial, DeferTick, ForwardRef, HfCycle, TickCycle, +}; use crate::ir::{HfPlusNode, HfPlusSource}; use crate::{Bounded, HfForwardRef, Optional, Singleton, Stream, Unbounded}; @@ -199,9 +201,9 @@ pub trait Location<'a>: Clone { ))) } - fn forward_ref>( + fn forward_ref>( &self, - ) -> (HfForwardRef<'a, (), S>, S) + ) -> (HfForwardRef<'a, S>, S) where Self: NoTick, { @@ -231,9 +233,9 @@ pub trait Location<'a>: Clone { ) } - fn tick_forward_ref>>( + fn tick_forward_ref>>( &self, - ) -> (HfForwardRef<'a, TickCycle, S>, S) + ) -> (HfForwardRef<'a, S>, S) where Self: NoTick, { diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index c7c1a73349f3..a540f853911c 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -7,7 +7,7 @@ use stageleft::{q, IntoQuotedMut, Quoted}; use crate::builder::FlowState; use crate::cycle::{ - CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick, TickCycle, + CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick, ForwardRef, TickCycle, }; use crate::ir::{HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::{check_matching_location, Location, LocationId, NoTick, Tick}; @@ -135,6 +135,26 @@ impl<'a, T, N: Location<'a>> DeferTick for Singleton> { } } +impl<'a, T, N: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> + for Singleton> +{ + type Location = Tick; + + fn create_source(ident: syn::Ident, initial: Self, location: Tick) -> Self { + let location_id = location.id(); + Singleton::new( + location, + HfPlusNode::Union( + Box::new(HfPlusNode::CycleSource { + ident, + location_kind: location_id, + }), + initial.ir_node.into_inner().into(), + ), + ) + } +} + impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> { fn complete(self, ident: syn::Ident) { self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { @@ -145,7 +165,7 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> CycleCollection<'a, TickCycle> for Singleton> { +impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Singleton> { type Location = Tick; fn create_source(ident: syn::Ident, location: Tick) -> Self { @@ -160,23 +180,13 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Singleton> CycleCollectionWithInitial<'a, TickCycle> - for Singleton> -{ - type Location = Tick; - - fn create_source(ident: syn::Ident, initial: Self, location: Tick) -> Self { - let location_id = location.id(); - Singleton::new( - location, - HfPlusNode::Union( - Box::new(HfPlusNode::CycleSource { - ident, - location_kind: location_id, - }), - initial.ir_node.into_inner().into(), - ), - ) +impl<'a, T, N: Location<'a>> CycleComplete<'a, ForwardRef> for Singleton> { + fn complete(self, ident: syn::Ident) { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(self.ir_node.into_inner()), + }); } } @@ -399,6 +409,21 @@ impl<'a, T, N: Location<'a>> DeferTick for Optional> { } } +impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> { + type Location = Tick; + + fn create_source(ident: syn::Ident, location: Tick) -> Self { + let location_id = location.id(); + Optional::new( + location, + HfPlusNode::CycleSource { + ident, + location_kind: location_id, + }, + ) + } +} + impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Optional> { fn complete(self, ident: syn::Ident) { self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { @@ -409,7 +434,7 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Optional> CycleCollection<'a, TickCycle> for Optional> { +impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Optional> { type Location = Tick; fn create_source(ident: syn::Ident, location: Tick) -> Self { @@ -424,17 +449,17 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional + NoTick> CycleComplete<'a, ()> for Optional { +impl<'a, T, N: Location<'a>> CycleComplete<'a, ForwardRef> for Optional> { fn complete(self, ident: syn::Ident) { self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, location_kind: self.location_kind(), - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + input: Box::new(self.ir_node.into_inner()), }); } } -impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ()> for Optional { +impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Optional { type Location = N; fn create_source(ident: syn::Ident, location: N) -> Self { @@ -449,6 +474,16 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ()> for Optional + NoTick> CycleComplete<'a, ForwardRef> for Optional { + fn complete(self, ident: syn::Ident) { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + }); + } +} + impl<'a, T, W, N: Location<'a>> From> for Optional { fn from(singleton: Singleton) -> Self { Optional::some(singleton) diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 4a6a1e3a6108..f17ed655805f 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -15,7 +15,7 @@ use syn::parse_quote; // TODO(shadaj): have to uses super due to stageleft limitations use super::staging_util::get_this_crate; use crate::builder::FlowState; -use crate::cycle::{CycleCollection, CycleComplete, DeferTick, TickCycle}; +use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::cluster::ClusterSelfId; use crate::location::external_process::{ExternalBincodeStream, ExternalBytesPort}; @@ -64,16 +64,6 @@ impl<'a, T, N: Location<'a>> DeferTick for Stream> { } } -impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Stream> { - fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(self.ir_node.into_inner()), - }); - } -} - impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream> { type Location = Tick; @@ -89,17 +79,17 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream + NoTick> CycleComplete<'a, ()> for Stream { +impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Stream> { fn complete(self, ident: syn::Ident) { self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { ident, location_kind: self.location_kind(), - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + input: Box::new(self.ir_node.into_inner()), }); } } -impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ()> for Stream { +impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Stream { type Location = N; fn create_source(ident: syn::Ident, location: N) -> Self { @@ -114,6 +104,16 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ()> for Stream + NoTick> CycleComplete<'a, ForwardRef> for Stream { + fn complete(self, ident: syn::Ident) { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + }); + } +} + impl<'a, T, W, N: Location<'a>> Stream { pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { Stream { From 244207c2acd2243ece6e787d54eadacf06e9e8bb Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Tue, 5 Nov 2024 12:33:58 -0800 Subject: [PATCH 26/74] refactor(hydroflow_plus): dedup signatures for `Stream` operators (#1525) --- hydroflow_plus/src/location/cluster.rs | 11 +- .../src/location/external_process.rs | 11 +- hydroflow_plus/src/location/mod.rs | 11 +- hydroflow_plus/src/location/process.rs | 11 +- hydroflow_plus/src/location/tick.rs | 6 +- hydroflow_plus/src/singleton.rs | 14 +- hydroflow_plus/src/stream.rs | 372 ++++++++---------- 7 files changed, 181 insertions(+), 255 deletions(-) diff --git a/hydroflow_plus/src/location/cluster.rs b/hydroflow_plus/src/location/cluster.rs index e18b5f2af1b8..3c7de35a841a 100644 --- a/hydroflow_plus/src/location/cluster.rs +++ b/hydroflow_plus/src/location/cluster.rs @@ -54,15 +54,8 @@ impl<'a, C> Location<'a> for Cluster<'a, C> { &self.flow_state } - fn make_from(id: LocationId, flow_state: FlowState) -> Self { - match id { - LocationId::Cluster(id) => Cluster { - id, - flow_state, - _phantom: PhantomData, - }, - _ => panic!(), - } + fn is_top_level() -> bool { + true } } diff --git a/hydroflow_plus/src/location/external_process.rs b/hydroflow_plus/src/location/external_process.rs index 4d992d36f5af..de347154722b 100644 --- a/hydroflow_plus/src/location/external_process.rs +++ b/hydroflow_plus/src/location/external_process.rs @@ -53,15 +53,8 @@ impl<'a, P> Location<'a> for ExternalProcess<'a, P> { &self.flow_state } - fn make_from(id: LocationId, flow_state: FlowState) -> Self { - match id { - LocationId::ExternalProcess(id) => ExternalProcess { - id, - flow_state, - _phantom: PhantomData, - }, - _ => panic!(), - } + fn is_top_level() -> bool { + true } } diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index 6a7ff92797a8..279b6d2a9768 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -55,7 +55,7 @@ pub trait Location<'a>: Clone { fn flow_state(&self) -> &FlowState; - fn make_from(id: LocationId, flow_state: FlowState) -> Self; + fn is_top_level() -> bool; fn nest(&self) -> Tick where @@ -111,10 +111,12 @@ pub trait Location<'a>: Clone { fn source_iter>( &self, e: impl Quoted<'a, E>, - ) -> Stream + ) -> Stream where Self: Sized + NoTick, { + // TODO(shadaj): we mark this as unbounded because we do not yet have a representation + // for bounded top-level streams, and this is the only way to generate one let e = e.splice_untyped(); Stream::new( @@ -126,10 +128,13 @@ pub trait Location<'a>: Clone { ) } - fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton + fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton where Self: Sized + NoTick, { + // TODO(shadaj): we mark this as unbounded because we do not yet have a representation + // for bounded top-level singletons, and this is the only way to generate one + let e_arr = q!([e]); let e = e_arr.splice_untyped(); diff --git a/hydroflow_plus/src/location/process.rs b/hydroflow_plus/src/location/process.rs index dadda1ce8f3c..301d7ce6c2a8 100644 --- a/hydroflow_plus/src/location/process.rs +++ b/hydroflow_plus/src/location/process.rs @@ -28,14 +28,7 @@ impl<'a, P> Location<'a> for Process<'a, P> { &self.flow_state } - fn make_from(id: LocationId, flow_state: FlowState) -> Self { - match id { - LocationId::Process(id) => Process { - id, - flow_state, - _phantom: PhantomData, - }, - _ => panic!(), - } + fn is_top_level() -> bool { + true } } diff --git a/hydroflow_plus/src/location/tick.rs b/hydroflow_plus/src/location/tick.rs index 48a27e8bbf04..acdf26e72756 100644 --- a/hydroflow_plus/src/location/tick.rs +++ b/hydroflow_plus/src/location/tick.rs @@ -26,9 +26,7 @@ impl<'a, L: Location<'a>> Location<'a> for Tick { self.l.flow_state() } - fn make_from(id: LocationId, flow_state: FlowState) -> Self { - Tick { - l: L::make_from(id, flow_state), - } + fn is_top_level() -> bool { + false } } diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index a540f853911c..f84e3d1d4d03 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -515,6 +515,15 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Optional { } impl<'a, T, W, N: Location<'a>> Optional { + // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream + pub fn into_stream(self) -> Stream { + if N::is_top_level() { + panic!("Converting an optional to a stream is not yet supported at the top level"); + } + + Stream::new(self.location, self.ir_node.into_inner()) + } + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( self.location, @@ -563,11 +572,6 @@ impl<'a, T, W, N: Location<'a>> Optional { } impl<'a, T, N: Location<'a>> Optional> { - // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream - pub fn into_stream(self) -> Stream> { - Stream::new(self.location, self.ir_node.into_inner()) - } - pub fn cross_singleton( self, other: impl Into>>, diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index f17ed655805f..fdaf9150fa87 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -35,17 +35,15 @@ pub enum Bounded {} /// An infinite stream of elements of type `T`. /// /// Type Parameters: -/// - `'a`: the lifetime of the final Hydroflow graph, which constraints -/// which values can be captured in closures passed to operators /// - `T`: the type of elements in the stream -/// - `W`: the boundedness of the stream, which is either [`Bounded`] +/// - `B`: the boundedness of the stream, which is either [`Bounded`] /// or [`Unbounded`] /// - `N`: the type of the node that the stream is materialized on -pub struct Stream { +pub struct Stream { location: N, pub(crate) ir_node: RefCell, - _phantom: PhantomData<(T, N, W)>, + _phantom: PhantomData<(T, B, N)>, } impl<'a, T, W, N: Location<'a>> Stream { @@ -159,6 +157,13 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } + pub fn cloned(self) -> Stream + where + T: Clone, + { + self.map(q!(|d| d.clone())) + } + pub fn flat_map, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F>, @@ -221,7 +226,17 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - // TODO(shadaj): should allow for differing windows, using strongest one + /// Allow this stream through if the other stream has elements, otherwise the output is empty. + pub fn continue_if(self, signal: Optional) -> Stream { + self.cross_singleton(signal.map(q!(|_u| ()))) + .map(q!(|(d, _signal)| d)) + } + + /// Allow this stream through if the other stream is empty, otherwise the output is empty. + pub fn continue_unless(self, other: Optional) -> Stream { + self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) + } + pub fn cross_product(self, other: Stream) -> Stream<(T, O), W, N> where T: Clone, @@ -250,119 +265,100 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - pub fn unique(self) -> Stream - where - T: Eq + Hash, - { + pub fn enumerate(self) -> Stream<(usize, T), W, N> { Stream::new( self.location, - HfPlusNode::Unique(Box::new(self.ir_node.into_inner())), - ) - } - - pub fn dest_sink + 'a>(self, sink: impl Quoted<'a, S>) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::DestSink { - sink: sink.splice_typed().into(), - input: Box::new(self.ir_node.into_inner()), - }); - } -} - -impl<'a, T, N: Location<'a>> Stream> { - pub fn all_ticks(self) -> Stream { - Stream::new( - self.location.outer().clone(), - HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), + HfPlusNode::Enumerate(Box::new(self.ir_node.into_inner())), ) } - pub fn persist(self) -> Stream> + pub fn unique(self) -> Stream where - T: Clone, + T: Eq + Hash, { Stream::new( self.location, - HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), + HfPlusNode::Unique(Box::new(self.ir_node.into_inner())), ) } - pub fn defer_tick(self) -> Stream> { - Stream::new( - self.location, - HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), - ) - } + pub fn filter_not_in(self, other: Stream) -> Stream + where + T: Eq + Hash, + { + check_matching_location(&self.location, &other.location); - pub fn inspect( - self, - f: impl IntoQuotedMut<'a, F>, - ) -> Stream> { Stream::new( self.location, - HfPlusNode::Inspect { - f: f.splice_fn1_borrow().into(), - input: Box::new(self.ir_node.into_inner()), - }, + HfPlusNode::Difference( + Box::new(self.ir_node.into_inner()), + Box::new(other.ir_node.into_inner()), + ), ) } - pub fn first(self) -> Optional> { + pub fn first(self) -> Optional { Optional::new(self.location, self.ir_node.into_inner()) } - /// Allow this stream through if the other stream has elements, otherwise the output is empty. - pub fn continue_if( - self, - signal: Optional>, - ) -> Stream> { - self.cross_singleton(signal.map(q!(|_u| ()))) - .map(q!(|(d, _signal)| d)) - } - - /// Allow this stream through if the other stream is empty, otherwise the output is empty. - pub fn continue_unless( - self, - other: Optional>, - ) -> Stream> { - self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) - } - - pub fn enumerate(self) -> Stream<(usize, T), Bounded, Tick> { - Stream::new( - self.location, - HfPlusNode::Enumerate(Box::new(self.ir_node.into_inner())), - ) + pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { + if N::is_top_level() { + Stream::new( + self.location, + HfPlusNode::Persist(Box::new(HfPlusNode::Inspect { + f: f.splice_fn1_borrow().into(), + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + })), + ) + } else { + Stream::new( + self.location, + HfPlusNode::Inspect { + f: f.splice_fn1_borrow().into(), + input: Box::new(self.ir_node.into_inner()), + }, + ) + } } pub fn fold A + 'a, F: Fn(&mut A, T)>( self, init: impl IntoQuotedMut<'a, I>, comb: impl IntoQuotedMut<'a, F>, - ) -> Singleton> { - Singleton::new( - self.location, - HfPlusNode::Fold { - init: init.splice_fn0().into(), - acc: comb.splice_fn2_borrow_mut().into(), - input: Box::new(self.ir_node.into_inner()), - }, - ) + ) -> Singleton { + let mut core = HfPlusNode::Fold { + init: init.splice_fn0().into(), + acc: comb.splice_fn2_borrow_mut().into(), + input: Box::new(self.ir_node.into_inner()), + }; + + if N::is_top_level() { + // top-level (possibly unbounded) singletons are represented as + // a stream which produces all values from all ticks every tick, + // so Unpersist will always give the lastest aggregation + core = HfPlusNode::Persist(Box::new(core)); + } + + Singleton::new(self.location, core) } pub fn reduce( self, comb: impl IntoQuotedMut<'a, F>, - ) -> Optional> { - Optional::new( - self.location, - HfPlusNode::Reduce { - f: comb.splice_fn2_borrow_mut().into(), - input: Box::new(self.ir_node.into_inner()), - }, - ) + ) -> Optional { + let mut core = HfPlusNode::Reduce { + f: comb.splice_fn2_borrow_mut().into(), + input: Box::new(self.ir_node.into_inner()), + }; + + if N::is_top_level() { + core = HfPlusNode::Persist(Box::new(core)); + } + + Optional::new(self.location, core) } - pub fn max(self) -> Optional> + pub fn max(self) -> Optional where T: Ord, { @@ -373,7 +369,7 @@ impl<'a, T, N: Location<'a>> Stream> { })) } - pub fn min(self) -> Optional> + pub fn min(self) -> Optional where T: Ord, { @@ -384,7 +380,13 @@ impl<'a, T, N: Location<'a>> Stream> { })) } - pub fn sort(self) -> Stream> + pub fn count(self) -> Singleton { + self.fold(q!(|| 0usize), q!(|count, _| *count += 1)) + } +} + +impl<'a, T, N: Location<'a>> Stream { + pub fn sort(self) -> Stream where T: Ord, { @@ -393,199 +395,137 @@ impl<'a, T, N: Location<'a>> Stream> { HfPlusNode::Sort(Box::new(self.ir_node.into_inner())), ) } +} - pub fn count(self) -> Singleton> { - self.fold(q!(|| 0usize), q!(|count, _| *count += 1)) - } +impl<'a, K, V1, W, N: Location<'a>> Stream<(K, V1), W, N> { + pub fn join(self, n: Stream<(K, V2), W, N>) -> Stream<(K, (V1, V2)), W, N> + where + K: Eq + Hash, + { + check_matching_location(&self.location, &n.location); - pub fn delta(self) -> Stream> { Stream::new( self.location, - HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), - ) - } -} - -impl<'a, T, W, N: Location<'a> + NoTick> Stream { - pub fn tick_batch(self) -> Stream> { - Stream::new( - self.location.nest(), - HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), + HfPlusNode::Join( + Box::new(self.ir_node.into_inner()), + Box::new(n.ir_node.into_inner()), + ), ) } - pub fn tick_prefix(self) -> Stream> + pub fn anti_join(self, n: Stream) -> Stream<(K, V1), W, N> where - T: Clone, + K: Eq + Hash, { - self.tick_batch().persist() - } + check_matching_location(&self.location, &n.location); - pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( self.location, - HfPlusNode::Persist(Box::new(HfPlusNode::Inspect { - f: f.splice_fn1_borrow().into(), - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - })), + HfPlusNode::AntiJoin( + Box::new(self.ir_node.into_inner()), + Box::new(n.ir_node.into_inner()), + ), ) } } -impl<'a, T, W, N: Location<'a> + NoTick> Stream { - pub fn for_each(self, f: impl IntoQuotedMut<'a, F>) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::ForEach { - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - f: f.splice_fn1().into(), - }); - } -} - -impl<'a, T, N: Location<'a> + NoTick> Stream { - pub fn sample_every( - self, - interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { - let samples = self.location.source_interval(interval).tick_batch(); - self.tick_batch().continue_if(samples.first()).all_ticks() - } - - pub fn fold A + 'a, F: Fn(&mut A, T)>( +impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { + pub fn fold_keyed A + 'a, F: Fn(&mut A, V) + 'a>( self, init: impl IntoQuotedMut<'a, I>, comb: impl IntoQuotedMut<'a, F>, - ) -> Singleton { - // unbounded singletons are represented as a stream - // which produces all values from all ticks every tick, - // so delta will always give the lastest aggregation - Singleton::new( + ) -> Stream<(K, A), Bounded, Tick> { + Stream::new( self.location, - HfPlusNode::Persist(Box::new(HfPlusNode::Fold { + HfPlusNode::FoldKeyed { init: init.splice_fn0().into(), acc: comb.splice_fn2_borrow_mut().into(), input: Box::new(self.ir_node.into_inner()), - })), + }, ) } - pub fn reduce( + pub fn reduce_keyed( self, comb: impl IntoQuotedMut<'a, F>, - ) -> Optional { - Optional::new( + ) -> Stream<(K, V), Bounded, Tick> { + Stream::new( self.location, - HfPlusNode::Persist(Box::new(HfPlusNode::Reduce { + HfPlusNode::ReduceKeyed { f: comb.splice_fn2_borrow_mut().into(), input: Box::new(self.ir_node.into_inner()), - })), + }, ) } +} - pub fn max(self) -> Optional - where - T: Ord, - { - self.reduce(q!(|curr, new| { - if new > *curr { - *curr = new; - } - })) +impl<'a, T, W, N: Location<'a> + NoTick> Stream { + pub fn tick_batch(self) -> Stream> { + Stream::new( + self.location.nest(), + HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), + ) } - pub fn min(self) -> Optional + pub fn tick_prefix(self) -> Stream> where - T: Ord, + T: Clone, { - self.reduce(q!(|curr, new| { - if new < *curr { - *curr = new; - } - })) + self.tick_batch().persist() } -} -impl<'a, T, N: Location<'a>> Stream { - pub fn filter_not_in(self, other: Stream) -> Stream - where - T: Eq + Hash, - { - check_matching_location(&self.location, &other.location); + pub fn sample_every( + self, + interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, + ) -> Stream { + let samples = self.location.source_interval(interval).tick_batch(); + self.tick_batch().continue_if(samples.first()).all_ticks() + } - Stream::new( - self.location, - HfPlusNode::Difference( - Box::new(self.ir_node.into_inner()), - Box::new(other.ir_node.into_inner()), - ), - ) + pub fn for_each(self, f: impl IntoQuotedMut<'a, F>) { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::ForEach { + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + f: f.splice_fn1().into(), + }); } -} -impl<'a, T: Clone, W, N: Location<'a>> Stream<&T, W, N> { - pub fn cloned(self) -> Stream { - self.map(q!(|d| d.clone())) + pub fn dest_sink + 'a>(self, sink: impl Quoted<'a, S>) { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::DestSink { + sink: sink.splice_typed().into(), + input: Box::new(self.ir_node.into_inner()), + }); } } -impl<'a, K, V1, W, N: Location<'a>> Stream<(K, V1), W, N> { - // TODO(shadaj): figure out window semantics - pub fn join(self, n: Stream<(K, V2), W2, N>) -> Stream<(K, (V1, V2)), W, N> - where - K: Eq + Hash, - { - check_matching_location(&self.location, &n.location); - +impl<'a, T, N: Location<'a>> Stream> { + pub fn all_ticks(self) -> Stream { Stream::new( - self.location, - HfPlusNode::Join( - Box::new(self.ir_node.into_inner()), - Box::new(n.ir_node.into_inner()), - ), + self.location.outer().clone(), + HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn anti_join(self, n: Stream) -> Stream<(K, V1), W, N> + pub fn persist(self) -> Stream> where - K: Eq + Hash, + T: Clone, { - check_matching_location(&self.location, &n.location); - Stream::new( self.location, - HfPlusNode::AntiJoin( - Box::new(self.ir_node.into_inner()), - Box::new(n.ir_node.into_inner()), - ), + HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } -} -impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { - pub fn fold_keyed A + 'a, F: Fn(&mut A, V) + 'a>( - self, - init: impl IntoQuotedMut<'a, I>, - comb: impl IntoQuotedMut<'a, F>, - ) -> Stream<(K, A), Bounded, Tick> { + pub fn defer_tick(self) -> Stream> { Stream::new( self.location, - HfPlusNode::FoldKeyed { - init: init.splice_fn0().into(), - acc: comb.splice_fn2_borrow_mut().into(), - input: Box::new(self.ir_node.into_inner()), - }, + HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) } - pub fn reduce_keyed( - self, - comb: impl IntoQuotedMut<'a, F>, - ) -> Stream<(K, V), Bounded, Tick> { + pub fn delta(self) -> Stream> { Stream::new( self.location, - HfPlusNode::ReduceKeyed { - f: comb.splice_fn2_borrow_mut().into(), - input: Box::new(self.ir_node.into_inner()), - }, + HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), ) } } From 919099ea3a414560b473ec89b993eeb26dfa2579 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Tue, 5 Nov 2024 16:09:03 -0800 Subject: [PATCH 27/74] refactor(hydroflow_plus)!: dedup signatures for `Singleton` and `Optional` (#1526) Also renames `cross_singleton` to `zip` when both sides are singleton-like. --- hydroflow_plus/src/lib.rs | 5 +- hydroflow_plus/src/optional.rs | 378 ++++++++++++ hydroflow_plus/src/singleton.rs | 560 +++--------------- hydroflow_plus_test/src/cluster/paxos.rs | 6 +- .../src/cluster/paxos_bench.rs | 2 +- hydroflow_plus_test/src/cluster/paxos_kv.rs | 25 +- ...ter__compute_pi__tests__compute_pi_ir.snap | 4 +- ..._tests__compute_pi_ir@surface_graph_1.snap | 4 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 30 +- 9 files changed, 488 insertions(+), 526 deletions(-) create mode 100644 hydroflow_plus/src/optional.rs diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index a0fe5f3c9619..0689ce2c04ab 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -22,7 +22,10 @@ pub mod stream; pub use stream::{Bounded, Stream, Unbounded}; pub mod singleton; -pub use singleton::{Optional, Singleton}; +pub use singleton::Singleton; + +pub mod optional; +pub use optional::Optional; pub mod location; pub use location::{Cluster, ClusterId, Location, Process, Tick}; diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs new file mode 100644 index 000000000000..5c951990b120 --- /dev/null +++ b/hydroflow_plus/src/optional.rs @@ -0,0 +1,378 @@ +use std::cell::RefCell; +use std::marker::PhantomData; +use std::ops::Deref; +use std::rc::Rc; + +use stageleft::{q, IntoQuotedMut, Quoted}; +use syn::parse_quote; + +use crate::builder::FlowState; +use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; +use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; +use crate::location::{check_matching_location, LocationId, NoTick}; +use crate::{Bounded, Location, Singleton, Stream, Tick, Unbounded}; + +pub struct Optional { + pub(crate) location: N, + pub(crate) ir_node: RefCell, + + _phantom: PhantomData<(T, N, W)>, +} + +impl<'a, T, W, N: Location<'a>> Optional { + pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { + Optional { + location, + ir_node: RefCell::new(ir_node), + _phantom: PhantomData, + } + } + + pub fn some(singleton: Singleton) -> Self { + Optional::new(singleton.location, singleton.ir_node.into_inner()) + } + + fn location_kind(&self) -> LocationId { + self.location.id() + } + + fn flow_state(&self) -> &FlowState { + self.location.flow_state() + } +} + +impl<'a, T, N: Location<'a>> DeferTick for Optional> { + fn defer_tick(self) -> Self { + Optional::defer_tick(self) + } +} + +impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> { + type Location = Tick; + + fn create_source(ident: syn::Ident, location: Tick) -> Self { + let location_id = location.id(); + Optional::new( + location, + HfPlusNode::CycleSource { + ident, + location_kind: location_id, + }, + ) + } +} + +impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Optional> { + fn complete(self, ident: syn::Ident) { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(self.ir_node.into_inner()), + }); + } +} + +impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Optional> { + type Location = Tick; + + fn create_source(ident: syn::Ident, location: Tick) -> Self { + let location_id = location.id(); + Optional::new( + location, + HfPlusNode::CycleSource { + ident, + location_kind: location_id, + }, + ) + } +} + +impl<'a, T, N: Location<'a>> CycleComplete<'a, ForwardRef> for Optional> { + fn complete(self, ident: syn::Ident) { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(self.ir_node.into_inner()), + }); + } +} + +impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Optional { + type Location = N; + + fn create_source(ident: syn::Ident, location: N) -> Self { + let location_id = location.id(); + Optional::new( + location, + HfPlusNode::Persist(Box::new(HfPlusNode::CycleSource { + ident, + location_kind: location_id, + })), + ) + } +} + +impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Optional { + fn complete(self, ident: syn::Ident) { + self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + }); + } +} + +impl<'a, T, W, N: Location<'a>> From> for Optional { + fn from(singleton: Singleton) -> Self { + Optional::some(singleton) + } +} + +impl<'a, T: Clone, W, N: Location<'a>> Clone for Optional { + fn clone(&self) -> Self { + if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { + let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); + *self.ir_node.borrow_mut() = HfPlusNode::Tee { + inner: TeeNode(Rc::new(RefCell::new(orig_ir_node))), + }; + } + + if let HfPlusNode::Tee { inner } = self.ir_node.borrow().deref() { + Optional { + location: self.location.clone(), + ir_node: HfPlusNode::Tee { + inner: TeeNode(inner.0.clone()), + } + .into(), + _phantom: PhantomData, + } + } else { + unreachable!() + } + } +} + +impl<'a, T, W, N: Location<'a>> Optional { + // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream + pub fn into_stream(self) -> Stream { + if N::is_top_level() { + panic!("Converting an optional to a stream is not yet supported at the top level"); + } + + Stream::new(self.location, self.ir_node.into_inner()) + } + + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { + Optional::new( + self.location, + HfPlusNode::Map { + f: f.splice_fn1().into(), + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + + pub fn flat_map, F: Fn(T) -> I + 'a>( + self, + f: impl IntoQuotedMut<'a, F>, + ) -> Stream { + Stream::new( + self.location, + HfPlusNode::FlatMap { + f: f.splice_fn1().into(), + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + + pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { + Optional::new( + self.location, + HfPlusNode::Filter { + f: f.splice_fn1_borrow().into(), + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + + pub fn filter_map Option + 'a>( + self, + f: impl IntoQuotedMut<'a, F>, + ) -> Optional { + Optional::new( + self.location, + HfPlusNode::FilterMap { + f: f.splice_fn1().into(), + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + + pub fn union(self, other: Optional) -> Optional { + check_matching_location(&self.location, &other.location); + + if N::is_top_level() { + Optional::new( + self.location, + HfPlusNode::Persist(Box::new(HfPlusNode::Union( + Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + Box::new(HfPlusNode::Unpersist(Box::new(other.ir_node.into_inner()))), + ))), + ) + } else { + Optional::new( + self.location, + HfPlusNode::Union( + Box::new(self.ir_node.into_inner()), + Box::new(other.ir_node.into_inner()), + ), + ) + } + } + + pub fn zip(self, other: impl Into>) -> Optional<(T, O), W, N> + where + O: Clone, + { + let other: Optional = other.into(); + check_matching_location(&self.location, &other.location); + + if N::is_top_level() { + Optional::new( + self.location, + HfPlusNode::Persist(Box::new(HfPlusNode::CrossSingleton( + Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + Box::new(HfPlusNode::Unpersist(Box::new(other.ir_node.into_inner()))), + ))), + ) + } else { + Optional::new( + self.location, + HfPlusNode::CrossSingleton( + Box::new(self.ir_node.into_inner()), + Box::new(other.ir_node.into_inner()), + ), + ) + } + } + + pub fn unwrap_or(self, other: Singleton) -> Singleton { + check_matching_location(&self.location, &other.location); + + if N::is_top_level() { + Singleton::new( + self.location, + HfPlusNode::Persist(Box::new(HfPlusNode::Union( + Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + Box::new(HfPlusNode::Unpersist(Box::new(other.ir_node.into_inner()))), + ))), + ) + } else { + Singleton::new( + self.location, + HfPlusNode::Union( + Box::new(self.ir_node.into_inner()), + Box::new(other.ir_node.into_inner()), + ), + ) + } + } + + pub fn into_singleton(self) -> Singleton, W, N> + where + T: Clone, + { + let none: syn::Expr = parse_quote!([::std::option::Option::None]); + let core_ir = HfPlusNode::Persist(Box::new(HfPlusNode::Source { + source: HfPlusSource::Iter(none.into()), + location_kind: self.location.id(), + })); + + let none_singleton = if N::is_top_level() { + Singleton::new( + self.location.clone(), + HfPlusNode::Persist(Box::new(core_ir)), + ) + } else { + Singleton::new(self.location.clone(), core_ir) + }; + + self.map(q!(|v| Some(v))).unwrap_or(none_singleton) + } +} + +impl<'a, T, N: Location<'a>> Optional { + pub fn continue_if(self, signal: Optional) -> Optional { + self.zip(signal.map(q!(|_u| ()))).map(q!(|(d, _signal)| d)) + } + + pub fn continue_unless(self, other: Optional) -> Optional { + self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) + } + + pub fn then(self, value: Singleton) -> Optional { + value.continue_if(self) + } +} + +impl<'a, T, B, N: Location<'a> + NoTick> Optional { + pub fn latest_tick(self) -> Optional> { + Optional::new( + self.location.nest(), + HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), + ) + } + + pub fn tick_samples(self) -> Stream { + self.latest_tick().all_ticks() + } + + pub fn sample_every( + self, + interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, + ) -> Stream { + let samples = self.location.source_interval(interval).tick_batch(); + + self.latest_tick() + .continue_if(samples.first()) + .latest() + .tick_samples() + } +} + +impl<'a, T, N: Location<'a>> Optional> { + pub fn all_ticks(self) -> Stream { + Stream::new( + self.location.outer().clone(), + HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), + ) + } + + pub fn latest(self) -> Optional { + Optional::new( + self.location.outer().clone(), + HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), + ) + } + + pub fn defer_tick(self) -> Optional> { + Optional::new( + self.location, + HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), + ) + } + + pub fn persist(self) -> Stream> { + Stream::new( + self.location, + HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), + ) + } + + pub fn delta(self) -> Optional> { + Optional::new( + self.location, + HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), + ) + } +} diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index f84e3d1d4d03..7c5df816fa89 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -12,94 +12,10 @@ use crate::cycle::{ use crate::ir::{HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::{check_matching_location, Location, LocationId, NoTick, Tick}; use crate::stream::{Bounded, Unbounded}; -use crate::Stream; - -pub trait CrossResult<'a, Other> { - type Out; - type Location; - - fn other_location(other: &Other) -> Self::Location; - fn other_ir_node(other: Other) -> HfPlusNode; - - fn make(location: Self::Location, ir_node: HfPlusNode) -> Self::Out; -} - -impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Singleton> - for Singleton -{ - type Out = Singleton<(T, U), W, N>; - type Location = N; - - fn other_location(other: &Singleton) -> N { - other.location.clone() - } - - fn other_ir_node(other: Singleton) -> HfPlusNode { - other.ir_node.into_inner() - } - - fn make(location: N, ir_node: HfPlusNode) -> Self::Out { - Singleton::new(location, ir_node) - } -} - -impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Optional> - for Singleton -{ - type Out = Optional<(T, U), W, N>; - type Location = N; - - fn other_location(other: &Optional) -> N { - other.location.clone() - } - - fn other_ir_node(other: Optional) -> HfPlusNode { - other.ir_node.into_inner() - } - - fn make(location: N, ir_node: HfPlusNode) -> Self::Out { - Optional::new(location, ir_node) - } -} - -impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Optional> for Optional { - type Out = Optional<(T, U), W, N>; - type Location = N; - - fn other_location(other: &Optional) -> N { - other.location.clone() - } - - fn other_ir_node(other: Optional) -> HfPlusNode { - other.ir_node.into_inner() - } - - fn make(location: N, ir_node: HfPlusNode) -> Self::Out { - Optional::new(location, ir_node) - } -} - -impl<'a, T, U: Clone, W, N: Location<'a>> CrossResult<'a, Singleton> - for Optional -{ - type Out = Optional<(T, U), W, N>; - type Location = N; - - fn other_location(other: &Singleton) -> N { - other.location.clone() - } - - fn other_ir_node(other: Singleton) -> HfPlusNode { - other.ir_node.into_inner() - } - - fn make(location: N, ir_node: HfPlusNode) -> Self::Out { - Optional::new(location, ir_node) - } -} +use crate::{Optional, Stream}; pub struct Singleton { - location: N, + pub(crate) location: N, pub(crate) ir_node: RefCell, _phantom: PhantomData<(T, N, W)>, @@ -215,6 +131,11 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Singleton { } impl<'a, T, W, N: Location<'a>> Singleton { + // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream + pub fn into_stream(self) -> Stream { + Stream::new(self.location, self.ir_node.into_inner()) + } + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Singleton { Singleton::new( self.location, @@ -260,79 +181,40 @@ impl<'a, T, W, N: Location<'a>> Singleton { }, ) } -} -impl<'a, T, N: Location<'a>> Singleton> { - // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream - pub fn into_stream(self) -> Stream> { - Stream::new(self.location, self.ir_node.into_inner()) - } - - pub fn cross_singleton(self, other: Other) -> >::Out + pub fn zip(self, other: Other) -> >::Out where - Self: CrossResult<'a, Other, Location = Tick>, + Self: ZipResult<'a, Other, Location = N>, { check_matching_location(&self.location, &Self::other_location(&other)); - Self::make( - self.location, - HfPlusNode::CrossSingleton( - Box::new(self.ir_node.into_inner()), - Box::new(Self::other_ir_node(other)), - ), - ) - } - - pub fn continue_if( - self, - signal: Optional>, - ) -> Optional> { - self.cross_singleton(signal.map(q!(|_u| ()))) - .map(q!(|(d, _signal)| d)) - } - - pub fn continue_unless( - self, - other: Optional>, - ) -> Optional> { - self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) + if N::is_top_level() { + Self::make( + self.location, + HfPlusNode::Persist(Box::new(HfPlusNode::CrossSingleton( + Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + Box::new(HfPlusNode::Unpersist(Box::new(Self::other_ir_node(other)))), + ))), + ) + } else { + Self::make( + self.location, + HfPlusNode::CrossSingleton( + Box::new(self.ir_node.into_inner()), + Box::new(Self::other_ir_node(other)), + ), + ) + } } } -impl<'a, T, N: Location<'a>> Singleton> { - pub fn all_ticks(self) -> Stream { - Stream::new( - self.location.outer().clone(), - HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), - ) - } - - pub fn latest(self) -> Singleton { - Singleton::new( - self.location.outer().clone(), - HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), - ) - } - - pub fn defer_tick(self) -> Singleton> { - Singleton::new( - self.location, - HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), - ) +impl<'a, T, N: Location<'a>> Singleton { + pub fn continue_if(self, signal: Optional) -> Optional { + self.zip(signal.map(q!(|_u| ()))).map(q!(|(d, _signal)| d)) } - pub fn persist(self) -> Stream> { - Stream::new( - self.location, - HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), - ) - } - - pub fn delta(self) -> Optional> { - Optional::new( - self.location, - HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), - ) + pub fn continue_unless(self, other: Optional) -> Optional { + self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } } @@ -344,6 +226,10 @@ impl<'a, T, B, N: Location<'a> + NoTick> Singleton { ) } + pub fn tick_samples(self) -> Stream { + self.latest_tick().all_ticks() + } + pub fn sample_every( self, interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, @@ -357,297 +243,7 @@ impl<'a, T, B, N: Location<'a> + NoTick> Singleton { } } -impl<'a, T, N: Location<'a> + NoTick> Singleton { - pub fn cross_singleton(self, other: Other) -> >::Out - where - Self: CrossResult<'a, Other, Location = N>, - { - check_matching_location(&self.location, &Self::other_location(&other)); - - Self::make( - self.location, - HfPlusNode::Persist(Box::new(HfPlusNode::CrossSingleton( - Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - Box::new(HfPlusNode::Unpersist(Box::new(Self::other_ir_node(other)))), - ))), - ) - } -} - -pub struct Optional { - pub(crate) location: N, - pub(crate) ir_node: RefCell, - - _phantom: PhantomData<(T, N, W)>, -} - -impl<'a, T, W, N: Location<'a>> Optional { - pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { - Optional { - location, - ir_node: RefCell::new(ir_node), - _phantom: PhantomData, - } - } - - pub fn some(singleton: Singleton) -> Self { - Optional::new(singleton.location, singleton.ir_node.into_inner()) - } - - fn location_kind(&self) -> LocationId { - self.location.id() - } - - fn flow_state(&self) -> &FlowState { - self.location.flow_state() - } -} - -impl<'a, T, N: Location<'a>> DeferTick for Optional> { - fn defer_tick(self) -> Self { - Optional::defer_tick(self) - } -} - -impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> { - type Location = Tick; - - fn create_source(ident: syn::Ident, location: Tick) -> Self { - let location_id = location.id(); - Optional::new( - location, - HfPlusNode::CycleSource { - ident, - location_kind: location_id, - }, - ) - } -} - -impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Optional> { - fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(self.ir_node.into_inner()), - }); - } -} - -impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Optional> { - type Location = Tick; - - fn create_source(ident: syn::Ident, location: Tick) -> Self { - let location_id = location.id(); - Optional::new( - location, - HfPlusNode::CycleSource { - ident, - location_kind: location_id, - }, - ) - } -} - -impl<'a, T, N: Location<'a>> CycleComplete<'a, ForwardRef> for Optional> { - fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(self.ir_node.into_inner()), - }); - } -} - -impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Optional { - type Location = N; - - fn create_source(ident: syn::Ident, location: N) -> Self { - let location_id = location.id(); - Optional::new( - location, - HfPlusNode::Persist(Box::new(HfPlusNode::CycleSource { - ident, - location_kind: location_id, - })), - ) - } -} - -impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Optional { - fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - }); - } -} - -impl<'a, T, W, N: Location<'a>> From> for Optional { - fn from(singleton: Singleton) -> Self { - Optional::some(singleton) - } -} - -impl<'a, T: Clone, W, N: Location<'a>> Clone for Optional { - fn clone(&self) -> Self { - if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { - let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); - *self.ir_node.borrow_mut() = HfPlusNode::Tee { - inner: TeeNode(Rc::new(RefCell::new(orig_ir_node))), - }; - } - - if let HfPlusNode::Tee { inner } = self.ir_node.borrow().deref() { - Optional { - location: self.location.clone(), - ir_node: HfPlusNode::Tee { - inner: TeeNode(inner.0.clone()), - } - .into(), - _phantom: PhantomData, - } - } else { - unreachable!() - } - } -} - -impl<'a, T, W, N: Location<'a>> Optional { - // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream - pub fn into_stream(self) -> Stream { - if N::is_top_level() { - panic!("Converting an optional to a stream is not yet supported at the top level"); - } - - Stream::new(self.location, self.ir_node.into_inner()) - } - - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { - Optional::new( - self.location, - HfPlusNode::Map { - f: f.splice_fn1().into(), - input: Box::new(self.ir_node.into_inner()), - }, - ) - } - - pub fn flat_map, F: Fn(T) -> I + 'a>( - self, - f: impl IntoQuotedMut<'a, F>, - ) -> Stream { - Stream::new( - self.location, - HfPlusNode::FlatMap { - f: f.splice_fn1().into(), - input: Box::new(self.ir_node.into_inner()), - }, - ) - } - - pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { - Optional::new( - self.location, - HfPlusNode::Filter { - f: f.splice_fn1_borrow().into(), - input: Box::new(self.ir_node.into_inner()), - }, - ) - } - - pub fn filter_map Option + 'a>( - self, - f: impl IntoQuotedMut<'a, F>, - ) -> Optional { - Optional::new( - self.location, - HfPlusNode::FilterMap { - f: f.splice_fn1().into(), - input: Box::new(self.ir_node.into_inner()), - }, - ) - } -} - -impl<'a, T, N: Location<'a>> Optional> { - pub fn cross_singleton( - self, - other: impl Into>>, - ) -> Optional<(T, O), Bounded, Tick> - where - O: Clone, - { - let other: Optional> = other.into(); - check_matching_location(&self.location, &other.location); - - Optional::new( - self.location, - HfPlusNode::CrossSingleton( - Box::new(self.ir_node.into_inner()), - Box::new(other.ir_node.into_inner()), - ), - ) - } - - pub fn continue_if( - self, - signal: Optional>, - ) -> Optional> { - self.cross_singleton(signal.map(q!(|_u| ()))) - .map(q!(|(d, _signal)| d)) - } - - pub fn then(self, value: Singleton>) -> Optional> { - value.continue_if(self) - } - - pub fn continue_unless( - self, - other: Optional>, - ) -> Optional> { - self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) - } - - pub fn union(self, other: Optional>) -> Optional> { - check_matching_location(&self.location, &other.location); - - Optional::new( - self.location, - HfPlusNode::Union( - Box::new(self.ir_node.into_inner()), - Box::new(other.ir_node.into_inner()), - ), - ) - } - - pub fn unwrap_or( - self, - other: Singleton>, - ) -> Singleton> { - check_matching_location(&self.location, &other.location); - - Singleton::new( - self.location, - HfPlusNode::Union( - Box::new(self.ir_node.into_inner()), - Box::new(other.ir_node.into_inner()), - ), - ) - } - - pub fn into_singleton(self) -> Singleton, Bounded, Tick> - where - T: Clone, - N: NoTick, - { - let none_singleton = self.location.outer().singleton_each_tick(q!(None)); - self.map(q!(|v| Some(v))).unwrap_or(none_singleton) - } -} - -impl<'a, T, N: Location<'a>> Optional> { +impl<'a, T, N: Location<'a>> Singleton> { pub fn all_ticks(self) -> Stream { Stream::new( self.location.outer().clone(), @@ -655,15 +251,15 @@ impl<'a, T, N: Location<'a>> Optional> { ) } - pub fn latest(self) -> Optional { - Optional::new( + pub fn latest(self) -> Singleton { + Singleton::new( self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn defer_tick(self) -> Optional> { - Optional::new( + pub fn defer_tick(self) -> Singleton> { + Singleton::new( self.location, HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) @@ -684,62 +280,46 @@ impl<'a, T, N: Location<'a>> Optional> { } } -impl<'a, T, B, N: Location<'a> + NoTick> Optional { - pub fn latest_tick(self) -> Optional> { - Optional::new( - self.location.nest(), - HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), - ) - } +pub trait ZipResult<'a, Other> { + type Out; + type Location; - pub fn tick_samples(self) -> Stream { - self.latest_tick().all_ticks() - } + fn other_location(other: &Other) -> Self::Location; + fn other_ir_node(other: Other) -> HfPlusNode; - pub fn sample_every( - self, - interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { - let samples = self.location.source_interval(interval).tick_batch(); + fn make(location: Self::Location, ir_node: HfPlusNode) -> Self::Out; +} - self.latest_tick() - .continue_if(samples.first()) - .latest() - .tick_samples() - } +impl<'a, T, U: Clone, W, N: Location<'a>> ZipResult<'a, Singleton> for Singleton { + type Out = Singleton<(T, U), W, N>; + type Location = N; - pub fn unwrap_or( - self, - other: impl Into>, - ) -> Singleton { - let other = other.into(); - check_matching_location(&self.location, &other.location); + fn other_location(other: &Singleton) -> N { + other.location.clone() + } - self.latest_tick().unwrap_or(other.latest_tick()).latest() + fn other_ir_node(other: Singleton) -> HfPlusNode { + other.ir_node.into_inner() } - pub fn into_singleton(self) -> Singleton, Unbounded, N> - where - T: Clone, - { - let none_singleton = self.location.singleton(q!(None)); - self.map(q!(|v| Some(v))).unwrap_or(none_singleton) + fn make(location: N, ir_node: HfPlusNode) -> Self::Out { + Singleton::new(location, ir_node) } } -impl<'a, T, N: Location<'a> + NoTick> Optional { - pub fn cross_singleton( - self, - other: impl Into>, - ) -> Optional<(T, O), Unbounded, N> - where - O: Clone, - { - let other: Optional = other.into(); - check_matching_location(&self.location, &other.location); +impl<'a, T, U: Clone, W, N: Location<'a>> ZipResult<'a, Optional> for Singleton { + type Out = Optional<(T, U), W, N>; + type Location = N; - self.latest_tick() - .cross_singleton(other.latest_tick()) - .latest() + fn other_location(other: &Optional) -> N { + other.location.clone() + } + + fn other_ir_node(other: Optional) -> HfPlusNode { + other.ir_node.into_inner() + } + + fn make(location: N, ir_node: HfPlusNode) -> Self::Out { + Optional::new(location, ir_node) } } diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 32a5074cc8ff..170046d6fdbb 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -238,7 +238,7 @@ fn p_ballot_calc<'a>( let p_new_ballot_num = p_received_max_ballot .clone() - .cross_singleton(p_ballot_num.clone()) + .zip(p_ballot_num.clone()) .map(q!(move |(received_max_ballot, ballot_num)| { if received_max_ballot > (Ballot { @@ -255,7 +255,7 @@ fn p_ballot_calc<'a>( let p_has_largest_ballot = p_received_max_ballot .clone() - .cross_singleton(p_ballot_num.clone()) + .zip(p_ballot_num.clone()) .filter(q!( move |(received_max_ballot, ballot_num)| *received_max_ballot <= Ballot { @@ -608,7 +608,7 @@ fn p_p2a<'a, P: PaxosPayload>( let p_num_payloads = p_indexed_payloads.count(); let p_next_slot_after_sending_payloads = p_num_payloads .clone() - .cross_singleton(p_next_slot.clone()) + .zip(p_next_slot.clone()) .map(q!(|(num_payloads, next_slot)| next_slot + num_payloads)); let p_new_next_slot = p_next_slot_after_reconciling_p1bs diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index aa3d3db6c9f1..0af5e6ce350a 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -242,7 +242,7 @@ fn bench_client<'a>( ); c_latencies - .cross_singleton(c_throughput) + .zip(c_throughput) .latest_tick() .continue_if(c_stats_output_timer) .all_ticks() diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 8cdd3c7db5de..320643a4befb 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -162,18 +162,19 @@ pub fn replica<'a, K: KvKey, V: KvValue>( let (r_checkpointed_seqs_complete_cycle, r_checkpointed_seqs) = replicas.tick_cycle::>(); let r_max_checkpointed_seq = r_checkpointed_seqs.persist().max().into_singleton(); - let r_checkpoint_seq_new = r_max_checkpointed_seq - .cross_singleton(r_new_highest_seq) - .filter_map(q!( - move |(max_checkpointed_seq, new_highest_seq)| if max_checkpointed_seq - .map(|m| new_highest_seq - m >= checkpoint_frequency) - .unwrap_or(true) - { - Some(new_highest_seq) - } else { - None - } - )); + let r_checkpoint_seq_new = + r_max_checkpointed_seq + .zip(r_new_highest_seq) + .filter_map(q!( + move |(max_checkpointed_seq, new_highest_seq)| if max_checkpointed_seq + .map(|m| new_highest_seq - m >= checkpoint_frequency) + .unwrap_or(true) + { + Some(new_highest_seq) + } else { + None + } + )); r_checkpointed_seqs_complete_cycle.complete_next_tick(r_checkpoint_seq_new.clone()); // Tell clients that the payload has been committed. All ReplicaPayloads contain the client's machine ID (to string) as value. diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap index 370f07676be9..eb4e28cb096b 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap @@ -6,7 +6,7 @@ expression: built.ir() ForEach { f: stageleft :: runtime_support :: fn1_type_hint :: < (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) | { println ! ("pi: {} ({} trials)" , 4.0 * inside as f64 / total as f64 , total) ; } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((u64 , u64) , ()) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((u64 , u64) , ()) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u64 , u64) , (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) , (inside_batch , total_batch) | { * inside += inside_batch ; * total += total_batch ; } }), @@ -70,7 +70,7 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Source { source: Stream( { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap index 73ad09b5e521..debe85fe030f 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap @@ -7,9 +7,9 @@ expression: ir.surface_syntax_string() 3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); 4v1 = reduce :: < 'static > (stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u64 , u64) , (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) , (inside_batch , total_batch) | { * inside += inside_batch ; * total += total_batch ; } })); 5v1 = source_stream ({ use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }); -6v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () })); +6v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () })); 7v1 = cross_singleton (); -8v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < ((u64 , u64) , ()) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d })); +8v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < ((u64 , u64) , ()) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d })); 9v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) | { println ! ("pi: {} ({} trials)" , 4.0 * inside as f64 / total as f64 , total) ; } })); 1v1 -> 2v1; diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index a96b377e89c1..2b9f3f5cf572 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -160,7 +160,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ballot_num | Ballot { num : ballot_num , proposer_id : p_id } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), @@ -184,7 +184,7 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Source { source: Stream( { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, @@ -294,7 +294,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), @@ -327,7 +327,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Source { source: Stream( { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, @@ -403,7 +403,7 @@ expression: built.ir() ), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), @@ -434,7 +434,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Tee { inner: : Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), @@ -462,7 +462,7 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( Union( Map { @@ -600,7 +600,7 @@ expression: built.ir() }, ), Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Tee { inner: , }, @@ -750,15 +750,15 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { inner: , }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | c | * c == 0 }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), @@ -1060,7 +1060,7 @@ expression: built.ir() }, Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < usize > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | v | Some (v) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < usize > > ({ use hydroflow_plus :: __staged :: optional :: * ; | v | Some (v) }), input: CycleSource { ident: Ident { sym: cycle_2, @@ -1073,7 +1073,7 @@ expression: built.ir() Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use hydroflow_plus :: __staged :: singleton :: * ; None } ; [e] }, + [:: std :: option :: Option :: None], ), location_kind: Cluster( 3, @@ -1141,7 +1141,7 @@ expression: built.ir() input: CrossSingleton( Union( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < usize > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | v | Some (v) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < usize > > ({ use hydroflow_plus :: __staged :: optional :: * ; | v | Some (v) }), input: Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( @@ -1159,7 +1159,7 @@ expression: built.ir() Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use hydroflow_plus :: __staged :: singleton :: * ; None } ; [e] }, + [:: std :: option :: Option :: None], ), location_kind: Cluster( 3, From 2faffdbf2cc886da22e496df64f46aefa380766c Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Tue, 5 Nov 2024 18:54:13 -0800 Subject: [PATCH 28/74] fix(stageleft): properly handle `crate::` imports (#1527) --- hydroflow_plus/src/lib.rs | 1 - hydroflow_plus/src/location/cluster.rs | 3 +-- hydroflow_plus/src/location/mod.rs | 5 +++-- hydroflow_plus/src/stream.rs | 3 +-- stageleft_tool/src/lib.rs | 14 +++++++++++++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 0689ce2c04ab..46539fa81d1a 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -34,7 +34,6 @@ pub mod deploy; pub use deploy::{ClusterSpec, Deploy, ProcessSpec}; pub mod cycle; -pub use cycle::HfForwardRef; pub mod builder; pub use builder::FlowBuilder; diff --git a/hydroflow_plus/src/location/cluster.rs b/hydroflow_plus/src/location/cluster.rs index 3c7de35a841a..441fd7f8f1bd 100644 --- a/hydroflow_plus/src/location/cluster.rs +++ b/hydroflow_plus/src/location/cluster.rs @@ -8,10 +8,9 @@ use serde::{Deserialize, Serialize}; use stageleft::runtime_support::FreeVariable; use stageleft::{quote_type, Quoted}; -// TODO(shadaj): have to use super due to stageleft limitations -use super::super::staging_util::get_this_crate; use super::{Location, LocationId}; use crate::builder::FlowState; +use crate::staging_util::get_this_crate; pub struct Cluster<'a, C> { pub(crate) id: usize, diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index 279b6d2a9768..8bd3f0ea5a32 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -9,10 +9,11 @@ use stageleft::{q, Quoted}; use super::builder::FlowState; use crate::cycle::{ - CycleCollection, CycleCollectionWithInitial, DeferTick, ForwardRef, HfCycle, TickCycle, + CycleCollection, CycleCollectionWithInitial, DeferTick, ForwardRef, HfCycle, HfForwardRef, + TickCycle, }; use crate::ir::{HfPlusNode, HfPlusSource}; -use crate::{Bounded, HfForwardRef, Optional, Singleton, Stream, Unbounded}; +use crate::{Bounded, Optional, Singleton, Stream, Unbounded}; pub mod external_process; pub use external_process::ExternalProcess; diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index fdaf9150fa87..c2c791558a5e 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -12,8 +12,6 @@ use serde::Serialize; use stageleft::{q, IntoQuotedMut, Quoted}; use syn::parse_quote; -// TODO(shadaj): have to uses super due to stageleft limitations -use super::staging_util::get_this_crate; use crate::builder::FlowState; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, TeeNode}; @@ -22,6 +20,7 @@ use crate::location::external_process::{ExternalBincodeStream, ExternalBytesPort use crate::location::{ check_matching_location, CanSend, ExternalProcess, Location, LocationId, NoTick, Tick, }; +use crate::staging_util::get_this_crate; use crate::{Cluster, ClusterId, Optional, Process, Singleton}; /// Marks the stream as being unbounded, which means that it is not diff --git a/stageleft_tool/src/lib.rs b/stageleft_tool/src/lib.rs index 22cdb6ef0f34..ea239830bfb3 100644 --- a/stageleft_tool/src/lib.rs +++ b/stageleft_tool/src/lib.rs @@ -4,9 +4,9 @@ use std::{env, fs}; use proc_macro2::Span; use quote::ToTokens; -use syn::parse_quote; use syn::visit::Visit; use syn::visit_mut::VisitMut; +use syn::{parse_quote, UsePath}; struct GenMacroVistor { exported_macros: BTreeSet<(String, String)>, @@ -144,6 +144,18 @@ impl VisitMut for GenFinalPubVistor { syn::visit_mut::visit_item_use_mut(self, i); } + fn visit_use_path_mut(&mut self, i: &mut UsePath) { + if i.ident == "crate" { + i.tree = Box::new(syn::UseTree::Path(UsePath { + ident: parse_quote!(__staged), + colon2_token: Default::default(), + tree: i.tree.clone(), + })); + } + + syn::visit_mut::visit_use_path_mut(self, i); + } + fn visit_item_mod_mut(&mut self, i: &mut syn::ItemMod) { let is_runtime_or_test = i.attrs.iter().any(|a| { a.path().to_token_stream().to_string() == "stageleft :: runtime" From e796200743f2cc2da5a0e91c492f016ca98008e8 Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Wed, 6 Nov 2024 09:36:45 -0800 Subject: [PATCH 29/74] fix(hydroflow_lang)!: fix #1401 `lattice_bimorphism()` double-emit, add docs (#1522) Fixes the issue by combining the all values generated per subgraph execution into one, which effectively de-duplicates the values. Adds basic docs. BREAKING CHANGE: changes exact behavior of `lattice_bimorphism()`, instead of emitting multiple items, will emit a single item which is the merge of what would've been all the old outputs. `Output: Merge` is a new required bound. --- ...phism__cartesian_product@graphvis_dot.snap | 30 +++----- ...m__cartesian_product@graphvis_mermaid.snap | 30 +++----- ...__cartesian_product_1401@graphvis_dot.snap | 69 +++++++++++++++++++ ...rtesian_product_1401@graphvis_mermaid.snap | 59 ++++++++++++++++ ...esian_product_tick_state@graphvis_dot.snap | 35 +++------- ...n_product_tick_state@graphvis_mermaid.snap | 35 ++++------ ...lattice_bimorphism__join@graphvis_dot.snap | 31 +++------ ...ice_bimorphism__join@graphvis_mermaid.snap | 31 +++------ hydroflow/tests/surface_lattice_bimorphism.rs | 35 ++++++++-- .../src/graph/ops/lattice_bimorphism.rs | 45 ++++++++++-- 10 files changed, 254 insertions(+), 146 deletions(-) create mode 100644 hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_1401@graphvis_dot.snap create mode 100644 hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_1401@graphvis_mermaid.snap diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_dot.snap index dae12d839db5..9e81d0e6973d 100644 --- a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_dot.snap +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_dot.snap @@ -12,22 +12,18 @@ digraph { n5v1 [label="(n5v1) map(SetUnionSingletonSet::new_from)", shape=invhouse, fillcolor="#88aaff"] n6v1 [label="(n6v1) state::<'static, SetUnionHashSet>()", shape=invhouse, fillcolor="#88aaff"] n7v1 [label="(n7v1) lattice_bimorphism(CartesianProductBimorphism::>::default(), lhs, rhs)", shape=invhouse, fillcolor="#88aaff"] - n8v1 [label="(n8v1) lattice_reduce()", shape=invhouse, fillcolor="#88aaff"] - n9v1 [label="(n9v1) for_each(|x| out_send.send(x).unwrap())", shape=house, fillcolor="#ffff88"] + n8v1 [label="(n8v1) for_each(|x| out_send.send(x).unwrap())", shape=house, fillcolor="#ffff88"] + n9v1 [label="(n9v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n10v1 [label="(n10v1) handoff", shape=parallelogram, fillcolor="#ddddff"] - n11v1 [label="(n11v1) handoff", shape=parallelogram, fillcolor="#ddddff"] - n12v1 [label="(n12v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n2v1 -> n3v1 n1v1 -> n2v1 n5v1 -> n6v1 n4v1 -> n5v1 - n3v1 -> n10v1 - n6v1 -> n11v1 - n8v1 -> n9v1 - n7v1 -> n12v1 - n10v1 -> n7v1 [label="0"] - n11v1 -> n7v1 [label="1"] - n12v1 -> n8v1 [color=red] + n3v1 -> n9v1 + n6v1 -> n10v1 + n7v1 -> n8v1 + n9v1 -> n7v1 [label="0"] + n10v1 -> n7v1 [label="1"] n3v1 -> n7v1 [color=red] n6v1 -> n7v1 [color=red] subgraph "cluster n1v1" { @@ -63,21 +59,11 @@ digraph { style=filled label = "sg_3v1\nstratum 1" n7v1 + n8v1 subgraph "cluster_sg_3v1_var_my_join" { label="var my_join" n7v1 - } - } - subgraph "cluster n4v1" { - fillcolor="#dddddd" - style=filled - label = "sg_4v1\nstratum 1" - n8v1 - n9v1 - subgraph "cluster_sg_4v1_var_my_join" { - label="var my_join" n8v1 - n9v1 } } } diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_mermaid.snap index 7b6a31512589..60c4b26b33f7 100644 --- a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_mermaid.snap +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product@graphvis_mermaid.snap @@ -15,24 +15,20 @@ linkStyle default stroke:#aaa 5v1[\"(5v1) map(SetUnionSingletonSet::new_from)"/]:::pullClass 6v1[\"(6v1) state::<'static, SetUnionHashSet<u32>>()"/]:::pullClass 7v1[\"(7v1) lattice_bimorphism(CartesianProductBimorphism::<HashSet<_>>::default(), lhs, rhs)"/]:::pullClass -8v1[\"(8v1) lattice_reduce()"/]:::pullClass -9v1[/"(9v1) for_each(|x| out_send.send(x).unwrap())"\]:::pushClass +8v1[/"(8v1) for_each(|x| out_send.send(x).unwrap())"\]:::pushClass +9v1["(9v1) handoff"]:::otherClass 10v1["(10v1) handoff"]:::otherClass -11v1["(11v1) handoff"]:::otherClass -12v1["(12v1) handoff"]:::otherClass 2v1-->3v1 1v1-->2v1 5v1-->6v1 4v1-->5v1 -3v1-->10v1 -6v1-->11v1 -8v1-->9v1 -7v1-->12v1 -10v1-->|0|7v1 -11v1-->|1|7v1 -12v1-->8v1; linkStyle 10 stroke:#060 -3v1--x7v1; linkStyle 11 stroke:red -6v1--x7v1; linkStyle 12 stroke:red +3v1-->9v1 +6v1-->10v1 +7v1-->8v1 +9v1-->|0|7v1 +10v1-->|1|7v1 +3v1--x7v1; linkStyle 9 stroke:red +6v1--x7v1; linkStyle 10 stroke:red subgraph sg_1v1 ["sg_1v1 stratum 0"] 1v1 2v1 @@ -55,15 +51,9 @@ subgraph sg_2v1 ["sg_2v1 stratum 0"] end subgraph sg_3v1 ["sg_3v1 stratum 1"] 7v1 + 8v1 subgraph sg_3v1_var_my_join ["var my_join"] 7v1 - end -end -subgraph sg_4v1 ["sg_4v1 stratum 1"] - 8v1 - 9v1 - subgraph sg_4v1_var_my_join ["var my_join"] 8v1 - 9v1 end end diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_1401@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_1401@graphvis_dot.snap new file mode 100644 index 000000000000..a1f6888a2b86 --- /dev/null +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_1401@graphvis_dot.snap @@ -0,0 +1,69 @@ +--- +source: hydroflow/tests/surface_lattice_bimorphism.rs +expression: "df.meta_graph().unwrap().to_dot(& Default :: default())" +--- +digraph { + node [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace", style=filled]; + edge [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace"]; + n1v1 [label="(n1v1) source_iter(0..1)", shape=invhouse, fillcolor="#88aaff"] + n2v1 [label="(n2v1) map(SetUnionSingletonSet::new_from)", shape=invhouse, fillcolor="#88aaff"] + n3v1 [label="(n3v1) state::<'static, SetUnionHashSet>()", shape=invhouse, fillcolor="#88aaff"] + n4v1 [label="(n4v1) source_iter(1..2)", shape=invhouse, fillcolor="#88aaff"] + n5v1 [label="(n5v1) map(SetUnionSingletonSet::new_from)", shape=invhouse, fillcolor="#88aaff"] + n6v1 [label="(n6v1) state::<'static, SetUnionHashSet>()", shape=invhouse, fillcolor="#88aaff"] + n7v1 [label="(n7v1) lattice_bimorphism(CartesianProductBimorphism::>::default(), lhs, rhs)", shape=invhouse, fillcolor="#88aaff"] + n8v1 [label="(n8v1) for_each(|x| out_send.send(x).unwrap())", shape=house, fillcolor="#ffff88"] + n9v1 [label="(n9v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n10v1 [label="(n10v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n2v1 -> n3v1 + n1v1 -> n2v1 + n5v1 -> n6v1 + n4v1 -> n5v1 + n3v1 -> n9v1 + n6v1 -> n10v1 + n7v1 -> n8v1 + n9v1 -> n7v1 [label="0"] + n10v1 -> n7v1 [label="1"] + n3v1 -> n7v1 [color=red] + n6v1 -> n7v1 [color=red] + subgraph "cluster n1v1" { + fillcolor="#dddddd" + style=filled + label = "sg_1v1\nstratum 0" + n1v1 + n2v1 + n3v1 + subgraph "cluster_sg_1v1_var_lhs" { + label="var lhs" + n1v1 + n2v1 + n3v1 + } + } + subgraph "cluster n2v1" { + fillcolor="#dddddd" + style=filled + label = "sg_2v1\nstratum 0" + n4v1 + n5v1 + n6v1 + subgraph "cluster_sg_2v1_var_rhs" { + label="var rhs" + n4v1 + n5v1 + n6v1 + } + } + subgraph "cluster n3v1" { + fillcolor="#dddddd" + style=filled + label = "sg_3v1\nstratum 1" + n7v1 + n8v1 + subgraph "cluster_sg_3v1_var_my_join" { + label="var my_join" + n7v1 + n8v1 + } + } +} diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_1401@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_1401@graphvis_mermaid.snap new file mode 100644 index 000000000000..08e6a806fc42 --- /dev/null +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_1401@graphvis_mermaid.snap @@ -0,0 +1,59 @@ +--- +source: hydroflow/tests/surface_lattice_bimorphism.rs +expression: "df.meta_graph().unwrap().to_mermaid(& Default :: default())" +--- +%%{init:{'theme':'base','themeVariables':{'clusterBkg':'#ddd','clusterBorder':'#888'}}}%% +flowchart TD +classDef pullClass fill:#8af,stroke:#000,text-align:left,white-space:pre +classDef pushClass fill:#ff8,stroke:#000,text-align:left,white-space:pre +classDef otherClass fill:#fdc,stroke:#000,text-align:left,white-space:pre +linkStyle default stroke:#aaa +1v1[\"(1v1) source_iter(0..1)"/]:::pullClass +2v1[\"(2v1) map(SetUnionSingletonSet::new_from)"/]:::pullClass +3v1[\"(3v1) state::<'static, SetUnionHashSet<u32>>()"/]:::pullClass +4v1[\"(4v1) source_iter(1..2)"/]:::pullClass +5v1[\"(5v1) map(SetUnionSingletonSet::new_from)"/]:::pullClass +6v1[\"(6v1) state::<'static, SetUnionHashSet<u32>>()"/]:::pullClass +7v1[\"(7v1) lattice_bimorphism(CartesianProductBimorphism::<HashSet<_>>::default(), lhs, rhs)"/]:::pullClass +8v1[/"(8v1) for_each(|x| out_send.send(x).unwrap())"\]:::pushClass +9v1["(9v1) handoff"]:::otherClass +10v1["(10v1) handoff"]:::otherClass +2v1-->3v1 +1v1-->2v1 +5v1-->6v1 +4v1-->5v1 +3v1-->9v1 +6v1-->10v1 +7v1-->8v1 +9v1-->|0|7v1 +10v1-->|1|7v1 +3v1--x7v1; linkStyle 9 stroke:red +6v1--x7v1; linkStyle 10 stroke:red +subgraph sg_1v1 ["sg_1v1 stratum 0"] + 1v1 + 2v1 + 3v1 + subgraph sg_1v1_var_lhs ["var lhs"] + 1v1 + 2v1 + 3v1 + end +end +subgraph sg_2v1 ["sg_2v1 stratum 0"] + 4v1 + 5v1 + 6v1 + subgraph sg_2v1_var_rhs ["var rhs"] + 4v1 + 5v1 + 6v1 + end +end +subgraph sg_3v1 ["sg_3v1 stratum 1"] + 7v1 + 8v1 + subgraph sg_3v1_var_my_join ["var my_join"] + 7v1 + 8v1 + end +end diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_tick_state@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_tick_state@graphvis_dot.snap index 8e00b09a5bba..239b30948c41 100644 --- a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_tick_state@graphvis_dot.snap +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_tick_state@graphvis_dot.snap @@ -12,24 +12,20 @@ digraph { n5v1 [label="(n5v1) map(SetUnionSingletonSet::new_from)", shape=invhouse, fillcolor="#88aaff"] n6v1 [label="(n6v1) state::<'tick, SetUnionHashSet>()", shape=invhouse, fillcolor="#88aaff"] n7v1 [label="(n7v1) lattice_bimorphism(CartesianProductBimorphism::>::default(), lhs, rhs)", shape=invhouse, fillcolor="#88aaff"] - n8v1 [label="(n8v1) lattice_reduce()", shape=invhouse, fillcolor="#88aaff"] - n9v1 [label="(n9v1) inspect(|x| println!(\"{:?}: {:?}\", context.current_tick(), x))", shape=invhouse, fillcolor="#88aaff"] - n10v1 [label="(n10v1) for_each(|x| out_send.send(x).unwrap())", shape=house, fillcolor="#ffff88"] + n8v1 [label="(n8v1) inspect(|x| println!(\"{:?}: {:?}\", context.current_tick(), x))", shape=invhouse, fillcolor="#88aaff"] + n9v1 [label="(n9v1) for_each(|x| out_send.send(x).unwrap())", shape=house, fillcolor="#ffff88"] + n10v1 [label="(n10v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n11v1 [label="(n11v1) handoff", shape=parallelogram, fillcolor="#ddddff"] - n12v1 [label="(n12v1) handoff", shape=parallelogram, fillcolor="#ddddff"] - n13v1 [label="(n13v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n2v1 -> n3v1 n1v1 -> n2v1 n5v1 -> n6v1 n4v1 -> n5v1 - n3v1 -> n11v1 [label="items"] - n6v1 -> n12v1 [label="items"] - n9v1 -> n10v1 + n3v1 -> n10v1 [label="items"] + n6v1 -> n11v1 [label="items"] n8v1 -> n9v1 - n7v1 -> n13v1 - n11v1 -> n7v1 [label="0"] - n12v1 -> n7v1 [label="1"] - n13v1 -> n8v1 [color=red] + n7v1 -> n8v1 + n10v1 -> n7v1 [label="0"] + n11v1 -> n7v1 [label="1"] n3v1 -> n7v1 [color=red] n6v1 -> n7v1 [color=red] subgraph "cluster n1v1" { @@ -65,24 +61,13 @@ digraph { style=filled label = "sg_3v1\nstratum 1" n7v1 - subgraph "cluster_sg_3v1_var_my_join" { - label="var my_join" - n7v1 - } - } - subgraph "cluster n4v1" { - fillcolor="#dddddd" - style=filled - label = "sg_4v1\nstratum 1" n8v1 n9v1 - n10v1 - subgraph "cluster_sg_4v1_var_my_join" { + subgraph "cluster_sg_3v1_var_my_join" { label="var my_join" + n7v1 n8v1 n9v1 - n10v1 } } } - diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_tick_state@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_tick_state@graphvis_mermaid.snap index 639b4d963c73..a71d84cc9f12 100644 --- a/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_tick_state@graphvis_mermaid.snap +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__cartesian_product_tick_state@graphvis_mermaid.snap @@ -15,26 +15,22 @@ linkStyle default stroke:#aaa 5v1[\"(5v1) map(SetUnionSingletonSet::new_from)"/]:::pullClass 6v1[\"(6v1) state::<'tick, SetUnionHashSet<u32>>()"/]:::pullClass 7v1[\"(7v1) lattice_bimorphism(CartesianProductBimorphism::<HashSet<_>>::default(), lhs, rhs)"/]:::pullClass -8v1[\"(8v1) lattice_reduce()"/]:::pullClass -9v1[\"(9v1) inspect(|x| println!("{:?}: {:?}", context.current_tick(), x))"/]:::pullClass -10v1[/"(10v1) for_each(|x| out_send.send(x).unwrap())"\]:::pushClass +8v1[\"(8v1) inspect(|x| println!("{:?}: {:?}", context.current_tick(), x))"/]:::pullClass +9v1[/"(9v1) for_each(|x| out_send.send(x).unwrap())"\]:::pushClass +10v1["(10v1) handoff"]:::otherClass 11v1["(11v1) handoff"]:::otherClass -12v1["(12v1) handoff"]:::otherClass -13v1["(13v1) handoff"]:::otherClass 2v1-->3v1 1v1-->2v1 5v1-->6v1 4v1-->5v1 -3v1-->|items|11v1 -6v1-->|items|12v1 -9v1-->10v1 +3v1-->|items|10v1 +6v1-->|items|11v1 8v1-->9v1 -7v1-->13v1 -11v1-->|0|7v1 -12v1-->|1|7v1 -13v1-->8v1; linkStyle 11 stroke:#060 -3v1--x7v1; linkStyle 12 stroke:red -6v1--x7v1; linkStyle 13 stroke:red +7v1-->8v1 +10v1-->|0|7v1 +11v1-->|1|7v1 +3v1--x7v1; linkStyle 10 stroke:red +6v1--x7v1; linkStyle 11 stroke:red subgraph sg_1v1 ["sg_1v1 stratum 0"] 1v1 2v1 @@ -57,18 +53,11 @@ subgraph sg_2v1 ["sg_2v1 stratum 0"] end subgraph sg_3v1 ["sg_3v1 stratum 1"] 7v1 - subgraph sg_3v1_var_my_join ["var my_join"] - 7v1 - end -end -subgraph sg_4v1 ["sg_4v1 stratum 1"] 8v1 9v1 - 10v1 - subgraph sg_4v1_var_my_join ["var my_join"] + subgraph sg_3v1_var_my_join ["var my_join"] + 7v1 8v1 9v1 - 10v1 end end - diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__join@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__join@graphvis_dot.snap index 4f4c53e737d3..11eb8a895be1 100644 --- a/hydroflow/tests/snapshots/surface_lattice_bimorphism__join@graphvis_dot.snap +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__join@graphvis_dot.snap @@ -12,22 +12,18 @@ digraph { n5v1 [label="(n5v1) map(|(k, v)| MapUnionSingletonMap::new_from((k, SetUnionSingletonSet::new_from(v))))", shape=invhouse, fillcolor="#88aaff"] n6v1 [label="(n6v1) state::<'static, MapUnionHashMap>>()", shape=invhouse, fillcolor="#88aaff"] n7v1 [label="(n7v1) lattice_bimorphism(\l KeyedBimorphism::<\l HashMap<_, _>,\l _,\l >::new(CartesianProductBimorphism::>::default()),\l lhs,\l rhs,\l)\l", shape=invhouse, fillcolor="#88aaff"] - n8v1 [label="(n8v1) lattice_reduce()", shape=invhouse, fillcolor="#88aaff"] - n9v1 [label="(n9v1) for_each(|x| out_send.send(x).unwrap())", shape=house, fillcolor="#ffff88"] + n8v1 [label="(n8v1) for_each(|x| out_send.send(x).unwrap())", shape=house, fillcolor="#ffff88"] + n9v1 [label="(n9v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n10v1 [label="(n10v1) handoff", shape=parallelogram, fillcolor="#ddddff"] - n11v1 [label="(n11v1) handoff", shape=parallelogram, fillcolor="#ddddff"] - n12v1 [label="(n12v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n2v1 -> n3v1 n1v1 -> n2v1 n5v1 -> n6v1 n4v1 -> n5v1 - n3v1 -> n10v1 - n6v1 -> n11v1 - n8v1 -> n9v1 - n7v1 -> n12v1 - n10v1 -> n7v1 [label="0"] - n11v1 -> n7v1 [label="1"] - n12v1 -> n8v1 [color=red] + n3v1 -> n9v1 + n6v1 -> n10v1 + n7v1 -> n8v1 + n9v1 -> n7v1 [label="0"] + n10v1 -> n7v1 [label="1"] n3v1 -> n7v1 [color=red] n6v1 -> n7v1 [color=red] subgraph "cluster n1v1" { @@ -63,22 +59,11 @@ digraph { style=filled label = "sg_3v1\nstratum 1" n7v1 + n8v1 subgraph "cluster_sg_3v1_var_my_join" { label="var my_join" n7v1 - } - } - subgraph "cluster n4v1" { - fillcolor="#dddddd" - style=filled - label = "sg_4v1\nstratum 1" - n8v1 - n9v1 - subgraph "cluster_sg_4v1_var_my_join" { - label="var my_join" n8v1 - n9v1 } } } - diff --git a/hydroflow/tests/snapshots/surface_lattice_bimorphism__join@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_lattice_bimorphism__join@graphvis_mermaid.snap index c271ea026819..f4a08363658f 100644 --- a/hydroflow/tests/snapshots/surface_lattice_bimorphism__join@graphvis_mermaid.snap +++ b/hydroflow/tests/snapshots/surface_lattice_bimorphism__join@graphvis_mermaid.snap @@ -15,24 +15,20 @@ linkStyle default stroke:#aaa 5v1[\"(5v1) map(|(k, v)| MapUnionSingletonMap::new_from((k, SetUnionSingletonSet::new_from(v))))"/]:::pullClass 6v1[\"(6v1) state::<'static, MapUnionHashMap<usize, SetUnionHashSet<usize>>>()"/]:::pullClass 7v1[\"

lattice_bimorphism(
KeyedBimorphism::<
HashMap<_, _>,
_,
>::new(CartesianProductBimorphism::<HashSet<_>>::default()),
lhs,
rhs,
)
"/]:::pullClass -8v1[\"(8v1) lattice_reduce()"/]:::pullClass -9v1[/"(9v1) for_each(|x| out_send.send(x).unwrap())"\]:::pushClass +8v1[/"(8v1) for_each(|x| out_send.send(x).unwrap())"\]:::pushClass +9v1["(9v1) handoff"]:::otherClass 10v1["(10v1) handoff"]:::otherClass -11v1["(11v1) handoff"]:::otherClass -12v1["(12v1) handoff"]:::otherClass 2v1-->3v1 1v1-->2v1 5v1-->6v1 4v1-->5v1 -3v1-->10v1 -6v1-->11v1 -8v1-->9v1 -7v1-->12v1 -10v1-->|0|7v1 -11v1-->|1|7v1 -12v1-->8v1; linkStyle 10 stroke:#060 -3v1--x7v1; linkStyle 11 stroke:red -6v1--x7v1; linkStyle 12 stroke:red +3v1-->9v1 +6v1-->10v1 +7v1-->8v1 +9v1-->|0|7v1 +10v1-->|1|7v1 +3v1--x7v1; linkStyle 9 stroke:red +6v1--x7v1; linkStyle 10 stroke:red subgraph sg_1v1 ["sg_1v1 stratum 0"] 1v1 2v1 @@ -55,16 +51,9 @@ subgraph sg_2v1 ["sg_2v1 stratum 0"] end subgraph sg_3v1 ["sg_3v1 stratum 1"] 7v1 + 8v1 subgraph sg_3v1_var_my_join ["var my_join"] 7v1 - end -end -subgraph sg_4v1 ["sg_4v1 stratum 1"] - 8v1 - 9v1 - subgraph sg_4v1_var_my_join ["var my_join"] 8v1 - 9v1 end end - diff --git a/hydroflow/tests/surface_lattice_bimorphism.rs b/hydroflow/tests/surface_lattice_bimorphism.rs index 30ff17165576..9909bc0832a5 100644 --- a/hydroflow/tests/surface_lattice_bimorphism.rs +++ b/hydroflow/tests/surface_lattice_bimorphism.rs @@ -27,7 +27,6 @@ pub fn test_cartesian_product() { rhs -> [1]my_join; my_join = lattice_bimorphism(CartesianProductBimorphism::>::default(), #lhs, #rhs) - -> lattice_reduce() -> for_each(|x| out_send.send(x).unwrap()); }; @@ -47,6 +46,33 @@ pub fn test_cartesian_product() { ); } +#[multiplatform_test(test, wasm, env_tracing)] +pub fn test_cartesian_product_1401() { + let (out_send, out_recv) = hydroflow::util::unbounded_channel::<_>(); + + let mut df = hydroflow_syntax! { + lhs = source_iter(0..1) + -> map(SetUnionSingletonSet::new_from) + -> state::<'static, SetUnionHashSet>(); + rhs = source_iter(1..2) + -> map(SetUnionSingletonSet::new_from) + -> state::<'static, SetUnionHashSet>(); + + lhs -> [0]my_join; + rhs -> [1]my_join; + + my_join = lattice_bimorphism(CartesianProductBimorphism::>::default(), #lhs, #rhs) + -> for_each(|x| out_send.send(x).unwrap()); + }; + assert_graphvis_snapshots!(df); + df.run_available(); + + assert_eq!( + &[SetUnionHashSet::new(HashSet::from_iter([(0, 1)]))], + &*collect_ready::, _>(out_recv) + ); +} + #[multiplatform_test] pub fn test_join() { let (out_send, out_recv) = hydroflow::util::unbounded_channel::<_>(); @@ -63,7 +89,6 @@ pub fn test_join() { rhs -> [1]my_join; my_join = lattice_bimorphism(KeyedBimorphism::, _>::new(CartesianProductBimorphism::>::default()), #lhs, #rhs) - -> lattice_reduce() -> for_each(|x| out_send.send(x).unwrap()); }; @@ -105,7 +130,6 @@ pub fn test_cartesian_product_tick_state() { rhs[items] -> [1]my_join; my_join = lattice_bimorphism(CartesianProductBimorphism::>::default(), #lhs, #rhs) - -> lattice_reduce() -> inspect(|x| println!("{:?}: {:?}", context.current_tick(), x)) -> for_each(|x| out_send.send(x).unwrap()); }; @@ -140,7 +164,7 @@ pub fn test_cartesian_product_tick_state() { ); } -#[test] +#[multiplatform_test] fn test_ght_join_bimorphism() { type MyGhtATrie = GhtType!(u32, u64, u16 => &'static str: VariadicHashSet); type MyGhtBTrie = GhtType!(u32, u64, u16 => &'static str: VariadicHashSet); @@ -176,13 +200,10 @@ fn test_ght_join_bimorphism() { my_join = lattice_bimorphism(MyBim::default(), #lhs, #rhs) - -> lattice_reduce() -> enumerate() -> inspect(|x| println!("{:?} {:#?}", context.current_tick(), x)) -> flat_map(|(_num, ght)| ght.recursive_iter().map(::clone_ref_var).collect::>()) -> null(); - // -> for_each(|x| println!("{:#?}\n", x)); }; - // hf.meta_graph().unwrap().open_mermaid(&Default::default()); hf.run_available(); } diff --git a/hydroflow_lang/src/graph/ops/lattice_bimorphism.rs b/hydroflow_lang/src/graph/ops/lattice_bimorphism.rs index 769af75c4fe5..a5ef7183ec7c 100644 --- a/hydroflow_lang/src/graph/ops/lattice_bimorphism.rs +++ b/hydroflow_lang/src/graph/ops/lattice_bimorphism.rs @@ -6,7 +6,40 @@ use super::{ RANGE_0, RANGE_1, }; -// TODO(mingwei): +/// An operator representing a [lattice bimorphism](https://hydro.run/docs/hydroflow/lattices_crate/lattice_math#lattice-bimorphism). +/// +/// > 2 input streams, of type `LhsItem` and `RhsItem`. +/// +/// > Three argument, one `LatticeBimorphism` function `Func`, an `LhsState` singleton reference, and an `RhsState` singleton reference. +/// +/// > 1 output stream of the output type of the `LatticeBimorphism` function. +/// +/// The function must be a lattice bimorphism for both `(LhsState, RhsItem)` and `(RhsState, LhsItem)`. +/// +/// ```hydroflow +/// use std::collections::HashSet; +/// use lattices::set_union::{CartesianProductBimorphism, SetUnionHashSet, SetUnionSingletonSet}; +/// +/// lhs = source_iter(0..3) +/// -> map(SetUnionSingletonSet::new_from) +/// -> state::<'static, SetUnionHashSet>(); +/// rhs = source_iter(3..5) +/// -> map(SetUnionSingletonSet::new_from) +/// -> state::<'static, SetUnionHashSet>(); +/// +/// lhs -> [0]my_join; +/// rhs -> [1]my_join; +/// +/// my_join = lattice_bimorphism(CartesianProductBimorphism::>::default(), #lhs, #rhs) +/// -> assert_eq([SetUnionHashSet::new(HashSet::from_iter([ +/// (0, 3), +/// (0, 4), +/// (1, 3), +/// (1, 4), +/// (2, 3), +/// (2, 4), +/// ]))]); +/// ``` pub const LATTICE_BIMORPHISM: OperatorConstraints = OperatorConstraints { name: "lattice_bimorphism", categories: &[OperatorCategory::MultiIn], @@ -53,7 +86,7 @@ pub const LATTICE_BIMORPHISM: OperatorConstraints = OperatorConstraints { lhs_state_handle: #root::scheduled::state::StateHandle<::std::cell::RefCell>, rhs_state_handle: #root::scheduled::state::StateHandle<::std::cell::RefCell>, context: &'a #root::scheduled::context::Context, - ) -> impl 'a + ::std::iter::Iterator + ) -> Option where Func: 'a + #root::lattices::LatticeBimorphism @@ -62,11 +95,12 @@ pub const LATTICE_BIMORPHISM: OperatorConstraints = OperatorConstraints { RhsIter: 'a + ::std::iter::Iterator, LhsState: 'static + ::std::clone::Clone, RhsState: 'static + ::std::clone::Clone, + Output: #root::lattices::Merge, { let lhs_state = context.state_ref(lhs_state_handle); let rhs_state = context.state_ref(rhs_state_handle); - ::std::iter::from_fn(move || { + let iter = ::std::iter::from_fn(move || { // Use `from_fn` instead of `chain` to dodge multiple ownership of `func`. if let Some(lhs_item) = lhs_iter.next() { Some(func.call(lhs_item, (*rhs_state.borrow()).clone())) @@ -74,7 +108,8 @@ pub const LATTICE_BIMORPHISM: OperatorConstraints = OperatorConstraints { let rhs_item = rhs_iter.next()?; Some(func.call((*lhs_state.borrow()).clone(), rhs_item)) } - }) + }); + iter.reduce(|a, b| #root::lattices::Merge::merge_owned(a, b)) } check_inputs( #func, @@ -83,7 +118,7 @@ pub const LATTICE_BIMORPHISM: OperatorConstraints = OperatorConstraints { #lhs_state_handle, #rhs_state_handle, &#context, - ) + ).into_iter() }; }; From 5b819a2dc6c507222a3e22d71efcde8b43cebad5 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 6 Nov 2024 11:47:03 -0800 Subject: [PATCH 30/74] refactor(hydroflow_plus): deduplicate some error messages and drop unused `Interval` IR node (#1540) --- hydroflow_plus/src/builder/mod.rs | 2 + hydroflow_plus/src/ir.rs | 7 ---- hydroflow_plus/src/optional.rs | 50 +++++++++++++++++-------- hydroflow_plus/src/singleton.rs | 34 +++++++++++------ hydroflow_plus/src/stream.rs | 62 +++++++++++++++++++++---------- 5 files changed, 102 insertions(+), 53 deletions(-) diff --git a/hydroflow_plus/src/builder/mod.rs b/hydroflow_plus/src/builder/mod.rs index e4c65a069a95..33101b554e67 100644 --- a/hydroflow_plus/src/builder/mod.rs +++ b/hydroflow_plus/src/builder/mod.rs @@ -27,6 +27,8 @@ pub struct FlowStateInner { pub type FlowState = Rc>; +pub const FLOW_USED_MESSAGE: &str = "Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled."; + pub struct FlowBuilder<'a> { flow_state: FlowState, nodes: RefCell>, diff --git a/hydroflow_plus/src/ir.rs b/hydroflow_plus/src/ir.rs index 62d1604b2b1c..557eb8076e65 100644 --- a/hydroflow_plus/src/ir.rs +++ b/hydroflow_plus/src/ir.rs @@ -69,7 +69,6 @@ pub enum HfPlusSource { Stream(DebugExpr), ExternalNetwork(), Iter(DebugExpr), - Interval(DebugExpr), Spin(), } @@ -616,12 +615,6 @@ impl<'a> HfPlusNode { } } - HfPlusSource::Interval(expr) => { - parse_quote! { - #source_ident = source_interval(#expr); - } - } - HfPlusSource::Spin() => { parse_quote! { #source_ident = spin(); diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index 5c951990b120..ddf05115633c 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use stageleft::{q, IntoQuotedMut, Quoted}; use syn::parse_quote; -use crate::builder::FlowState; +use crate::builder::{FlowState, FLOW_USED_MESSAGE}; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; use crate::location::{check_matching_location, LocationId, NoTick}; @@ -64,11 +64,17 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> CycleComplete<'a, TickCycle> for Optional> { fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(self.ir_node.into_inner()), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(self.ir_node.into_inner()), + }); } } @@ -89,11 +95,17 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Optional> CycleComplete<'a, ForwardRef> for Optional> { fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(self.ir_node.into_inner()), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(self.ir_node.into_inner()), + }); } } @@ -114,11 +126,17 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Opt impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Optional { fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + }); } } diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 7c5df816fa89..608fa8dae742 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use stageleft::{q, IntoQuotedMut, Quoted}; -use crate::builder::FlowState; +use crate::builder::{FlowState, FLOW_USED_MESSAGE}; use crate::cycle::{ CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick, ForwardRef, TickCycle, }; @@ -73,11 +73,17 @@ impl<'a, T, N: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> { fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(self.ir_node.into_inner()), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(self.ir_node.into_inner()), + }); } } @@ -98,11 +104,17 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Singleton> CycleComplete<'a, ForwardRef> for Singleton> { fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(self.ir_node.into_inner()), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(self.ir_node.into_inner()), + }); } } diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index c2c791558a5e..777f8648bd49 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -12,7 +12,7 @@ use serde::Serialize; use stageleft::{q, IntoQuotedMut, Quoted}; use syn::parse_quote; -use crate::builder::FlowState; +use crate::builder::{FlowState, FLOW_USED_MESSAGE}; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::cluster::ClusterSelfId; @@ -78,11 +78,17 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream> CycleComplete<'a, TickCycle> for Stream> { fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(self.ir_node.into_inner()), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(self.ir_node.into_inner()), + }); } } @@ -103,11 +109,17 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Str impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Stream { fn complete(self, ident: syn::Ident) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::CycleSink { - ident, - location_kind: self.location_kind(), - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + }); } } @@ -482,17 +494,29 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { } pub fn for_each(self, f: impl IntoQuotedMut<'a, F>) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::ForEach { - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - f: f.splice_fn1().into(), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::ForEach { + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + f: f.splice_fn1().into(), + }); } pub fn dest_sink + 'a>(self, sink: impl Quoted<'a, S>) { - self.flow_state().clone().borrow_mut().leaves.as_mut().expect("Attempted to add a leaf to a flow that has already been finalized. No leaves can be added after the flow has been compiled.").push(HfPlusLeaf::DestSink { - sink: sink.splice_typed().into(), - input: Box::new(self.ir_node.into_inner()), - }); + self.flow_state() + .clone() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::DestSink { + sink: sink.splice_typed().into(), + input: Box::new(self.ir_node.into_inner()), + }); } } From 4fcba1d89f89db8127d2da24290279d24af15e87 Mon Sep 17 00:00:00 2001 From: Rohit Kulshreshtha Date: Wed, 6 Nov 2024 12:21:06 -0800 Subject: [PATCH 31/74] feat(Gossip KV): Cleanup code duplication in main.rs (#1536) Closes #1533 --- .idea/hydroflow.iml | 1 + datastores/gossip_kv/server/main.rs | 140 +++++++++++++++------------- 2 files changed, 76 insertions(+), 65 deletions(-) diff --git a/.idea/hydroflow.iml b/.idea/hydroflow.iml index 03944299ddf0..8871ceafeb5a 100644 --- a/.idea/hydroflow.iml +++ b/.idea/hydroflow.iml @@ -39,6 +39,7 @@ + diff --git a/datastores/gossip_kv/server/main.rs b/datastores/gossip_kv/server/main.rs index cb7dac2c8b39..b04d7f927b54 100644 --- a/datastores/gossip_kv/server/main.rs +++ b/datastores/gossip_kv/server/main.rs @@ -4,16 +4,18 @@ use std::future::ready; use std::hash::Hash; use std::io::Error; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::num::ParseFloatError; +use std::time::Duration; use clap::Parser; use gossip_kv::membership::{MemberDataBuilder, Protocol}; use gossip_kv::server::{server, SeedNode}; -use gossip_kv::{ClientRequest, GossipMessage}; -use hydroflow::futures::{SinkExt, StreamExt}; +use hydroflow::futures::{Sink, SinkExt, StreamExt}; use hydroflow::tokio_stream::wrappers::IntervalStream; use hydroflow::util::{bind_udp_bytes, ipv4_resolve}; -use hydroflow::{bincode, tokio}; +use hydroflow::{bincode, bytes, tokio}; use prometheus::{gather, Encoder, TextEncoder}; +use serde::Serialize; use tracing::{error, info, trace}; use warp::Filter; @@ -33,8 +35,19 @@ struct Opts { /// Port to listen for client requests. #[clap(short, long, default_value = "3001")] client_port: u16, + + /// The duration (in seconds) between gossip rounds. + #[clap(short, long, default_value = "5", value_parser = clap_duration_from_secs)] + gossip_frequency: Duration, +} + +/// Parse duration from float string for clap args. +fn clap_duration_from_secs(arg: &str) -> Result { + arg.parse().map(Duration::from_secs_f32) } +/// Create a SeedNode from a SeedNodeSettings. +/// Performs a DNS lookup on the address. fn make_seed_node(settings: &SeedNodeSettings) -> SeedNode { SeedNode { id: settings.id.clone(), @@ -42,6 +55,7 @@ fn make_seed_node(settings: &SeedNodeSettings) -> SeedNode { } } +/// Handler for the /metrics route. Used to expose prometheus metrics for the server. async fn metrics_handler() -> Result { let encoder = TextEncoder::new(); let metric_families = gather(); @@ -55,19 +69,66 @@ async fn metrics_handler() -> Result { )) } +/// Setup serialization for outbound networking messages. +fn setup_outbound_serialization( + outbound: Outbound, +) -> impl Sink<(Message, SocketAddr), Error = Error> +where + Outbound: Sink<(bytes::Bytes, SocketAddr), Error = Error>, + Message: Serialize + Debug + Send + 'static, +{ + outbound.with(|(msg, addr): (Message, SocketAddr)| { + ready(Ok::<(bytes::Bytes, SocketAddr), Error>(( + hydroflow::util::serialize_to_bytes(msg), + addr, + ))) + }) +} + +/// Setup deserialization for inbound networking messages. + +fn setup_inbound_deserialization( + inbound: Inbound, +) -> impl hydroflow::futures::Stream +where + Inbound: hydroflow::futures::Stream>, + Message: for<'de> serde::Deserialize<'de> + Debug + Send + 'static, +{ + inbound.filter_map(|input| { + let mapped = match input { + Ok((bytes, addr)) => { + let msg: bincode::Result = hydroflow::util::deserialize_from_bytes(&bytes); + match msg { + Ok(msg) => Some((msg, addr)), + Err(e) => { + error!("Error deserializing message: {:?}", e); + None + } + } + } + Err(e) => { + error!("Error receiving message: {:?}", e); + None + } + }; + ready(mapped) + }) +} + #[hydroflow::main] async fn main() { tracing_subscriber::fmt::init(); let opts: Opts = Opts::parse(); + // Setup metrics server let metrics_route = warp::path("metrics").and_then(metrics_handler); tokio::spawn(async move { info!("Starting metrics server on port 4003"); warp::serve(metrics_route).run(([0, 0, 0, 0], 4003)).await; }); - // Setup protocol information in the member metadata. + // Setup protocol information for this member let client_protocol_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), opts.client_port); let gossip_protocol_address = @@ -78,6 +139,7 @@ async fn main() { .add_protocol(Protocol::new("client".into(), client_protocol_address)) .build(); + // Bind to the UDP ports let (client_outbound, client_inbound, _) = bind_udp_bytes(client_protocol_address).await; let (gossip_outbound, gossip_inbound, _) = bind_udp_bytes(gossip_protocol_address).await; @@ -86,70 +148,18 @@ async fn main() { member_data.id, client_protocol_address ); - // TODO: Remove code duplication here. - // Setup message serialization for outbound client responses. - let client_ob = client_outbound.with(|(msg, addr)| { - ready(Ok::<(hydroflow::bytes::Bytes, SocketAddr), Error>(( - hydroflow::util::serialize_to_bytes(msg), - addr, - ))) - }); + // Setup serde for client requests + let client_ob = setup_outbound_serialization(client_outbound); + let client_ib = setup_inbound_deserialization(client_inbound); - // Setup message deserialization for inbound client requests. - let client_ib = client_inbound.filter_map(|input| { - let mapped = match input { - Ok((bytes, addr)) => { - let msg: bincode::Result = - hydroflow::util::deserialize_from_bytes(&bytes); - match msg { - Ok(msg) => Some((msg, addr)), - Err(e) => { - error!("Error deserializing message: {:?}", e); - None - } - } - } - Err(e) => { - error!("Error receiving message: {:?}", e); - None - } - }; - ready(mapped) - }); - - // Setup message serialization for outbound client responses. - let gossip_ob = gossip_outbound.with(|(msg, addr)| { - ready(Ok::<(hydroflow::bytes::Bytes, SocketAddr), Error>(( - hydroflow::util::serialize_to_bytes(msg), - addr, - ))) - }); - - // Setup message deserialization for inbound client requests. - let gossip_ib = gossip_inbound.filter_map(|input| { - let mapped = match input { - Ok((bytes, addr)) => { - let msg: bincode::Result = - hydroflow::util::deserialize_from_bytes(&bytes); - match msg { - Ok(msg) => Some((msg, addr)), - Err(e) => { - error!("Error deserializing message: {:?}", e); - None - } - } - } - Err(e) => { - error!("Error receiving message: {:?}", e); - None - } - }; - ready(mapped) - }); + // Setup serde for gossip messages + let gossip_ob = setup_outbound_serialization(gossip_outbound); + let gossip_ib = setup_inbound_deserialization(gossip_inbound); - let gossip_rx = - IntervalStream::new(tokio::time::interval(tokio::time::Duration::from_secs(5))).map(|_| ()); + // Setup regular gossip triggers + let gossip_rx = IntervalStream::new(tokio::time::interval(opts.gossip_frequency)).map(|_| ()); + // Setup watcher for setting changes let (_watcher, server_settings, settings_stream) = setup_settings_watch(); let seed_nodes = server_settings From 8442d1b524621a9f8b43372a9c25991efb33c25e Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Wed, 6 Nov 2024 13:13:44 -0800 Subject: [PATCH 32/74] style: fixes for latest nightly clippy (#1537) --- datastores/gossip_kv/server/main.rs | 1 - datastores/gossip_kv/server/membership.rs | 2 +- hydro_deploy/core/src/deployment.rs | 5 +++ .../core/src/hydroflow_crate/build.rs | 1 + .../src/hydroflow_crate/tracing_options.rs | 2 + .../serialization/lattices/map_union.rs | 2 +- .../lattices/my_last_write_wins.rs | 2 +- .../protocol/serialization/lattices/point.rs | 2 +- .../serialization/lattices/with_bot.rs | 2 +- .../kvs_bench/protocol/serialization/mod.rs | 2 +- hydroflow/src/scheduled/net/network_vertex.rs | 2 +- .../surface_source_iter_badtype.rs | 2 +- .../surface_source_iter_badtype.stderr | 45 +++++++++---------- hydroflow_datalog_core/src/grammar.rs | 6 ++- 14 files changed, 43 insertions(+), 33 deletions(-) diff --git a/datastores/gossip_kv/server/main.rs b/datastores/gossip_kv/server/main.rs index b04d7f927b54..2d2990a7e0d3 100644 --- a/datastores/gossip_kv/server/main.rs +++ b/datastores/gossip_kv/server/main.rs @@ -86,7 +86,6 @@ where } /// Setup deserialization for inbound networking messages. - fn setup_inbound_deserialization( inbound: Inbound, ) -> impl hydroflow::futures::Stream diff --git a/datastores/gossip_kv/server/membership.rs b/datastores/gossip_kv/server/membership.rs index 2e20ab534bf0..28a3fcadf137 100644 --- a/datastores/gossip_kv/server/membership.rs +++ b/datastores/gossip_kv/server/membership.rs @@ -4,7 +4,7 @@ use gossip_kv::membership::MemberId; // use rand::distributions::Distribution; // use rand::{Rng}; -/// This is a simple distribution that generates a random lower-case alphanumeric +// /// This is a simple distribution that generates a random lower-case alphanumeric // struct LowercaseAlphanumeric; // // impl Distribution for LowercaseAlphanumeric { diff --git a/hydro_deploy/core/src/deployment.rs b/hydro_deploy/core/src/deployment.rs index faec27e64bf5..bcec6996e3d5 100644 --- a/hydro_deploy/core/src/deployment.rs +++ b/hydro_deploy/core/src/deployment.rs @@ -219,6 +219,11 @@ impl Deployment { /// Buildstructor methods. #[buildstructor::buildstructor] impl Deployment { + #[allow( + clippy::allow_attributes, + clippy::too_many_arguments, + reason = "buildstructor" + )] #[builder(entry = "GcpComputeEngineHost", exit = "add")] pub fn add_gcp_compute_engine_host( &mut self, diff --git a/hydro_deploy/core/src/hydroflow_crate/build.rs b/hydro_deploy/core/src/hydroflow_crate/build.rs index 61efa475a1a7..b6612ca288a1 100644 --- a/hydro_deploy/core/src/hydroflow_crate/build.rs +++ b/hydro_deploy/core/src/hydroflow_crate/build.rs @@ -167,6 +167,7 @@ pub async fn build_crate_memoized(params: BuildParams) -> Result<&'static BuildO let path_buf: PathBuf = path.clone().into(); let path = path.into_string(); let data = std::fs::read(path).unwrap(); + assert!(spawned.wait().unwrap().success()); return Ok(BuildOutput { unique_id: nanoid!(8), bin_data: data, diff --git a/hydro_deploy/core/src/hydroflow_crate/tracing_options.rs b/hydro_deploy/core/src/hydroflow_crate/tracing_options.rs index c7f2957a6200..096ae9da0ff0 100644 --- a/hydro_deploy/core/src/hydroflow_crate/tracing_options.rs +++ b/hydro_deploy/core/src/hydroflow_crate/tracing_options.rs @@ -1,3 +1,5 @@ +#![allow(clippy::too_many_arguments, reason = "buildstructor")] + use std::path::PathBuf; use inferno::collapse::dtrace::Options as DtraceOptions; diff --git a/hydroflow/examples/kvs_bench/protocol/serialization/lattices/map_union.rs b/hydroflow/examples/kvs_bench/protocol/serialization/lattices/map_union.rs index 1be9a9c4e9b8..92127af97e60 100644 --- a/hydroflow/examples/kvs_bench/protocol/serialization/lattices/map_union.rs +++ b/hydroflow/examples/kvs_bench/protocol/serialization/lattices/map_union.rs @@ -15,7 +15,7 @@ pub struct MapUnionHashMapWrapper<'a, const SIZE: usize>( pub &'a MapUnionHashMap>, ); -impl<'a, const SIZE: usize> Serialize for MapUnionHashMapWrapper<'a, SIZE> { +impl Serialize for MapUnionHashMapWrapper<'_, SIZE> { fn serialize(&self, serializer: S) -> Result where S: Serializer, diff --git a/hydroflow/examples/kvs_bench/protocol/serialization/lattices/my_last_write_wins.rs b/hydroflow/examples/kvs_bench/protocol/serialization/lattices/my_last_write_wins.rs index 91fc1b71dfb4..74b3041bfbe7 100644 --- a/hydroflow/examples/kvs_bench/protocol/serialization/lattices/my_last_write_wins.rs +++ b/hydroflow/examples/kvs_bench/protocol/serialization/lattices/my_last_write_wins.rs @@ -11,7 +11,7 @@ use crate::protocol::MyLastWriteWins; #[repr(transparent)] pub struct MyLastWriteWinsWrapper<'a, const SIZE: usize>(pub &'a MyLastWriteWins); -impl<'a, const SIZE: usize> Serialize for MyLastWriteWinsWrapper<'a, SIZE> { +impl Serialize for MyLastWriteWinsWrapper<'_, SIZE> { fn serialize(&self, serializer: S) -> Result where S: Serializer, diff --git a/hydroflow/examples/kvs_bench/protocol/serialization/lattices/point.rs b/hydroflow/examples/kvs_bench/protocol/serialization/lattices/point.rs index b0410e203947..98b2552a5c28 100644 --- a/hydroflow/examples/kvs_bench/protocol/serialization/lattices/point.rs +++ b/hydroflow/examples/kvs_bench/protocol/serialization/lattices/point.rs @@ -10,7 +10,7 @@ use crate::buffer_pool::{AutoReturnBuffer, AutoReturnBufferDeserializer, BufferP #[repr(transparent)] pub struct PointWrapper<'a, const SIZE: usize>(pub &'a Point, ()>); -impl<'a, const SIZE: usize> Serialize for PointWrapper<'a, SIZE> { +impl Serialize for PointWrapper<'_, SIZE> { fn serialize(&self, serializer: S) -> Result where S: Serializer, diff --git a/hydroflow/examples/kvs_bench/protocol/serialization/lattices/with_bot.rs b/hydroflow/examples/kvs_bench/protocol/serialization/lattices/with_bot.rs index f5204743b698..d7af4a572f24 100644 --- a/hydroflow/examples/kvs_bench/protocol/serialization/lattices/with_bot.rs +++ b/hydroflow/examples/kvs_bench/protocol/serialization/lattices/with_bot.rs @@ -14,7 +14,7 @@ pub struct WithBotWrapper<'a, const SIZE: usize>( pub &'a WithBot, ()>>, ); -impl<'a, const SIZE: usize> Serialize for WithBotWrapper<'a, SIZE> { +impl Serialize for WithBotWrapper<'_, SIZE> { fn serialize(&self, serializer: S) -> Result where S: Serializer, diff --git a/hydroflow/examples/kvs_bench/protocol/serialization/mod.rs b/hydroflow/examples/kvs_bench/protocol/serialization/mod.rs index db7686f995a2..29fdfb142503 100644 --- a/hydroflow/examples/kvs_bench/protocol/serialization/mod.rs +++ b/hydroflow/examples/kvs_bench/protocol/serialization/mod.rs @@ -116,7 +116,7 @@ enum KvsRequestField { Delete, } struct KvsRequestFieldVisitor; -impl<'de> Visitor<'de> for KvsRequestFieldVisitor { +impl Visitor<'_> for KvsRequestFieldVisitor { type Value = KvsRequestField; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/hydroflow/src/scheduled/net/network_vertex.rs b/hydroflow/src/scheduled/net/network_vertex.rs index 13077e6290e8..b2db71169646 100644 --- a/hydroflow/src/scheduled/net/network_vertex.rs +++ b/hydroflow/src/scheduled/net/network_vertex.rs @@ -18,7 +18,7 @@ pub type Address = String; // These methods can't be wrapped up in a trait because async methods are not // allowed in traits (yet). -impl<'a> Hydroflow<'a> { +impl Hydroflow<'_> { // TODO(justin): document these, but they're derivatives of inbound_tcp_vertex_internal. pub async fn inbound_tcp_vertex_port(&mut self, port: u16) -> RecvPort> where diff --git a/hydroflow/tests/compile-fail/surface_source_iter_badtype.rs b/hydroflow/tests/compile-fail/surface_source_iter_badtype.rs index 026c5f14bf66..f97ad44dfcf2 100644 --- a/hydroflow/tests/compile-fail/surface_source_iter_badtype.rs +++ b/hydroflow/tests/compile-fail/surface_source_iter_badtype.rs @@ -2,7 +2,7 @@ use hydroflow::hydroflow_syntax; fn main() { let mut df = hydroflow_syntax! { - source_iter(5) -> for_each(std::mem::drop); + source_iter(()) -> for_each(std::mem::drop); }; df.run_available(); } diff --git a/hydroflow/tests/compile-fail/surface_source_iter_badtype.stderr b/hydroflow/tests/compile-fail/surface_source_iter_badtype.stderr index 9866f73a5d6b..85d2c8e16e8d 100644 --- a/hydroflow/tests/compile-fail/surface_source_iter_badtype.stderr +++ b/hydroflow/tests/compile-fail/surface_source_iter_badtype.stderr @@ -1,45 +1,44 @@ -error[E0277]: `{integer}` is not an iterator +error[E0277]: `()` is not an iterator --> tests/compile-fail/surface_source_iter_badtype.rs:5:21 | -5 | source_iter(5) -> for_each(std::mem::drop); - | ------------^- +5 | source_iter(()) -> for_each(std::mem::drop); + | ------------^^- | | | - | | `{integer}` is not an iterator + | | `()` is not an iterator | required by a bound introduced by this call | - = help: the trait `Iterator` is not implemented for `{integer}`, which is required by `{integer}: IntoIterator` - = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required for `{integer}` to implement `IntoIterator` + = help: the trait `Iterator` is not implemented for `()`, which is required by `(): IntoIterator` + = note: required for `()` to implement `IntoIterator` note: required by a bound in `check_iter` --> tests/compile-fail/surface_source_iter_badtype.rs:5:9 | -5 | source_iter(5) -> for_each(std::mem::drop); - | ^^^^^^^^^^^^^^ required by this bound in `check_iter` +5 | source_iter(()) -> for_each(std::mem::drop); + | ^^^^^^^^^^^^^^^ required by this bound in `check_iter` -error[E0277]: `{integer}` is not an iterator +error[E0277]: `()` is not an iterator --> tests/compile-fail/surface_source_iter_badtype.rs:5:9 | -5 | source_iter(5) -> for_each(std::mem::drop); - | ^^^^^^^^^^^^^^ `{integer}` is not an iterator +5 | source_iter(()) -> for_each(std::mem::drop); + | ^^^^^^^^^^^^^^^ `()` is not an iterator | - = help: the trait `Iterator` is not implemented for `{integer}`, which is required by `{integer}: IntoIterator` - = note: required for `{integer}` to implement `IntoIterator` + = help: the trait `Iterator` is not implemented for `()`, which is required by `(): IntoIterator` + = note: required for `()` to implement `IntoIterator` note: required by a bound in `check_iter` --> tests/compile-fail/surface_source_iter_badtype.rs:5:9 | -5 | source_iter(5) -> for_each(std::mem::drop); - | ^^^^^^^^^^^^^^ required by this bound in `check_iter` +5 | source_iter(()) -> for_each(std::mem::drop); + | ^^^^^^^^^^^^^^^ required by this bound in `check_iter` -error[E0277]: `{integer}` is not an iterator +error[E0277]: `()` is not an iterator --> tests/compile-fail/surface_source_iter_badtype.rs:5:9 | -5 | source_iter(5) -> for_each(std::mem::drop); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `{integer}` is not an iterator +5 | source_iter(()) -> for_each(std::mem::drop); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator | - = help: the trait `Iterator` is not implemented for `{integer}`, which is required by `{integer}: IntoIterator` - = note: required for `{integer}` to implement `IntoIterator` + = help: the trait `Iterator` is not implemented for `()`, which is required by `(): IntoIterator` + = note: required for `()` to implement `IntoIterator` note: required by a bound in `check_iter` --> tests/compile-fail/surface_source_iter_badtype.rs:5:9 | -5 | source_iter(5) -> for_each(std::mem::drop); - | ^^^^^^^^^^^^^^ required by this bound in `check_iter` +5 | source_iter(()) -> for_each(std::mem::drop); + | ^^^^^^^^^^^^^^^ required by this bound in `check_iter` diff --git a/hydroflow_datalog_core/src/grammar.rs b/hydroflow_datalog_core/src/grammar.rs index fe9f95360f42..10345e0c38ac 100644 --- a/hydroflow_datalog_core/src/grammar.rs +++ b/hydroflow_datalog_core/src/grammar.rs @@ -136,7 +136,11 @@ pub mod datalog { } #[derive(Debug, Clone)] - #[expect(clippy::manual_non_exhaustive, reason = "`()` used for leaf")] + #[allow( + clippy::allow_attributes, + clippy::manual_non_exhaustive, + reason = "`()` used for leaf" + )] pub struct AtNode { #[rust_sitter::leaf(text = "@")] _at: (), From 9f744052dd4ac744f5a1baa4e0cb9253adaeba1b Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 6 Nov 2024 14:09:36 -0800 Subject: [PATCH 33/74] refactor(hydroflow_plus): use `location.flow_state()` to avoid clone (#1541) --- hydroflow_plus/src/optional.rs | 18 +++++++----------- hydroflow_plus/src/singleton.rs | 14 +++++--------- hydroflow_plus/src/stream.rs | 28 +++++++++++----------------- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index ddf05115633c..71eab96cbd0f 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use stageleft::{q, IntoQuotedMut, Quoted}; use syn::parse_quote; -use crate::builder::{FlowState, FLOW_USED_MESSAGE}; +use crate::builder::FLOW_USED_MESSAGE; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; use crate::location::{check_matching_location, LocationId, NoTick}; @@ -35,10 +35,6 @@ impl<'a, T, W, N: Location<'a>> Optional { fn location_kind(&self) -> LocationId { self.location.id() } - - fn flow_state(&self) -> &FlowState { - self.location.flow_state() - } } impl<'a, T, N: Location<'a>> DeferTick for Optional> { @@ -64,8 +60,8 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> CycleComplete<'a, TickCycle> for Optional> { fn complete(self, ident: syn::Ident) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() @@ -95,8 +91,8 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Optional> CycleComplete<'a, ForwardRef> for Optional> { fn complete(self, ident: syn::Ident) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() @@ -126,8 +122,8 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Opt impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Optional { fn complete(self, ident: syn::Ident) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 608fa8dae742..7271cc4a0854 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use stageleft::{q, IntoQuotedMut, Quoted}; -use crate::builder::{FlowState, FLOW_USED_MESSAGE}; +use crate::builder::FLOW_USED_MESSAGE; use crate::cycle::{ CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick, ForwardRef, TickCycle, }; @@ -33,10 +33,6 @@ impl<'a, T, W, N: Location<'a>> Singleton { fn location_kind(&self) -> LocationId { self.location.id() } - - fn flow_state(&self) -> &FlowState { - self.location.flow_state() - } } impl<'a, T, N: Location<'a>> From> for Singleton { @@ -73,8 +69,8 @@ impl<'a, T, N: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> { fn complete(self, ident: syn::Ident) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() @@ -104,8 +100,8 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Singleton> CycleComplete<'a, ForwardRef> for Singleton> { fn complete(self, ident: syn::Ident) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 777f8648bd49..065635d2d9f7 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -12,7 +12,7 @@ use serde::Serialize; use stageleft::{q, IntoQuotedMut, Quoted}; use syn::parse_quote; -use crate::builder::{FlowState, FLOW_USED_MESSAGE}; +use crate::builder::FLOW_USED_MESSAGE; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::cluster::ClusterSelfId; @@ -46,10 +46,6 @@ pub struct Stream { } impl<'a, T, W, N: Location<'a>> Stream { - fn flow_state(&self) -> &FlowState { - self.location.flow_state() - } - fn location_kind(&self) -> LocationId { self.location.id() } @@ -78,8 +74,8 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream> CycleComplete<'a, TickCycle> for Stream> { fn complete(self, ident: syn::Ident) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() @@ -109,8 +105,8 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Str impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Stream { fn complete(self, ident: syn::Ident) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() @@ -494,8 +490,8 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { } pub fn for_each(self, f: impl IntoQuotedMut<'a, F>) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() @@ -507,8 +503,8 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { } pub fn dest_sink + 'a>(self, sink: impl Quoted<'a, S>) { - self.flow_state() - .clone() + self.location + .flow_state() .borrow_mut() .leaves .as_mut() @@ -664,8 +660,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { { let serialize_pipeline = Some(serialize_bincode::(N::is_demux())); - let flow_state = self.flow_state().clone(); - let mut flow_state_borrow = flow_state.borrow_mut(); + let mut flow_state_borrow = self.location.flow_state().borrow_mut(); let external_key = flow_state_borrow.next_external_out; flow_state_borrow.next_external_out += 1; @@ -725,8 +720,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { where N: CanSend<'a, ExternalProcess<'a, N2>, In = T, Out = Bytes>, { - let flow_state = self.flow_state().clone(); - let mut flow_state_borrow = flow_state.borrow_mut(); + let mut flow_state_borrow = self.location.flow_state().borrow_mut(); let external_key = flow_state_borrow.next_external_out; flow_state_borrow.next_external_out += 1; From e9d05bf11a0e85da8ed1a0fe00be7769298308c2 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 6 Nov 2024 14:42:07 -0800 Subject: [PATCH 34/74] refactor(hydroflow_plus): move `HfCompiled` and friends to a module (#1542) --- hydroflow_plus/src/builder/built.rs | 2 +- hydroflow_plus/src/builder/compiled.rs | 123 ++++++++++++++++++++ hydroflow_plus/src/builder/deploy.rs | 3 +- hydroflow_plus/src/builder/mod.rs | 5 +- hydroflow_plus/src/lib.rs | 153 +------------------------ hydroflow_plus/src/runtime_context.rs | 33 ++++++ 6 files changed, 164 insertions(+), 155 deletions(-) create mode 100644 hydroflow_plus/src/builder/compiled.rs create mode 100644 hydroflow_plus/src/runtime_context.rs diff --git a/hydroflow_plus/src/builder/built.rs b/hydroflow_plus/src/builder/built.rs index 4caf767f0a6d..440dabe41973 100644 --- a/hydroflow_plus/src/builder/built.rs +++ b/hydroflow_plus/src/builder/built.rs @@ -3,11 +3,11 @@ use std::marker::PhantomData; use hydroflow_lang::graph::{eliminate_extra_unions_tees, HydroflowGraph}; +use super::compiled::HfCompiled; use super::deploy::{DeployFlow, DeployResult}; use crate::deploy::{ClusterSpec, Deploy, ExternalSpec, LocalDeploy, ProcessSpec}; use crate::ir::HfPlusLeaf; use crate::location::{Cluster, ExternalProcess, Process}; -use crate::HfCompiled; pub struct BuiltFlow<'a> { pub(super) ir: Vec, diff --git a/hydroflow_plus/src/builder/compiled.rs b/hydroflow_plus/src/builder/compiled.rs new file mode 100644 index 000000000000..0d16884e0166 --- /dev/null +++ b/hydroflow_plus/src/builder/compiled.rs @@ -0,0 +1,123 @@ +use std::collections::BTreeMap; +use std::marker::PhantomData; + +use hydroflow::scheduled::graph::Hydroflow; +use hydroflow_lang::graph::{partition_graph, HydroflowGraph}; +use proc_macro2::TokenStream; +use quote::quote; +use stageleft::runtime_support::FreeVariable; +use stageleft::Quoted; + +pub struct HfCompiled<'a, ID> { + pub(super) hydroflow_ir: BTreeMap, + pub(super) extra_stmts: BTreeMap>, + pub(super) _phantom: PhantomData<&'a mut &'a ID>, +} + +impl HfCompiled<'_, ID> { + pub fn hydroflow_ir(&self) -> &BTreeMap { + &self.hydroflow_ir + } + + pub fn take_ir(self) -> BTreeMap { + self.hydroflow_ir + } +} + +impl<'a> HfCompiled<'a, usize> { + pub fn with_dynamic_id(self, id: impl Quoted<'a, usize>) -> HfBuiltWithId<'a> { + let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") + .expect("hydroflow_plus should be present in `Cargo.toml`"); + let root = match hydroflow_crate { + proc_macro_crate::FoundCrate::Itself => quote! { hydroflow_plus }, + proc_macro_crate::FoundCrate::Name(name) => { + let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); + quote! { #ident } + } + }; + + let mut conditioned_tokens = None; + for (subgraph_id, flat_graph) in self.hydroflow_ir { + let partitioned_graph = + partition_graph(flat_graph).expect("Failed to partition (cycle detected)."); + + let mut diagnostics = Vec::new(); + let tokens = partitioned_graph.as_code(&root, true, quote::quote!(), &mut diagnostics); + let my_extra_stmts = self + .extra_stmts + .get(&subgraph_id) + .cloned() + .unwrap_or_default(); + + if let Some(conditioned_tokens) = conditioned_tokens.as_mut() { + *conditioned_tokens = syn::parse_quote! { + #conditioned_tokens else if __given_id == #subgraph_id { + #(#my_extra_stmts)* + #tokens + } + }; + } else { + conditioned_tokens = Some(syn::parse_quote! { + if __given_id == #subgraph_id { + #(#my_extra_stmts)* + #tokens + } + }); + } + } + + let conditioned_tokens: TokenStream = conditioned_tokens.unwrap(); + let id = id.splice_untyped(); + HfBuiltWithId { + tokens: syn::parse_quote!({ + let __given_id = #id; + #conditioned_tokens else { + panic!("Invalid node id: {}", __given_id); + } + }), + _phantom: PhantomData, + } + } +} + +impl<'a> Quoted<'a, Hydroflow<'a>> for HfCompiled<'a, ()> {} + +impl<'a> FreeVariable> for HfCompiled<'a, ()> { + fn to_tokens(mut self) -> (Option, Option) { + let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") + .expect("hydroflow_plus should be present in `Cargo.toml`"); + let root = match hydroflow_crate { + proc_macro_crate::FoundCrate::Itself => quote! { hydroflow_plus }, + proc_macro_crate::FoundCrate::Name(name) => { + let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); + quote! { #ident } + } + }; + + if self.hydroflow_ir.len() != 1 { + panic!("Expected exactly one subgraph in the Hydroflow IR"); + } + + let flat_graph = self.hydroflow_ir.remove(&0).unwrap(); + let partitioned_graph = + partition_graph(flat_graph).expect("Failed to partition (cycle detected)."); + + let mut diagnostics = Vec::new(); + let tokens = partitioned_graph.as_code(&root, true, quote::quote!(), &mut diagnostics); + + (None, Some(tokens)) + } +} + +pub struct HfBuiltWithId<'a> { + tokens: TokenStream, + _phantom: PhantomData<&'a mut &'a ()>, +} + +impl<'a> Quoted<'a, Hydroflow<'a>> for HfBuiltWithId<'a> {} + +impl<'a> FreeVariable> for HfBuiltWithId<'a> { + fn to_tokens(self) -> (Option, Option) { + (None, Some(self.tokens)) + } +} diff --git a/hydroflow_plus/src/builder/deploy.rs b/hydroflow_plus/src/builder/deploy.rs index c345d7f7bf43..e441b76eaed6 100644 --- a/hydroflow_plus/src/builder/deploy.rs +++ b/hydroflow_plus/src/builder/deploy.rs @@ -11,13 +11,14 @@ use serde::Serialize; use stageleft::Quoted; use super::built::build_inner; +use super::compiled::HfCompiled; use crate::deploy::{ExternalSpec, LocalDeploy, Node, RegisterPort}; use crate::ir::HfPlusLeaf; use crate::location::external_process::{ ExternalBincodeSink, ExternalBincodeStream, ExternalBytesPort, }; use crate::location::{ExternalProcess, Location, LocationId}; -use crate::{Cluster, ClusterSpec, Deploy, HfCompiled, Process, ProcessSpec}; +use crate::{Cluster, ClusterSpec, Deploy, Process, ProcessSpec}; pub struct DeployFlow<'a, D: LocalDeploy<'a>> { pub(super) ir: Vec, diff --git a/hydroflow_plus/src/builder/mod.rs b/hydroflow_plus/src/builder/mod.rs index 33101b554e67..5b9fabc52428 100644 --- a/hydroflow_plus/src/builder/mod.rs +++ b/hydroflow_plus/src/builder/mod.rs @@ -10,6 +10,7 @@ use crate::location::{Cluster, ExternalProcess, Process}; use crate::RuntimeContext; pub mod built; +pub mod compiled; pub mod deploy; pub struct FlowStateInner { @@ -151,8 +152,6 @@ impl<'a> FlowBuilder<'a> { } pub fn runtime_context(&self) -> RuntimeContext<'a> { - RuntimeContext { - _phantom: PhantomData, - } + RuntimeContext::new() } } diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 46539fa81d1a..bbc38afb76d8 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -2,22 +2,16 @@ stageleft::stageleft_no_entry_crate!(); -use std::collections::BTreeMap; -use std::marker::PhantomData; - -use hydroflow::scheduled::context::Context; pub use hydroflow::scheduled::graph::Hydroflow; pub use hydroflow::*; -use lang::graph::{partition_graph, HydroflowGraph}; -use proc_macro2::TokenStream; -use quote::quote; -use stageleft::runtime_support::FreeVariable; -use stageleft::Quoted; pub mod runtime_support { pub use bincode; } +pub mod runtime_context; +pub use runtime_context::RuntimeContext; + pub mod stream; pub use stream::{Bounded, Stream, Unbounded}; @@ -47,147 +41,6 @@ pub mod properties; mod staging_util; -#[derive(Clone)] -pub struct RuntimeContext<'a> { - _phantom: PhantomData<&'a mut &'a ()>, -} - -impl RuntimeContext<'_> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl Copy for RuntimeContext<'_> {} - -impl Default for RuntimeContext<'_> { - fn default() -> Self { - Self::new() - } -} - -impl<'a> FreeVariable<&'a Context> for RuntimeContext<'a> { - fn to_tokens(self) -> (Option, Option) { - (None, Some(quote!(&context))) - } -} - -pub struct HfCompiled<'a, ID> { - hydroflow_ir: BTreeMap, - extra_stmts: BTreeMap>, - _phantom: PhantomData<&'a mut &'a ID>, -} - -impl HfCompiled<'_, ID> { - pub fn hydroflow_ir(&self) -> &BTreeMap { - &self.hydroflow_ir - } - - pub fn take_ir(self) -> BTreeMap { - self.hydroflow_ir - } -} - -impl<'a> HfCompiled<'a, usize> { - pub fn with_dynamic_id(self, id: impl Quoted<'a, usize>) -> HfBuiltWithId<'a> { - let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") - .expect("hydroflow_plus should be present in `Cargo.toml`"); - let root = match hydroflow_crate { - proc_macro_crate::FoundCrate::Itself => quote! { hydroflow_plus }, - proc_macro_crate::FoundCrate::Name(name) => { - let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); - quote! { #ident } - } - }; - - let mut conditioned_tokens = None; - for (subgraph_id, flat_graph) in self.hydroflow_ir { - let partitioned_graph = - partition_graph(flat_graph).expect("Failed to partition (cycle detected)."); - - let mut diagnostics = Vec::new(); - let tokens = partitioned_graph.as_code(&root, true, quote::quote!(), &mut diagnostics); - let my_extra_stmts = self - .extra_stmts - .get(&subgraph_id) - .cloned() - .unwrap_or_default(); - - if let Some(conditioned_tokens) = conditioned_tokens.as_mut() { - *conditioned_tokens = syn::parse_quote! { - #conditioned_tokens else if __given_id == #subgraph_id { - #(#my_extra_stmts)* - #tokens - } - }; - } else { - conditioned_tokens = Some(syn::parse_quote! { - if __given_id == #subgraph_id { - #(#my_extra_stmts)* - #tokens - } - }); - } - } - - let conditioned_tokens: TokenStream = conditioned_tokens.unwrap(); - let id = id.splice_untyped(); - HfBuiltWithId { - tokens: syn::parse_quote!({ - let __given_id = #id; - #conditioned_tokens else { - panic!("Invalid node id: {}", __given_id); - } - }), - _phantom: PhantomData, - } - } -} - -impl<'a> Quoted<'a, Hydroflow<'a>> for HfCompiled<'a, ()> {} - -impl<'a> FreeVariable> for HfCompiled<'a, ()> { - fn to_tokens(mut self) -> (Option, Option) { - let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") - .expect("hydroflow_plus should be present in `Cargo.toml`"); - let root = match hydroflow_crate { - proc_macro_crate::FoundCrate::Itself => quote! { hydroflow_plus }, - proc_macro_crate::FoundCrate::Name(name) => { - let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); - quote! { #ident } - } - }; - - if self.hydroflow_ir.len() != 1 { - panic!("Expected exactly one subgraph in the Hydroflow IR"); - } - - let flat_graph = self.hydroflow_ir.remove(&0).unwrap(); - let partitioned_graph = - partition_graph(flat_graph).expect("Failed to partition (cycle detected)."); - - let mut diagnostics = Vec::new(); - let tokens = partitioned_graph.as_code(&root, true, quote::quote!(), &mut diagnostics); - - (None, Some(tokens)) - } -} - -pub struct HfBuiltWithId<'a> { - tokens: TokenStream, - _phantom: PhantomData<&'a mut &'a ()>, -} - -impl<'a> Quoted<'a, Hydroflow<'a>> for HfBuiltWithId<'a> {} - -impl<'a> FreeVariable> for HfBuiltWithId<'a> { - fn to_tokens(self) -> (Option, Option) { - (None, Some(self.tokens)) - } -} - #[stageleft::runtime] #[cfg(test)] mod tests { diff --git a/hydroflow_plus/src/runtime_context.rs b/hydroflow_plus/src/runtime_context.rs new file mode 100644 index 000000000000..2732b87339a3 --- /dev/null +++ b/hydroflow_plus/src/runtime_context.rs @@ -0,0 +1,33 @@ +use std::marker::PhantomData; + +use hydroflow::scheduled::context::Context; +use proc_macro2::TokenStream; +use quote::quote; +use stageleft::runtime_support::FreeVariable; + +#[derive(Clone)] +pub struct RuntimeContext<'a> { + _phantom: PhantomData<&'a mut &'a ()>, +} + +impl RuntimeContext<'_> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl Copy for RuntimeContext<'_> {} + +impl Default for RuntimeContext<'_> { + fn default() -> Self { + Self::new() + } +} + +impl<'a> FreeVariable<&'a Context> for RuntimeContext<'a> { + fn to_tokens(self) -> (Option, Option) { + (None, Some(quote!(&context))) + } +} From a1b45203178165683cb4b5ae611c598cc9c14853 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 6 Nov 2024 15:13:41 -0800 Subject: [PATCH 35/74] refactor(hydroflow_plus): move rewrites to a submodule (#1543) --- hydroflow_plus/src/builder/built.rs | 2 +- hydroflow_plus/src/lib.rs | 5 +--- hydroflow_plus/src/rewrites/mod.rs | 3 ++ .../src/{ => rewrites}/persist_pullup.rs | 0 hydroflow_plus/src/{ => rewrites}/profiler.rs | 3 +- .../src/{ => rewrites}/properties.rs | 0 ...__tests__persist_pullup_behind_tee-2.snap} | 14 ++++----- ...up__tests__persist_pullup_behind_tee.snap} | 14 ++++----- ...ist_pullup_behind_tee@surface_graph_0.snap | 20 +++++++++++++ ..._tests__persist_pullup_through_map-2.snap} | 8 ++--- ...p__tests__persist_pullup_through_map.snap} | 8 ++--- ...st_pullup_through_map@surface_graph_0.snap | 10 +++++++ ...ts__profiler_wrapping_all_operators-2.snap | 26 +++++++++++++++++ ...sts__profiler_wrapping_all_operators.snap} | 8 ++--- ...roperties__tests__property_optimized.snap} | 12 ++++---- ...ist_pullup_behind_tee@surface_graph_0.snap | 20 ------------- ...st_pullup_through_map@surface_graph_0.snap | 10 ------- ...sts__predicate_pushdown_through_map-2.snap | 29 ------------------- ...tests__predicate_pushdown_through_map.snap | 20 ------------- ...ts__profiler_wrapping_all_operators-2.snap | 26 ----------------- 20 files changed, 95 insertions(+), 143 deletions(-) create mode 100644 hydroflow_plus/src/rewrites/mod.rs rename hydroflow_plus/src/{ => rewrites}/persist_pullup.rs (100%) rename hydroflow_plus/src/{ => rewrites}/profiler.rs (96%) rename hydroflow_plus/src/{ => rewrites}/properties.rs (100%) rename hydroflow_plus/src/{snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee-2.snap => rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee-2.snap} (59%) rename hydroflow_plus/src/{snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee.snap => rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee.snap} (75%) create mode 100644 hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee@surface_graph_0.snap rename hydroflow_plus/src/{snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map-2.snap => rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map-2.snap} (51%) rename hydroflow_plus/src/{snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map.snap => rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map.snap} (58%) create mode 100644 hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map@surface_graph_0.snap create mode 100644 hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators-2.snap rename hydroflow_plus/src/{snapshots/hydroflow_plus__profiler__tests__profiler_wrapping_all_operators.snap => rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators.snap} (59%) rename hydroflow_plus/src/{snapshots/hydroflow_plus__properties__tests__property_optimized.snap => rewrites/snapshots/hydroflow_plus__rewrites__properties__tests__property_optimized.snap} (54%) delete mode 100644 hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee@surface_graph_0.snap delete mode 100644 hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map@surface_graph_0.snap delete mode 100644 hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__predicate_pushdown_through_map-2.snap delete mode 100644 hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__predicate_pushdown_through_map.snap delete mode 100644 hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__profiler_wrapping_all_operators-2.snap diff --git a/hydroflow_plus/src/builder/built.rs b/hydroflow_plus/src/builder/built.rs index 440dabe41973..ee196b3f3016 100644 --- a/hydroflow_plus/src/builder/built.rs +++ b/hydroflow_plus/src/builder/built.rs @@ -73,7 +73,7 @@ impl<'a> BuiltFlow<'a> { } pub fn with_default_optimize(self) -> BuiltFlow<'a> { - self.optimize_with(crate::persist_pullup::persist_pullup) + self.optimize_with(crate::rewrites::persist_pullup::persist_pullup) } fn into_deploy>(mut self) -> DeployFlow<'a, D> { diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index bbc38afb76d8..2e7f322ec1d8 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -34,10 +34,7 @@ pub use builder::FlowBuilder; pub mod ir; -pub mod persist_pullup; -pub mod profiler; - -pub mod properties; +pub mod rewrites; mod staging_util; diff --git a/hydroflow_plus/src/rewrites/mod.rs b/hydroflow_plus/src/rewrites/mod.rs new file mode 100644 index 000000000000..20c3302f0fe3 --- /dev/null +++ b/hydroflow_plus/src/rewrites/mod.rs @@ -0,0 +1,3 @@ +pub mod persist_pullup; +pub mod profiler; +pub mod properties; diff --git a/hydroflow_plus/src/persist_pullup.rs b/hydroflow_plus/src/rewrites/persist_pullup.rs similarity index 100% rename from hydroflow_plus/src/persist_pullup.rs rename to hydroflow_plus/src/rewrites/persist_pullup.rs diff --git a/hydroflow_plus/src/profiler.rs b/hydroflow_plus/src/rewrites/profiler.rs similarity index 96% rename from hydroflow_plus/src/profiler.rs rename to hydroflow_plus/src/rewrites/profiler.rs index 4e4ded0833cc..a2627567653c 100644 --- a/hydroflow_plus/src/profiler.rs +++ b/hydroflow_plus/src/rewrites/profiler.rs @@ -3,8 +3,9 @@ use std::cell::RefCell; use hydroflow::futures::channel::mpsc::UnboundedSender; use stageleft::*; +use super::profiler as myself; // TODO(shadaj): stageleft does not support `self::...` use crate::ir::*; -use crate::{profiler as myself, RuntimeContext}; +use crate::RuntimeContext; pub fn increment_counter(count: &mut u64) { *count += 1; diff --git a/hydroflow_plus/src/properties.rs b/hydroflow_plus/src/rewrites/properties.rs similarity index 100% rename from hydroflow_plus/src/properties.rs rename to hydroflow_plus/src/rewrites/properties.rs diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee-2.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee-2.snap similarity index 59% rename from hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee-2.snap rename to hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee-2.snap index 15f9404ce942..7ab17a3d0ed5 100644 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee-2.snap +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee-2.snap @@ -1,17 +1,17 @@ --- -source: hydroflow_plus/src/persist_pullup.rs +source: hydroflow_plus/src/rewrites/persist_pullup.rs expression: optimized.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), input: Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 }), input: Tee { inner: : Source { source: Iter( - { use crate :: __staged :: persist_pullup :: tests :: * ; 0 .. 10 }, + { use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; 0 .. 10 }, ), location_kind: Process( 0, @@ -22,14 +22,14 @@ expression: optimized.ir() ), }, ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), input: Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 }), input: Tee { inner: : Source { source: Iter( - { use crate :: __staged :: persist_pullup :: tests :: * ; 0 .. 10 }, + { use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; 0 .. 10 }, ), location_kind: Process( 0, diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee.snap similarity index 75% rename from hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee.snap rename to hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee.snap index 441cdcbfca74..b0cdf8700bfb 100644 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee.snap +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee.snap @@ -1,21 +1,21 @@ --- -source: hydroflow_plus/src/persist_pullup.rs +source: hydroflow_plus/src/rewrites/persist_pullup.rs expression: built.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), input: Unpersist( Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 }), input: Tee { inner: : Persist( Unpersist( Persist( Source { source: Iter( - { use crate :: __staged :: persist_pullup :: tests :: * ; 0 .. 10 }, + { use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; 0 .. 10 }, ), location_kind: Process( 0, @@ -30,18 +30,18 @@ expression: built.ir() ), }, ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), input: Unpersist( Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 }), input: Tee { inner: : Persist( Unpersist( Persist( Source { source: Iter( - { use crate :: __staged :: persist_pullup :: tests :: * ; 0 .. 10 }, + { use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; 0 .. 10 }, ), location_kind: Process( 0, diff --git a/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee@surface_graph_0.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee@surface_graph_0.snap new file mode 100644 index 000000000000..14beff31a5de --- /dev/null +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_behind_tee@surface_graph_0.snap @@ -0,0 +1,20 @@ +--- +source: hydroflow_plus/src/rewrites/persist_pullup.rs +expression: graph.surface_syntax_string() +--- +1v1 = source_iter ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; 0 .. 10 }); +2v1 = tee (); +3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 })); +4v1 = persist :: < 'static > (); +5v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) })); +6v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 })); +7v1 = persist :: < 'static > (); +8v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) })); + +1v1 -> 2v1; +2v1 -> 3v1; +3v1 -> 4v1; +4v1 -> 5v1; +2v1 -> 6v1; +6v1 -> 7v1; +7v1 -> 8v1; diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map-2.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map-2.snap similarity index 51% rename from hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map-2.snap rename to hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map-2.snap index e9cad525e761..9811741ab041 100644 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map-2.snap +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map-2.snap @@ -1,15 +1,15 @@ --- -source: hydroflow_plus/src/persist_pullup.rs +source: hydroflow_plus/src/rewrites/persist_pullup.rs expression: optimized.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 }), input: Source { source: Iter( - { use crate :: __staged :: persist_pullup :: tests :: * ; 0 .. 10 }, + { use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; 0 .. 10 }, ), location_kind: Process( 0, diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map.snap similarity index 58% rename from hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map.snap rename to hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map.snap index 0feab1293dd6..a079c2bfaaaf 100644 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map.snap +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map.snap @@ -1,17 +1,17 @@ --- -source: hydroflow_plus/src/persist_pullup.rs +source: hydroflow_plus/src/rewrites/persist_pullup.rs expression: built.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) }), input: Unpersist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 }), input: Persist( Source { source: Iter( - { use crate :: __staged :: persist_pullup :: tests :: * ; 0 .. 10 }, + { use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; 0 .. 10 }, ), location_kind: Process( 0, diff --git a/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map@surface_graph_0.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map@surface_graph_0.snap new file mode 100644 index 000000000000..3c16bf5d01c4 --- /dev/null +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__persist_pullup__tests__persist_pullup_through_map@surface_graph_0.snap @@ -0,0 +1,10 @@ +--- +source: hydroflow_plus/src/rewrites/persist_pullup.rs +expression: graph.surface_syntax_string() +--- +1v1 = source_iter ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; 0 .. 10 }); +2v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | v | v + 1 })); +3v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) })); + +1v1 -> 2v1; +2v1 -> 3v1; diff --git a/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators-2.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators-2.snap new file mode 100644 index 000000000000..627f53dd38ed --- /dev/null +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators-2.snap @@ -0,0 +1,26 @@ +--- +source: hydroflow_plus/src/rewrites/profiler.rs +expression: "&pushed_down.ir()" +--- +[ + ForEach { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: profiler :: tests :: * ; | n | println ! ("{}" , n) }), + input: Inspect { + f: { use crate :: __staged :: rewrites :: profiler :: * ; let counter_queue = Fake ; let counters = Fake ; let my_id = 0u32 ; { counter_queue . borrow () . unbounded_send ((my_id as usize , counters . borrow () [my_id as usize])) . unwrap () ; counters . borrow_mut () [my_id as usize] = 0 ; move | _ | { myself :: increment_counter (& mut counters . borrow_mut () [my_id as usize]) ; } } }, + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: profiler :: tests :: * ; | v | v + 1 }), + input: Inspect { + f: { use crate :: __staged :: rewrites :: profiler :: * ; let counter_queue = Fake ; let counters = Fake ; let my_id = 1u32 ; { counter_queue . borrow () . unbounded_send ((my_id as usize , counters . borrow () [my_id as usize])) . unwrap () ; counters . borrow_mut () [my_id as usize] = 0 ; move | _ | { myself :: increment_counter (& mut counters . borrow_mut () [my_id as usize]) ; } } }, + input: Source { + source: Iter( + { use crate :: __staged :: rewrites :: profiler :: tests :: * ; 0 .. 10 }, + ), + location_kind: Process( + 0, + ), + }, + }, + }, + }, + }, +] diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__profiler_wrapping_all_operators.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators.snap similarity index 59% rename from hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__profiler_wrapping_all_operators.snap rename to hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators.snap index c68abed47839..2d59233459b0 100644 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__profiler_wrapping_all_operators.snap +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators.snap @@ -1,17 +1,17 @@ --- -source: hydroflow_plus/src/profiler.rs +source: hydroflow_plus/src/rewrites/profiler.rs expression: "&built.ir()" --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: profiler :: tests :: * ; | n | println ! ("{}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: profiler :: tests :: * ; | n | println ! ("{}" , n) }), input: Unpersist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: profiler :: tests :: * ; | v | v + 1 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: profiler :: tests :: * ; | v | v + 1 }), input: Persist( Source { source: Iter( - { use crate :: __staged :: profiler :: tests :: * ; 0 .. 10 }, + { use crate :: __staged :: rewrites :: profiler :: tests :: * ; 0 .. 10 }, ), location_kind: Process( 0, diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__properties__tests__property_optimized.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__properties__tests__property_optimized.snap similarity index 54% rename from hydroflow_plus/src/snapshots/hydroflow_plus__properties__tests__property_optimized.snap rename to hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__properties__tests__property_optimized.snap index 2975d05aa318..d793961df8ce 100644 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__properties__tests__property_optimized.snap +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__properties__tests__property_optimized.snap @@ -1,18 +1,18 @@ --- -source: hydroflow_plus/src/properties.rs +source: hydroflow_plus/src/rewrites/properties.rs expression: built.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: string :: String , i32) , () > ({ use crate :: __staged :: properties :: tests :: * ; | (string , count) | println ! ("{}: {}" , string , count) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: string :: String , i32) , () > ({ use crate :: __staged :: rewrites :: properties :: tests :: * ; | (string , count) | println ! ("{}: {}" , string , count) }), input: FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: properties :: tests :: * ; | | 0 }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , () , () > ({ use crate :: __staged :: properties :: tests :: * ; | count : & mut i32 , _ | * count += 1 }), + init: stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: rewrites :: properties :: tests :: * ; | | 0 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , () , () > ({ use crate :: __staged :: rewrites :: properties :: tests :: * ; | count : & mut i32 , _ | * count += 1 }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < std :: string :: String , (std :: string :: String , ()) > ({ use crate :: __staged :: properties :: tests :: * ; | string : String | (string , ()) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < std :: string :: String , (std :: string :: String , ()) > ({ use crate :: __staged :: rewrites :: properties :: tests :: * ; | string : String | (string , ()) }), input: Source { source: Iter( - { use crate :: __staged :: properties :: tests :: * ; vec ! [] }, + { use crate :: __staged :: rewrites :: properties :: tests :: * ; vec ! [] }, ), location_kind: Process( 0, diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee@surface_graph_0.snap b/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee@surface_graph_0.snap deleted file mode 100644 index 6e05ca49f735..000000000000 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_behind_tee@surface_graph_0.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: hydroflow_plus/src/persist_pullup.rs -expression: graph.surface_syntax_string() ---- -1v1 = source_iter ({ use crate :: __staged :: persist_pullup :: tests :: * ; 0 .. 10 }); -2v1 = tee (); -3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 })); -4v1 = persist :: < 'static > (); -5v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) })); -6v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 })); -7v1 = persist :: < 'static > (); -8v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) })); - -1v1 -> 2v1; -2v1 -> 3v1; -3v1 -> 4v1; -4v1 -> 5v1; -2v1 -> 6v1; -6v1 -> 7v1; -7v1 -> 8v1; diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map@surface_graph_0.snap b/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map@surface_graph_0.snap deleted file mode 100644 index 3974a6828245..000000000000 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__persist_pullup__tests__persist_pullup_through_map@surface_graph_0.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: hydroflow_plus/src/persist_pullup.rs -expression: graph.surface_syntax_string() ---- -1v1 = source_iter ({ use crate :: __staged :: persist_pullup :: tests :: * ; 0 .. 10 }); -2v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | v | v + 1 })); -3v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: persist_pullup :: tests :: * ; | n | println ! ("{}" , n) })); - -1v1 -> 2v1; -2v1 -> 3v1; diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__predicate_pushdown_through_map-2.snap b/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__predicate_pushdown_through_map-2.snap deleted file mode 100644 index c2d399c162f4..000000000000 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__predicate_pushdown_through_map-2.snap +++ /dev/null @@ -1,29 +0,0 @@ ---- -source: hydroflow_plus/src/profiler.rs -expression: "&pushed_down.ir" ---- -[ - ForEach { - f: { use crate :: __staged :: profiler :: tests :: * ; | n | println ! ("{}" , n) }, - input: Map { - f: { use crate :: __staged :: profiler :: * ; let counter_queue = Fake ; let my_id_copy1 = 0u32 ; let counters_copy1 = Fake ; let my_id_copy2 = 0u32 ; let counters_copy2 = Fake ; let my_id_copy3 = 0u32 ; let context = & context ; let my_id_copy4 = 0u32 ; let counters = Fake ; let my_id = 0u32 ; { counter_queue . borrow () . unbounded_send ((my_id_copy1 as usize , counters_copy1 . borrow () [my_id_copy2 as usize])) . unwrap () ; counters_copy2 . borrow_mut () [my_id_copy3 as usize] = 0 ; move | v | { hydroflow_plus :: profiler :: increment_counter (context . current_tick () , my_id_copy4 , & mut counters . borrow_mut () [my_id as usize] ,) ; v } } }, - input: Map { - f: { use crate :: __staged :: profiler :: tests :: * ; | v | v + 1 }, - input: Map { - f: { use crate :: __staged :: profiler :: * ; let counter_queue = Fake ; let my_id_copy1 = 1u32 ; let counters_copy1 = Fake ; let my_id_copy2 = 1u32 ; let counters_copy2 = Fake ; let my_id_copy3 = 1u32 ; let context = & context ; let my_id_copy4 = 1u32 ; let counters = Fake ; let my_id = 1u32 ; { counter_queue . borrow () . unbounded_send ((my_id_copy1 as usize , counters_copy1 . borrow () [my_id_copy2 as usize])) . unwrap () ; counters_copy2 . borrow_mut () [my_id_copy3 as usize] = 0 ; move | v | { hydroflow_plus :: profiler :: increment_counter (context . current_tick () , my_id_copy4 , & mut counters . borrow_mut () [my_id as usize] ,) ; v } } }, - input: Persist( - Map { - f: { use crate :: __staged :: profiler :: * ; let counter_queue = Fake ; let my_id_copy1 = 2u32 ; let counters_copy1 = Fake ; let my_id_copy2 = 2u32 ; let counters_copy2 = Fake ; let my_id_copy3 = 2u32 ; let context = & context ; let my_id_copy4 = 2u32 ; let counters = Fake ; let my_id = 2u32 ; { counter_queue . borrow () . unbounded_send ((my_id_copy1 as usize , counters_copy1 . borrow () [my_id_copy2 as usize])) . unwrap () ; counters_copy2 . borrow_mut () [my_id_copy3 as usize] = 0 ; move | v | { hydroflow_plus :: profiler :: increment_counter (context . current_tick () , my_id_copy4 , & mut counters . borrow_mut () [my_id as usize] ,) ; v } } }, - input: Source { - source: Iter( - { use crate :: __staged :: profiler :: tests :: * ; 0 .. 10 }, - ), - location_id: 0, - }, - }, - ), - }, - }, - }, - }, -] diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__predicate_pushdown_through_map.snap b/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__predicate_pushdown_through_map.snap deleted file mode 100644 index 9fdb9d06e7ff..000000000000 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__predicate_pushdown_through_map.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: hydroflow_plus/src/profiler.rs -expression: "&built.ir" ---- -[ - ForEach { - f: { use crate :: __staged :: profiler :: tests :: * ; | n | println ! ("{}" , n) }, - input: Map { - f: { use crate :: __staged :: profiler :: tests :: * ; | v | v + 1 }, - input: Persist( - Source { - source: Iter( - { use crate :: __staged :: profiler :: tests :: * ; 0 .. 10 }, - ), - location_id: 0, - }, - ), - }, - }, -] diff --git a/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__profiler_wrapping_all_operators-2.snap b/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__profiler_wrapping_all_operators-2.snap deleted file mode 100644 index b2a4cfe8e7a0..000000000000 --- a/hydroflow_plus/src/snapshots/hydroflow_plus__profiler__tests__profiler_wrapping_all_operators-2.snap +++ /dev/null @@ -1,26 +0,0 @@ ---- -source: hydroflow_plus/src/profiler.rs -expression: "&pushed_down.ir()" ---- -[ - ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: profiler :: tests :: * ; | n | println ! ("{}" , n) }), - input: Inspect { - f: { use crate :: __staged :: profiler :: * ; let counter_queue = Fake ; let counters = Fake ; let my_id = 0u32 ; { counter_queue . borrow () . unbounded_send ((my_id as usize , counters . borrow () [my_id as usize])) . unwrap () ; counters . borrow_mut () [my_id as usize] = 0 ; move | _ | { myself :: increment_counter (& mut counters . borrow_mut () [my_id as usize]) ; } } }, - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: profiler :: tests :: * ; | v | v + 1 }), - input: Inspect { - f: { use crate :: __staged :: profiler :: * ; let counter_queue = Fake ; let counters = Fake ; let my_id = 1u32 ; { counter_queue . borrow () . unbounded_send ((my_id as usize , counters . borrow () [my_id as usize])) . unwrap () ; counters . borrow_mut () [my_id as usize] = 0 ; move | _ | { myself :: increment_counter (& mut counters . borrow_mut () [my_id as usize]) ; } } }, - input: Source { - source: Iter( - { use crate :: __staged :: profiler :: tests :: * ; 0 .. 10 }, - ), - location_kind: Process( - 0, - ), - }, - }, - }, - }, - }, -] From 656ee328c8710bce7370c851437a80ca3db46a5a Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Wed, 6 Nov 2024 15:18:25 -0800 Subject: [PATCH 36/74] test: ignore trybuild tests inconsistent on latest nightly --- ...urce_iter_badtype.rs => surface_source_iter_badtype.rs.ignore} | 0 ...var_expr_missing_comma.rs => var_expr_missing_comma.rs.ignore} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename hydroflow/tests/compile-fail/{surface_source_iter_badtype.rs => surface_source_iter_badtype.rs.ignore} (100%) rename variadics/tests/compile-fail/{var_expr_missing_comma.rs => var_expr_missing_comma.rs.ignore} (100%) diff --git a/hydroflow/tests/compile-fail/surface_source_iter_badtype.rs b/hydroflow/tests/compile-fail/surface_source_iter_badtype.rs.ignore similarity index 100% rename from hydroflow/tests/compile-fail/surface_source_iter_badtype.rs rename to hydroflow/tests/compile-fail/surface_source_iter_badtype.rs.ignore diff --git a/variadics/tests/compile-fail/var_expr_missing_comma.rs b/variadics/tests/compile-fail/var_expr_missing_comma.rs.ignore similarity index 100% rename from variadics/tests/compile-fail/var_expr_missing_comma.rs rename to variadics/tests/compile-fail/var_expr_missing_comma.rs.ignore From 33c2de2717235be700a5269b3bd20c4aeb0362d3 Mon Sep 17 00:00:00 2001 From: Rohit Kulshreshtha Date: Wed, 6 Nov 2024 16:04:54 -0800 Subject: [PATCH 37/74] feat(Gossip KV): Cleanup Local and AWS Deployments (#1545) Closes #1529 (P0), #1528 (P1). Summary of changes: 1. Local Deployments use Terraform. 2. Both Local / AWS Terraform projects have been split into separate "infra" and "application" sections because there are intervening steps (building images, etc.) that need to be done before applications can be deployed. 3. Independent documentation for each local/AWS deployment - should make it less confusing because these are expected to diverge. --- datastores/gossip_kv/Makefile | 83 ---- datastores/gossip_kv/README.md | 37 +- datastores/gossip_kv/deployment/aws/Makefile | 76 +++ datastores/gossip_kv/deployment/aws/README.md | 89 ++++ .../seed_node_config.yaml} | 0 .../deployment/aws/terraform/.gitignore | 31 +- .../terraform/application/.terraform.lock.hcl | 22 + .../aws/terraform/application/main.tf | 447 ++++++++++++++++++ .../aws/terraform/application/outputs.tf | 9 + .../aws/terraform/application/variables.tf | 17 + .../aws/terraform/infra/.terraform.lock.hcl | 125 +++++ .../deployment/aws/terraform/infra/main.tf | 143 ++++++ .../deployment/aws/terraform/infra/outputs.tf | 28 ++ .../aws/terraform/infra/variables.tf | 17 + .../gossip_kv/deployment/local/Makefile | 46 ++ .../gossip_kv/deployment/local/README.md | 109 +++++ .../gossip_kv/deployment/local/objects.yaml | 90 ---- .../deployment/local/seed_node_config.yaml | 17 + .../deployment/local/terraform/.gitignore | 30 ++ .../terraform/application/.terraform.lock.hcl | 21 + .../local/terraform/application/main.tf | 153 ++++++ .../local/terraform/infra/.terraform.lock.hcl | 40 ++ .../deployment/local/terraform/infra/main.tf | 26 + .../local/terraform/infra/outputs.tf | 18 + datastores/gossip_kv/server/README.md | 44 -- 25 files changed, 1468 insertions(+), 250 deletions(-) delete mode 100644 datastores/gossip_kv/Makefile create mode 100644 datastores/gossip_kv/deployment/aws/Makefile create mode 100644 datastores/gossip_kv/deployment/aws/README.md rename datastores/gossip_kv/deployment/{local/updated_seed_node_config.yaml => aws/seed_node_config.yaml} (100%) create mode 100644 datastores/gossip_kv/deployment/aws/terraform/application/.terraform.lock.hcl create mode 100644 datastores/gossip_kv/deployment/aws/terraform/application/main.tf create mode 100644 datastores/gossip_kv/deployment/aws/terraform/application/outputs.tf create mode 100644 datastores/gossip_kv/deployment/aws/terraform/application/variables.tf create mode 100644 datastores/gossip_kv/deployment/aws/terraform/infra/.terraform.lock.hcl create mode 100644 datastores/gossip_kv/deployment/aws/terraform/infra/main.tf create mode 100644 datastores/gossip_kv/deployment/aws/terraform/infra/outputs.tf create mode 100644 datastores/gossip_kv/deployment/aws/terraform/infra/variables.tf create mode 100644 datastores/gossip_kv/deployment/local/Makefile create mode 100644 datastores/gossip_kv/deployment/local/README.md delete mode 100644 datastores/gossip_kv/deployment/local/objects.yaml create mode 100644 datastores/gossip_kv/deployment/local/seed_node_config.yaml create mode 100644 datastores/gossip_kv/deployment/local/terraform/.gitignore create mode 100644 datastores/gossip_kv/deployment/local/terraform/application/.terraform.lock.hcl create mode 100644 datastores/gossip_kv/deployment/local/terraform/application/main.tf create mode 100644 datastores/gossip_kv/deployment/local/terraform/infra/.terraform.lock.hcl create mode 100644 datastores/gossip_kv/deployment/local/terraform/infra/main.tf create mode 100644 datastores/gossip_kv/deployment/local/terraform/infra/outputs.tf delete mode 100644 datastores/gossip_kv/server/README.md diff --git a/datastores/gossip_kv/Makefile b/datastores/gossip_kv/Makefile deleted file mode 100644 index dbfe144f0d78..000000000000 --- a/datastores/gossip_kv/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -# Makefile - -## Minikube Options. -MINIKUBE_DISK_SIZE:=100g -MINIKUBE_CPUS:=16 -MINIKUBE_MEMORY:=32768 - -BASE_IMAGE_VERSION:=latest -SERVER_IMAGE_VERSION:=latest -CLI_IMAGE_VERSION:=latest -LOAD_TEST_IMAGE_VERSION:=latest - -# Docker Image Tags -BASE_IMAGE_TAG:=hydroflow-gossip-kv-base-image:$(BASE_IMAGE_VERSION) -SERVER_IMAGE_TAG:=hydroflow-gossip-kv-server:$(SERVER_IMAGE_VERSION) -CLI_IMAGE_TAG:=hydroflow-gossip-kv-cli:$(CLI_IMAGE_VERSION) -LOAD_TEST_IMAGE_TAG:=hydroflow-gossip-kv-load-test:$(LOAD_TEST_IMAGE_VERSION) - -AWS_TERRAFORM_PATH=../../datastores/gossip_kv/deployment/aws/terraform - -# Target to start Minikube with specific options -start_minikube: - minikube start --disk-size=$(MINIKUBE_DISK_SIZE) --cpus=$(MINIKUBE_CPUS) --memory=$(MINIKUBE_MEMORY) - @echo "Please run 'eval \$$(minikube docker-env)' to use the Minikube Docker daemon" - -# Target to build the Docker images -build_docker_images: build_base_image build_server_image build_cli_image build_load_test_image - -build_base_image: - docker build -t "$(BASE_IMAGE_TAG)" -f ../../datastores/gossip_kv/server/baseimage.Dockerfile ../.. - -build_server_image: - docker build -t "$(SERVER_IMAGE_TAG)" -f ../../datastores/gossip_kv/server/Dockerfile ../.. - -build_cli_image: - docker build -t "$(CLI_IMAGE_TAG)" -f ../../datastores/gossip_kv/cli/Dockerfile ../.. - -build_load_test_image: - docker build -t "$(LOAD_TEST_IMAGE_TAG)" -f ../../datastores/gossip_kv/load_test_server/Dockerfile ../.. - -# Target to clean up the Minikube cluster -clean_local: - minikube delete - -# Target to deploy the Gossip KV Server to the Minikube cluster -deploy_local: - kubectl apply -f ../../datastores/gossip_kv/server/local - -# Target to delete the Minikube cluster and build again -rebuild_local: clean_local start_minikube build_docker_images - -aws_terraform_init: - terraform -chdir="$(AWS_TERRAFORM_PATH)" init - -aws_terraform_apply: - terraform -chdir="$(AWS_TERRAFORM_PATH)" apply - -aws_setup_kubectl: - @echo "Setting up kubectl to work with AWS EKS Cluster" - aws eks update-kubeconfig --region $$(terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw region) --name $$(terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw cluster_name) - -aws_upload_docker_images: build_docker_images - $(eval SERVER_REPO_URL := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -json repository_urls | jq -r '.["gossip_kv_server"]')) - $(eval CLI_REPO_URL := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -json repository_urls | jq -r '.["gossip_kv_cli"]')) - $(eval LOAD_TEST_REPO_URL := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -json repository_urls | jq -r '.["gossip_kv_load_test"]')) - $(eval REGION := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw region)) - docker tag $(SERVER_IMAGE_TAG) $(SERVER_REPO_URL):$(SERVER_IMAGE_VERSION) - docker tag $(CLI_IMAGE_TAG) $(CLI_REPO_URL):$(CLI_IMAGE_VERSION) - docker tag $(LOAD_TEST_IMAGE_TAG) $(LOAD_TEST_REPO_URL):$(LOAD_TEST_IMAGE_VERSION) - aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(SERVER_REPO_URL) - docker push $(SERVER_REPO_URL):$(SERVER_IMAGE_VERSION) - aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(CLI_REPO_URL) - docker push $(CLI_REPO_URL):$(CLI_IMAGE_VERSION) - aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(LOAD_TEST_REPO_URL) - docker push $(LOAD_TEST_REPO_URL):$(LOAD_TEST_IMAGE_VERSION) - -aws_tunnel_grafana: - $(eval GRAFANA_PORT := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw grafana_port)) - kubectl port-forward svc/grafana $(GRAFANA_PORT):$(GRAFANA_PORT) - -aws_tunnel_prometheus: - $(eval PROMETHEUS_PORT := $(shell terraform -chdir=$(AWS_TERRAFORM_PATH) output -raw prometheus_port)) - kubectl port-forward svc/prometheus $(PROMETHEUS_PORT):$(PROMETHEUS_PORT) \ No newline at end of file diff --git a/datastores/gossip_kv/README.md b/datastores/gossip_kv/README.md index b282dafe0f1b..e798fedc52a1 100644 --- a/datastores/gossip_kv/README.md +++ b/datastores/gossip_kv/README.md @@ -48,36 +48,9 @@ The `sys` data section contains system data / state that is required by the key- ### `usr` Data -## Protocol +# Deployment +## Local (Minikube) Deployment +See [Minikube Deployment](./deployment/local/README.md) for more information. -## Checkpoints - -# Running Locally Using Minikube -## Install Docker Desktop -```shell -brew install --cask docker -``` -### Run docker (macOS) -``` -open -a Docker -``` - -## Install Minikube -Read more [here](https://minikube.sigs.k8s.io/docs/start/) -```shell -brew install minikube -``` - -## Start Minikube -```shell -minikube start -``` - -## Install `kubectl` -```shell -brew install kubectl -``` -## Configure Minikube to use your Docker Environment -```shell -eval $(minikube -p minikube docker-env) -``` \ No newline at end of file +## AWS Deployment +See [AWS Deployment](./deployment/aws/README.md) for more information. \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/aws/Makefile b/datastores/gossip_kv/deployment/aws/Makefile new file mode 100644 index 000000000000..1ac35ba5c8f8 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/Makefile @@ -0,0 +1,76 @@ +INFRA_PATH=./terraform/infra +APPLICATION_PATH=./terraform/application + +BASE_IMAGE_VERSION:=latest +SERVER_IMAGE_VERSION:=latest +CLI_IMAGE_VERSION:=latest +LOAD_TEST_IMAGE_VERSION:=latest + +# Docker Image Tags +BASE_IMAGE_TAG:=hydroflow-gossip-kv-base-image:$(BASE_IMAGE_VERSION) +SERVER_IMAGE_TAG:=hydroflow-gossip-kv-server:$(SERVER_IMAGE_VERSION) +CLI_IMAGE_TAG:=hydroflow-gossip-kv-cli:$(CLI_IMAGE_VERSION) +LOAD_TEST_IMAGE_TAG:=hydroflow-gossip-kv-load-test:$(LOAD_TEST_IMAGE_VERSION) + +.PHONY : init infra docker_images base_image server_image cli_image upload_docker_images application config clean + +init: + terraform -chdir="$(INFRA_PATH)" init + terraform -chdir="$(APPLICATION_PATH)" init + +infra: + terraform -chdir="$(INFRA_PATH)" apply -auto-approve + +kubectl_setup: + @echo "Setting up kubectl to work with AWS EKS Cluster" + aws eks update-kubeconfig --region $$(terraform -chdir=$(INFRA_PATH) output -raw region) --name $$(terraform -chdir=$(INFRA_PATH) output -raw cluster_name) + +docker_images: base_image server_image cli_image + +base_image: + docker build -t "$(BASE_IMAGE_TAG)" -f ../../../../datastores/gossip_kv/server/baseimage.Dockerfile ../../../.. + +server_image: + docker build -t "$(SERVER_IMAGE_TAG)" -f ../../../../datastores/gossip_kv/server/Dockerfile ../../../.. + +cli_image: + docker build -t "$(CLI_IMAGE_TAG)" -f ../../../../datastores/gossip_kv/cli/Dockerfile ../../../.. + +upload_docker_images: docker_images + $(eval SERVER_REPO_URL := $(shell terraform -chdir=$(INFRA_PATH) output -json repository_urls | jq -r '.["gossip_kv_server"]')) + $(eval CLI_REPO_URL := $(shell terraform -chdir=$(INFRA_PATH) output -json repository_urls | jq -r '.["gossip_kv_cli"]')) + $(eval LOAD_TEST_REPO_URL := $(shell terraform -chdir=$(INFRA_PATH) output -json repository_urls | jq -r '.["gossip_kv_load_test"]')) + $(eval REGION := $(shell terraform -chdir=$(INFRA_PATH) output -raw region)) + echo $(SERVER_REPO_URL) + docker tag $(SERVER_IMAGE_TAG) $(SERVER_REPO_URL):$(SERVER_IMAGE_VERSION) + docker tag $(CLI_IMAGE_TAG) $(CLI_REPO_URL):$(CLI_IMAGE_VERSION) + docker tag $(LOAD_TEST_IMAGE_TAG) $(LOAD_TEST_REPO_URL):$(LOAD_TEST_IMAGE_VERSION) + aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(SERVER_REPO_URL) + docker push $(SERVER_REPO_URL):$(SERVER_IMAGE_VERSION) + aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(CLI_REPO_URL) + docker push $(CLI_REPO_URL):$(CLI_IMAGE_VERSION) + aws ecr get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(LOAD_TEST_REPO_URL) + docker push $(LOAD_TEST_REPO_URL):$(LOAD_TEST_IMAGE_VERSION) + +application: + terraform -chdir="$(APPLICATION_PATH)" apply -auto-approve + +tunnel_grafana: + $(eval GRAFANA_PORT := $(shell terraform -chdir=$(APPLICATION_PATH) output -raw grafana_port)) + @echo "Grafana will be accessible at http://localhost:$(GRAFANA_PORT)" + kubectl port-forward svc/grafana $(GRAFANA_PORT):$(GRAFANA_PORT) + +tunnel_prometheus: + $(eval PROMETHEUS_PORT := $(shell terraform -chdir=$(APPLICATION_PATH) output -raw prometheus_port)) + @echo "Prometheus will be accessible at http://localhost:$(PROMETHEUS_PORT)" + kubectl port-forward svc/prometheus $(PROMETHEUS_PORT):$(PROMETHEUS_PORT) + + +config: + kubectl apply -f seed_node_config.yaml + +clean: + terraform -chdir="$(APPLICATION_PATH)" destroy -auto-approve + terraform -chdir="$(INFRA_PATH)" destroy -auto-approve + rm -rf $(INFRA_PATH)/.terraform $(INFRA_PATH)/terraform.tfstate $(INFRA_PATH)/terraform.tfstate.backup + rm -rf $(APPLICATION_PATH)/.terraform $(APPLICATION_PATH)/terraform.tfstate $(APPLICATION_PATH)/terraform.tfstate.backup diff --git a/datastores/gossip_kv/deployment/aws/README.md b/datastores/gossip_kv/deployment/aws/README.md new file mode 100644 index 000000000000..7c821f0003a7 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/README.md @@ -0,0 +1,89 @@ +# AWS Deployment + +## Pre-requisites +- [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli) +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) +- [Docker](https://docs.docker.com/get-docker/) +- [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +- [Make](https://www.gnu.org/software/make/) +- [k9s](https://k9scli.io/) + +### Mac Setup Using Homebrew +```shell +brew install terraform +brew install awscli +brew install docker +brew install kubernetes-cli +brew install k9s +``` + +## Initialize Setup +Initializes terraform providers required for local deployment. +```shell +make init +``` + +## Configure AWS Credentials +Make sure you have the AWS credentials configured. Subsequent terraform commands will use these credentials to create +the infrastructure. +```shell +aws configure +``` + +## Create Infrastructure +Creates a local kubernetes cluster using minikube. + +```shell +make infra +``` + +## Kubectl +To enable kubectl usage with the newly created AWS EKS cluster, use the following command. + +```shell +make kubectl_setup +``` + +## K9s +To monitor the cluster using k9s, use the following command. + +```shell +k9s +``` + +## Build & Upload Docker Image +Build and upload the docker image for the application, into ECR. +```shell +make upload_docker_images +``` + +## Create Application +```shell +make application +``` + +## Check if the application is running +```shell +kubectl get pods # All gossip-kv-* pods should show status as "Running" +```` + +## Access Grafana Dashboards +```shell +make tunnel_grafana +``` + +## Access Prometheus +```shell +make tunnel_prometheus +``` + +## Update seed node configuration +```shell +make config +``` + +## Clean +Destroys the AWS resources and cleans up terraform state. +```shell +make clean +``` \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/local/updated_seed_node_config.yaml b/datastores/gossip_kv/deployment/aws/seed_node_config.yaml similarity index 100% rename from datastores/gossip_kv/deployment/local/updated_seed_node_config.yaml rename to datastores/gossip_kv/deployment/aws/seed_node_config.yaml diff --git a/datastores/gossip_kv/deployment/aws/terraform/.gitignore b/datastores/gossip_kv/deployment/aws/terraform/.gitignore index 3fa8c86b7b04..e63962a88795 100644 --- a/datastores/gossip_kv/deployment/aws/terraform/.gitignore +++ b/datastores/gossip_kv/deployment/aws/terraform/.gitignore @@ -1 +1,30 @@ -.terraform +### TERRAFORM IGNORES ### +# Terraform Plugin and module directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +*.tfvars +*.tfvars.json + +# Ignore local override files +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Terraform CLI configuration files +.terraformrc +terraform.rc + +# Backup files created by Terraform during state edits +*.backup + +# Terraform plan files (these are generated and can be large) +*.tfplan diff --git a/datastores/gossip_kv/deployment/aws/terraform/application/.terraform.lock.hcl b/datastores/gossip_kv/deployment/aws/terraform/application/.terraform.lock.hcl new file mode 100644 index 000000000000..d3440c12f8c2 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/terraform/application/.terraform.lock.hcl @@ -0,0 +1,22 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.33.0" + constraints = ">= 2.32.0" + hashes = [ + "h1:HDyytvOlqNw5fJ0SB/nzgqCWniK4LAZNx23LaPavQq8=", + "zh:255b35790b706d405e987750190658dcaefb663741b96803a9529ba5d7435329", + "zh:362feba1aa820a8e02869ec71d1a08e87243dbce43671dc0995fa6c5a2fafa1d", + "zh:39332abcf75b5dd9c78c79c7c0c094f7d4ca908d1b76bbd2aae67e8e3516710c", + "zh:3e8e7f758bb09a9b5b613c8866e77541f8f00b521070cc86bc095ce61f010baf", + "zh:427883b889b9c36630c3eec4d5c07bc4ae12cc0d358fc17ea42a8049bf8d5275", + "zh:69bfc4ed067a5e4844db1a1809343652ff239aa0a8da089b1671524c44e8740a", + "zh:6b9f731062b945c5020e0930ed9a1b1b50afd2caf751f0e70a282d165c970979", + "zh:6faf9ec006af7ee7014a9c3251d65b701792abb823f149b0b7e4ac4433848201", + "zh:b706f76d695104a47682ee6ab842870f9c70a680f979fa9e7efe34278c0831bc", + "zh:b9bca48de2c92f57389ed58dd2fac564deaccd79a92cafd08edeed3ba6b91d4d", + "zh:bbd3336dbee5aed9880f98e36fb8340e0c6d8f0399a05787521af599ccb3dac4", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/datastores/gossip_kv/deployment/aws/terraform/application/main.tf b/datastores/gossip_kv/deployment/aws/terraform/application/main.tf new file mode 100644 index 000000000000..b707edaa89c4 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/terraform/application/main.tf @@ -0,0 +1,447 @@ +terraform { + backend "local" { + path = "../state/application.tfstate" + } + + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.32.0" + } + } +} + +data "terraform_remote_state" "infra" { + backend = "local" + config = { + path = "../state/infra.tfstate" + } +} + + +provider "kubernetes" { + host = data.terraform_remote_state.infra.outputs.cluster_endpoint + cluster_ca_certificate = base64decode(data.terraform_remote_state.infra.outputs.cluster_certificate_authority_data) + exec { + api_version = "client.authentication.k8s.io/v1beta1" + command = "aws" + args = [ + "eks", + "get-token", + "--cluster-name", + data.terraform_remote_state.infra.outputs.cluster_name, + ] + } +} + +resource "kubernetes_stateful_set" "gossip_kv_seed_nodes" { + metadata { + name = "gossip-kv-seed-nodes" + labels = { + app = "gossip-kv-seed-nodes" + } + } + + spec { + service_name = "gossip-kv-seed-nodes" + replicas = 1 + + selector { + match_labels = { + app = "gossip-kv-seed-nodes" + } + } + + template { + metadata { + labels = { + app = "gossip-kv-seed-nodes" + } + annotations = { + "prometheus.io/scrape" : "true" + "prometheus.io/port" : var.pod_monitoring_port + } + } + + spec { + termination_grace_period_seconds = 5 + + container { + name = "gossip-kv-server" + image = "${data.terraform_remote_state.infra.outputs.repository_urls.gossip_kv_server}:latest" + image_pull_policy = "Always" + + env { + name = "RUST_LOG" + value = "trace" + } + + env { + name = "RUST_BACKTRACE" + value = "full" + } + + port { + container_port = 3001 + protocol = "UDP" + } + + port { + container_port = var.pod_monitoring_port + protocol = "TCP" + } + + volume_mount { + name = "gossip-kv-dynamic-config" + mount_path = "/config/dynamic" + } + } + + volume { + name = "gossip-kv-dynamic-config" + + config_map { + name = "gossip-kv-dynamic-config" + } + } + } + } + } +} + +resource "kubernetes_deployment" "gossip_kv_cli" { + metadata { + name = "gossip-kv-cli" + labels = { + app = "gossip-kv-cli" + } + } + + spec { + replicas = 1 + + selector { + match_labels = { + app = "gossip-kv-cli" + } + } + + template { + metadata { + labels = { + app = "gossip-kv-cli" + } + } + + spec { + termination_grace_period_seconds = 5 + + container { + name = "gossip-kv-cli" + image = "${data.terraform_remote_state.infra.outputs.repository_urls.gossip_kv_cli}:latest" + image_pull_policy = "Always" + command = ["/bin/sh"] + args = ["-c", "while true; do sleep 3600; done"] + tty = true + + env { + name = "RUST_LOG" + value = "info" + } + } + } + } + } +} + +resource "kubernetes_service" "gossip_kv_seed_nodes" { + metadata { + name = "gossip-kv-seed-nodes" + labels = { + app = "gossip-kv-seed-nodes" + } + } + + spec { + cluster_ip = "None" + selector = { + app = "gossip-kv-seed-nodes" + } + + port { + port = 3001 + target_port = 3001 + protocol = "UDP" + } + } +} + +resource "kubernetes_config_map" "gossip_kv_dynamic_config" { + metadata { + name = "gossip-kv-dynamic-config" + } + + data = { + "dynamic.toml" = < repo} + repository_name = each.value + + repository_read_write_access_arns = [data.aws_caller_identity.current.arn] + repository_lifecycle_policy = jsonencode({ + rules = [ + { + rulePriority = 1, + description = "Keep last 30 images", + selection = { + tagStatus = "tagged", + tagPrefixList = ["v"], + countType = "imageCountMoreThan", + countNumber = 30 + }, + action = { + type = "expire" + } + } + ] + }) + + repository_image_tag_mutability = "MUTABLE" + tags = { + Terraform = "true" + Environment = "dev" + } +} \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/aws/terraform/infra/outputs.tf b/datastores/gossip_kv/deployment/aws/terraform/infra/outputs.tf new file mode 100644 index 000000000000..f965d56a2157 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/terraform/infra/outputs.tf @@ -0,0 +1,28 @@ +output "cluster_endpoint" { + description = "Endpoint for EKS control plane" + value = module.eks_cluster.cluster_endpoint +} + +output "cluster_name" { + description = "Kubernetes Cluster Name" + value = module.eks_cluster.cluster_name +} + +output "vpc_id" { + value = module.vpc.vpc_id +} + +output "region" { + description = "AWS region" + value = var.region +} + +output "repository_urls" { + description = "URLs of all ECR repositories created" + value = { for repo, details in module.ecr : repo => details.repository_url } +} + +output "cluster_certificate_authority_data" { + description = "Base64 encoded PEM certificate data for the cluster" + value = module.eks_cluster.cluster_certificate_authority_data +} \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/aws/terraform/infra/variables.tf b/datastores/gossip_kv/deployment/aws/terraform/infra/variables.tf new file mode 100644 index 000000000000..7522b20fcc27 --- /dev/null +++ b/datastores/gossip_kv/deployment/aws/terraform/infra/variables.tf @@ -0,0 +1,17 @@ +variable "region" { + description = "AWS region" + type = string + default = "us-east-2" +} + +variable "instance_type" { + description = "Instance type for the EKS nodes" + type = string + default = "t3.small" +} + +variable "ecr_repositories" { + description = "List of ECR repository names" + type = list(string) + default = ["gossip_kv_server", "gossip_kv_cli", "gossip_kv_load_test"] +} \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/local/Makefile b/datastores/gossip_kv/deployment/local/Makefile new file mode 100644 index 000000000000..ee030a22207a --- /dev/null +++ b/datastores/gossip_kv/deployment/local/Makefile @@ -0,0 +1,46 @@ + +INFRA_PATH=./terraform/infra +APPLICATION_PATH=./terraform/application + +BASE_IMAGE_VERSION:=latest +SERVER_IMAGE_VERSION:=latest +CLI_IMAGE_VERSION:=latest +LOAD_TEST_IMAGE_VERSION:=latest + +# Docker Image Tags +BASE_IMAGE_TAG:=hydroflow-gossip-kv-base-image:$(BASE_IMAGE_VERSION) +SERVER_IMAGE_TAG:=hydroflow-gossip-kv-server:$(SERVER_IMAGE_VERSION) +CLI_IMAGE_TAG:=hydroflow-gossip-kv-cli:$(CLI_IMAGE_VERSION) +LOAD_TEST_IMAGE_TAG:=hydroflow-gossip-kv-load-test:$(LOAD_TEST_IMAGE_VERSION) + +.PHONY : init infra docker_images base_image server_image cli_image application config clean + +init: + terraform -chdir="$(INFRA_PATH)" init + terraform -chdir="$(APPLICATION_PATH)" init + +infra: + terraform -chdir="$(INFRA_PATH)" apply -auto-approve + +docker_images: base_image server_image cli_image + +base_image: + docker build -t "$(BASE_IMAGE_TAG)" -f ../../../../datastores/gossip_kv/server/baseimage.Dockerfile ../../../.. + +server_image: + docker build -t "$(SERVER_IMAGE_TAG)" -f ../../../../datastores/gossip_kv/server/Dockerfile ../../../.. + +cli_image: + docker build -t "$(CLI_IMAGE_TAG)" -f ../../../../datastores/gossip_kv/cli/Dockerfile ../../../.. + +application: + terraform -chdir="$(APPLICATION_PATH)" apply -auto-approve + +config: + kubectl apply -f seed_node_config.yaml + +clean: + terraform -chdir="$(APPLICATION_PATH)" destroy -auto-approve + terraform -chdir="$(INFRA_PATH)" destroy -auto-approve + rm -rf $(INFRA_PATH)/.terraform $(INFRA_PATH)/terraform.tfstate $(INFRA_PATH)/terraform.tfstate.backup + rm -rf $(APPLICATION_PATH)/.terraform $(APPLICATION_PATH)/terraform.tfstate $(APPLICATION_PATH)/terraform.tfstate.backup diff --git a/datastores/gossip_kv/deployment/local/README.md b/datastores/gossip_kv/deployment/local/README.md new file mode 100644 index 000000000000..e45c622f9280 --- /dev/null +++ b/datastores/gossip_kv/deployment/local/README.md @@ -0,0 +1,109 @@ +# Local (Minikube) Deployment + +## Pre-requisites +- [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli) +- [Minikube](https://minikube.sigs.k8s.io/docs/start/) +- [Docker](https://docs.docker.com/get-docker/) +- [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +- [Make](https://www.gnu.org/software/make/) +- [k9s](https://k9scli.io/) + +### Mac Setup Using Homebrew +```shell +brew install terraform +brew install minikube +brew install docker +brew install kubernetes-cli +brew install k9s +``` + +## Initialize Setup +Initializes terraform providers required for local deployment. +```shell +make init +``` + +## Create Infrastructure +Creates a local kubernetes cluster using minikube. + +```shell +make infra +``` +### List Minikube Profiles +The `make infra` command creates minikube profile 'terraform-provider-minikube'. The details of the profile can be +listed using the following command. +```shell +minikube profile list +``` +Other minikube commands can be specified using the profile name as shown below. +```shell +minikube --profile=terraform-provider-minikube +``` + +### Kubectl +The kubectl context is set to the minikube profile 'terraform-provider-minikube' by default. + +To switch back to the 'terraform-provider-minikube' context at anytime, use the following command. +```shell +kubectl config use-context terraform-provider-minikube +``` + +### K9s +To monitor the cluster using k9s, use the following command. + +```shell +k9s +``` + +If the current context is not set to `terraform-provider-minikube`, use the following command. +```shell +k9s --context terraform-provider-minikube +``` + +### Build Docker Image +Setup the docker environment to use the minikube docker registry. +```shell +eval $(minikube -p terraform-provider-minikube docker-env) +``` + +Build the docker image for the application, into the local minikube docker registry. +```shell +make docker_images +``` +You can view the docker images in the minikube registry using the following command. +```shell +docker images +``` + +If docker isn't pointing to the minikube registry, use the following command. +```shell +minikube -p terraform-provider-minikube ssh docker images +``` + +## Create Application +```shell +make application +``` + +### Check if the application is running +```shell +kubectl get pods # All gossip-kv-* pods should show status as "Running" +```` + +### Update seed node configuration +```shell +make config +``` + +## Clean + +Destroys the mini-kube cluster and removes the terraform state files. + +```shell +make clean +``` + +If terraform is an inconsistent state, use the following command to blow away the minikube cluster and start fresh. +```shell +minikube delete -p terraform-provider-minikube +``` \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/local/objects.yaml b/datastores/gossip_kv/deployment/local/objects.yaml deleted file mode 100644 index 7421df268e1e..000000000000 --- a/datastores/gossip_kv/deployment/local/objects.yaml +++ /dev/null @@ -1,90 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: gossip-kv-seed-nodes - labels: - app: gossip-kv-seed-nodes -spec: - replicas: 3 - serviceName: gossip-kv-seed-nodes - selector: - matchLabels: - app: gossip-kv-seed-nodes - template: - metadata: - labels: - app: gossip-kv-seed-nodes - spec: - terminationGracePeriodSeconds: 5 # Really aggressive, but makes teardown faster. Not recommended beyond benchmarking. - containers: - - name: gossip-kv-server - image: docker.io/hydroflow/gossip-kv-server:latest - imagePullPolicy: IfNotPresent -# Uncomment the following for debugging -# command: [ "/bin/sh" ] -# args: [ "-c", "while true; do sleep 3600; done" ] - env: - - name: RUST_LOG - value: "trace" - - name: RUST_BACKTRACE - value: "full" - ports: - - containerPort: 3001 - protocol: UDP - volumeMounts: - - name: gossip-kv-dynamic-config - mountPath: /config/dynamic - volumes: - - name: gossip-kv-dynamic-config - configMap: - name: gossip-kv-dynamic-config ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: gossip-kv-cli - labels: - app: gossip-kv-cli -spec: - replicas: 1 - selector: - matchLabels: - app: gossip-kv-cli - template: - metadata: - labels: - app: gossip-kv-cli - spec: - terminationGracePeriodSeconds: 5 # Really aggressive, but makes teardown faster. Not recommended beyond benchmarking. - containers: - - name: gossip-kv-cli - image: docker.io/hydroflow/gossip-kv-cli:latest - imagePullPolicy: IfNotPresent - command: ["/bin/sh"] - args: ["-c", "while true; do sleep 3600; done"] - tty: true - env: - - name: RUST_LOG - value: "info" ---- -apiVersion: v1 -kind: Service -metadata: - name: gossip-kv-seed-nodes - labels: - app: gossip-kv-seed-nodes -spec: - ports: - - port: 3001 - targetPort: 3001 - protocol: UDP - clusterIP: None - selector: - app: gossip-kv-seed-nodes ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: gossip-kv-dynamic-config -data: - dynamic.toml: | \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/local/seed_node_config.yaml b/datastores/gossip_kv/deployment/local/seed_node_config.yaml new file mode 100644 index 000000000000..bebeda8cb5e4 --- /dev/null +++ b/datastores/gossip_kv/deployment/local/seed_node_config.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: gossip-kv-dynamic-config +data: + dynamic.toml: | + [[seed_nodes]] + id = "gossip-kv-seed-nodes-0" + address = "gossip-kv-seed-nodes-0.gossip-kv-seed-nodes.default.svc.cluster.local:3000" + + [[seed_nodes]] + id = "gossip-kv-seed-nodes-1" + address = "gossip-kv-seed-nodes-1.gossip-kv-seed-nodes.default.svc.cluster.local:3000" + + [[seed_nodes]] + id = "gossip-kv-seed-nodes-2" + address = "gossip-kv-seed-nodes-2.gossip-kv-seed-nodes.default.svc.cluster.local:3000" \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/local/terraform/.gitignore b/datastores/gossip_kv/deployment/local/terraform/.gitignore new file mode 100644 index 000000000000..e63962a88795 --- /dev/null +++ b/datastores/gossip_kv/deployment/local/terraform/.gitignore @@ -0,0 +1,30 @@ +### TERRAFORM IGNORES ### +# Terraform Plugin and module directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +*.tfvars +*.tfvars.json + +# Ignore local override files +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Terraform CLI configuration files +.terraformrc +terraform.rc + +# Backup files created by Terraform during state edits +*.backup + +# Terraform plan files (these are generated and can be large) +*.tfplan diff --git a/datastores/gossip_kv/deployment/local/terraform/application/.terraform.lock.hcl b/datastores/gossip_kv/deployment/local/terraform/application/.terraform.lock.hcl new file mode 100644 index 000000000000..0bc31d25bae2 --- /dev/null +++ b/datastores/gossip_kv/deployment/local/terraform/application/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.33.0" + hashes = [ + "h1:HDyytvOlqNw5fJ0SB/nzgqCWniK4LAZNx23LaPavQq8=", + "zh:255b35790b706d405e987750190658dcaefb663741b96803a9529ba5d7435329", + "zh:362feba1aa820a8e02869ec71d1a08e87243dbce43671dc0995fa6c5a2fafa1d", + "zh:39332abcf75b5dd9c78c79c7c0c094f7d4ca908d1b76bbd2aae67e8e3516710c", + "zh:3e8e7f758bb09a9b5b613c8866e77541f8f00b521070cc86bc095ce61f010baf", + "zh:427883b889b9c36630c3eec4d5c07bc4ae12cc0d358fc17ea42a8049bf8d5275", + "zh:69bfc4ed067a5e4844db1a1809343652ff239aa0a8da089b1671524c44e8740a", + "zh:6b9f731062b945c5020e0930ed9a1b1b50afd2caf751f0e70a282d165c970979", + "zh:6faf9ec006af7ee7014a9c3251d65b701792abb823f149b0b7e4ac4433848201", + "zh:b706f76d695104a47682ee6ab842870f9c70a680f979fa9e7efe34278c0831bc", + "zh:b9bca48de2c92f57389ed58dd2fac564deaccd79a92cafd08edeed3ba6b91d4d", + "zh:bbd3336dbee5aed9880f98e36fb8340e0c6d8f0399a05787521af599ccb3dac4", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/datastores/gossip_kv/deployment/local/terraform/application/main.tf b/datastores/gossip_kv/deployment/local/terraform/application/main.tf new file mode 100644 index 000000000000..c9e439b31ea0 --- /dev/null +++ b/datastores/gossip_kv/deployment/local/terraform/application/main.tf @@ -0,0 +1,153 @@ +terraform { + backend "local" { + path = "../state/application.tfstate" + } +} + +data "terraform_remote_state" "infra" { + backend = "local" + config = { + path = "../state/infra.tfstate" + } +} + +provider "kubernetes" { + host = data.terraform_remote_state.infra.outputs.kubernetes_host + client_certificate = data.terraform_remote_state.infra.outputs.kubernetes_client_certificate + client_key = data.terraform_remote_state.infra.outputs.kubernetes_client_key + cluster_ca_certificate = data.terraform_remote_state.infra.outputs.kubernetes_cluster_ca_certificate +} + +resource "kubernetes_stateful_set" "gossip_kv_seed_nodes" { + metadata { + name = "gossip-kv-seed-nodes" + labels = { + app = "gossip-kv-seed-nodes" + } + } + + spec { + replicas = 3 + service_name = "gossip-kv-seed-nodes" + + selector { + match_labels = { + app = "gossip-kv-seed-nodes" + } + } + + template { + metadata { + labels = { + app = "gossip-kv-seed-nodes" + } + } + spec { + termination_grace_period_seconds = 5 # Allows for quick restarts. Not recommended for production. + + container { + name = "gossip-kv-server" + image = "docker.io/hydroflow-gossip-kv-server:latest" + image_pull_policy = "IfNotPresent" + env { + name = "RUST_LOG" + value = "trace" + } + env { + name = "RUST_BACKTRACE" + value = "full" + } + port { + container_port = 3001 + protocol = "UDP" + } + volume_mount { + name = "gossip-kv-dynamic-config" + mount_path = "/config/dynamic" + } + } + + volume { + name = "gossip-kv-dynamic-config" + config_map { + name = "gossip-kv-dynamic-config" + } + } + } + } + } +} + +resource "kubernetes_deployment" "gossip_kv_cli" { + metadata { + name = "gossip-kv-cli" + labels = { + app = "gossip-kv-cli" + } + } + + spec { + replicas = 1 + + selector { + match_labels = { + app = "gossip-kv-cli" + } + } + + template { + metadata { + labels = { + app = "gossip-kv-cli" + } + } + spec { + termination_grace_period_seconds = 5 + container { + name = "gossip-kv-cli" + image = "docker.io/hydroflow-gossip-kv-cli:latest" + image_pull_policy = "IfNotPresent" + command = ["/bin/sh"] + args = ["-c", "while true; do sleep 3600; done"] + tty = true + env { + name = "RUST_LOG" + value = "info" + } + } + } + } + } +} + +resource "kubernetes_service" "gossip_kv_seed_nodes" { + metadata { + name = "gossip-kv-seed-nodes" + labels = { + app = "gossip-kv-seed-nodes" + } + } + + spec { + port { + port = 3001 + target_port = 3001 + protocol = "UDP" + } + cluster_ip = "None" + selector = { + app = "gossip-kv-seed-nodes" + } + } +} + + +resource "kubernetes_config_map" "gossip_kv_dynamic_config" { + metadata { + name = "gossip-kv-dynamic-config" + } + + data = { + "dynamic.toml" = "" + } +} \ No newline at end of file diff --git a/datastores/gossip_kv/deployment/local/terraform/infra/.terraform.lock.hcl b/datastores/gossip_kv/deployment/local/terraform/infra/.terraform.lock.hcl new file mode 100644 index 000000000000..fc26421f157d --- /dev/null +++ b/datastores/gossip_kv/deployment/local/terraform/infra/.terraform.lock.hcl @@ -0,0 +1,40 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.33.0" + hashes = [ + "h1:HDyytvOlqNw5fJ0SB/nzgqCWniK4LAZNx23LaPavQq8=", + "zh:255b35790b706d405e987750190658dcaefb663741b96803a9529ba5d7435329", + "zh:362feba1aa820a8e02869ec71d1a08e87243dbce43671dc0995fa6c5a2fafa1d", + "zh:39332abcf75b5dd9c78c79c7c0c094f7d4ca908d1b76bbd2aae67e8e3516710c", + "zh:3e8e7f758bb09a9b5b613c8866e77541f8f00b521070cc86bc095ce61f010baf", + "zh:427883b889b9c36630c3eec4d5c07bc4ae12cc0d358fc17ea42a8049bf8d5275", + "zh:69bfc4ed067a5e4844db1a1809343652ff239aa0a8da089b1671524c44e8740a", + "zh:6b9f731062b945c5020e0930ed9a1b1b50afd2caf751f0e70a282d165c970979", + "zh:6faf9ec006af7ee7014a9c3251d65b701792abb823f149b0b7e4ac4433848201", + "zh:b706f76d695104a47682ee6ab842870f9c70a680f979fa9e7efe34278c0831bc", + "zh:b9bca48de2c92f57389ed58dd2fac564deaccd79a92cafd08edeed3ba6b91d4d", + "zh:bbd3336dbee5aed9880f98e36fb8340e0c6d8f0399a05787521af599ccb3dac4", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/scott-the-programmer/minikube" { + version = "0.4.2" + constraints = "0.4.2" + hashes = [ + "h1:kUXMe9QKg/SS9WI36FRVK31z3gj59DaljIJKO0cHH68=", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:74f33b5a5ca42defdf44a2e6c4e08336a353c514f6c37f2ff41b5471324f5b03", + "zh:7f2fa2bcc6bde40098647fb171c0fd952a4d9146ab3df113f16b50565359b4fb", + "zh:88e01b8a9615d2c9ee8b0b34398ed5b00617ba4581edb2ca50a18608a9642072", + "zh:89384deab32270c985f8d0f0e762b483ec37339900ef600156b89ea10f45c77b", + "zh:9164e86a0200cf2189ed939a554d1156a1acff67c3f79dfc296d1b9226d1e814", + "zh:ab0cb857884de38dcce31b66b112f5a886ef5a85abbaa6e96af3c23f312947af", + "zh:bd4fd61701bbc4b6ede08612c7e944813e0948887d666fa0ff64186e97e93c22", + "zh:cf48a09a4c92d77f373ea359415087891788ed11a2537000c79600c05524bd26", + "zh:fedc68ccc3f1b1447f9ed97e486f30ed526f9e098854dc18f9fc9b26f2f5ec43", + "zh:ff97171d251fb6658c729676c62b2315bcc3b056115992dbb19737e6ba550f0d", + ] +} diff --git a/datastores/gossip_kv/deployment/local/terraform/infra/main.tf b/datastores/gossip_kv/deployment/local/terraform/infra/main.tf new file mode 100644 index 000000000000..5262f27c06f7 --- /dev/null +++ b/datastores/gossip_kv/deployment/local/terraform/infra/main.tf @@ -0,0 +1,26 @@ +terraform { + backend "local" { + path = "../state/infra.tfstate" + } + + required_providers { + minikube = { + source = "scott-the-programmer/minikube" + version = "0.4.2" + } + } +} + +resource "minikube_cluster" "gossip_kv" { + driver = "docker" + cpus = 16 + memory = 32768 + disk_size = "100g" +} + +provider "kubernetes" { + host = minikube_cluster.gossip_kv.host + client_certificate = minikube_cluster.gossip_kv.client_certificate + client_key = minikube_cluster.gossip_kv.client_key + cluster_ca_certificate = minikube_cluster.gossip_kv.cluster_ca_certificate +} diff --git a/datastores/gossip_kv/deployment/local/terraform/infra/outputs.tf b/datastores/gossip_kv/deployment/local/terraform/infra/outputs.tf new file mode 100644 index 000000000000..07550ca955eb --- /dev/null +++ b/datastores/gossip_kv/deployment/local/terraform/infra/outputs.tf @@ -0,0 +1,18 @@ +output "kubernetes_host" { + value = minikube_cluster.gossip_kv.host +} + +output "kubernetes_client_certificate" { + value = minikube_cluster.gossip_kv.client_certificate + sensitive = true +} + +output "kubernetes_client_key" { + value = minikube_cluster.gossip_kv.client_key + sensitive = true +} + +output "kubernetes_cluster_ca_certificate" { + value = minikube_cluster.gossip_kv.cluster_ca_certificate + sensitive = true +} \ No newline at end of file diff --git a/datastores/gossip_kv/server/README.md b/datastores/gossip_kv/server/README.md deleted file mode 100644 index 434315e29f4f..000000000000 --- a/datastores/gossip_kv/server/README.md +++ /dev/null @@ -1,44 +0,0 @@ -From the `hydroflow` directory, run - -## Minikube - -### Start Minikube -Disk allocation is done by the driver used to create the VM. Setting this to a high value will do nothing if the -driver isn't correctly configured. You'll only notice that hydroflow runs out of disk space while compiling. -For Docker, the disk size is set in the Docker Desktop settings. Also, provide as many CPUs here as possible, since -building the code is CPU-intensive. -```shell -minikube start --disk-size=100g --cpus=16 --memory=32768 -``` - -### Use the Docker daemon from minikube -```shell -eval $(minikube docker-env) -``` - -## Build Docker Base Image -Speeds up code changes by caching build dependencies. -```shell -docker build -t "hydroflow/gossip-kv-server-base-image:latest" -f datastores/gossip_kv/server/baseimage.Dockerfile . -``` - -## Build Docker Image for Gossip Server -```shell -docker build -t "hydroflow/gossip-kv-server:latest" -f datastores/gossip_kv/server/Dockerfile . -``` - -## Build Docker Image for Gossip CLI -```shell -docker build -t "hydroflow/gossip-kv-cli:latest" -f datastores/gossip_kv/cli/Dockerfile . -``` - -## Check if minikube has the image -You should see "hydroflow/gossip-kv" -```shell -minikube image ls --format tablemin -``` - -## Deploy to Minikube -```shell -kubectl apply -f datastores/gossip_kv/server/deployment/local/objects.yaml -``` \ No newline at end of file From f7b95594dc143acdf5e64f4cf77f5ae784f2683c Mon Sep 17 00:00:00 2001 From: Rohit Kulshreshtha Date: Thu, 7 Nov 2024 09:27:35 -0800 Subject: [PATCH 38/74] chore(Gossip KV): Update main README.md (#1546) fixes #1531. --- datastores/gossip_kv/README.md | 83 +++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/datastores/gossip_kv/README.md b/datastores/gossip_kv/README.md index e798fedc52a1..76a1108da080 100644 --- a/datastores/gossip_kv/README.md +++ b/datastores/gossip_kv/README.md @@ -1,56 +1,87 @@ # Gossip Key-Value Store # Architecture -A gossip-based key-value store library. +The Gossip Key-Value Store is a distributed key-value store that uses a gossip protocol to replicate data across all +members of the cluster. The key-value store is eventually consistent and is designed to be used in scenarios where +strict consistency is not a hard requirement. + +## Data Model +Data stored in the key-value store is modelled as three level hierarchy `Namespace > Table > Rows`. ``` -┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐ -│ Process │ │ Process │ │ Process │ │ Process │ -│ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │ -│ │ User Application │ │ │ │ User Application │ │ │ │ User Application │ │ │ │ User Application │ │ -│ └────────▲──┬─────────┘ │ │ └─────────▲─┬─────────┘ │ │ └─────────▲─┬─────────┘ │ │ └─────────▲─┬─────────┘ │ -│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ -│ ┌────────┴──▼─────────┐ │ │ ┌─────────┴─▼─────────┐ │ │ ┌─────────┴─▼─────────┐ │ │ ┌─────────┴─▼─────────┐ │ -│ │ Gossip KV Store │ │◄──►│ │ Gossip KV Store │ │◄──►│ │ Gossip KV Store │ │◄──►│ │ Gossip KV Store │ │ -│ └─────────────────────┘ │ │ └─────────────────────┘ │ │ └─────────────────────┘ │ │ └─────────────────────┘ │ -└─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘ +┌──────────────┐ +│ Namespace │ +└─┬────────────┘ + │ ┌──────────────┐ + └────┤ Table │ + └─┬────────────┘ + │ ┌──────────────┐ + └────┤ Row │ + └──────────────┘ ``` -## Data Model -TODO: Elaborate -* User Application manipulate data using client library -* Replicated to all members of the gossip cluster -* Eventually consistent +Represented as JSON, the data model looks like this: ```json { "sys": { "members": { "member_id": { - "port": 1234, - "protocol": "v1" + "protocols": [ + { + "name": "gossip", + "endpoint": "..." + }, + { + "name": "...", + "endpoint": "..." + } + ] } } }, "usr": { - "key 1": "value 1", - "key 2": "value 2" + "table_A": { + "key_1": "value_1", + "key_2": "value_2" + }, + "table_B": { + "key_1": "value_1", + "key_2": "value_2" + } } } ``` -Data in divided into two sections: A `sys` section that contains system data used by the key-value store itself. The -`usr` section contains user-defined data. +Note that JSON is used here for illustrative purposes only - it isn't the actual data format used by the key-value +store. + +### Namespace +A namespace is a group of tables. There are only two namespaces in the key-value store: `sys` and `usr`. The `sys` +namespace is reserved for system data and is used by the key-value store itself. The `usr` namespace is used for user +data. ### `sys` Data -The `sys` data section contains system data / state that is required by the key-value store to do it's work. -#### Fields +### Members +The members table contains information about all the members in the cluster. Each member is identified by a unique +`member_id`. + +#### Protocols +Each member exposes a set of protocols and endpoint that can be used to communicate with it. For example, every member +exposes the `gossip` protocol which is used by the key-value store to replicate data across the cluster. ### `usr` Data +The `usr` namespace is used to store user data. The data is stored in tables and rows. Each table is a collection of rows +where each row is a key-value pair. Values are stored as strings and are not interpreted by the key-value store. + +# Unit Testing +A deterministic unit test suite is provided for the key-value store and the replication protocols. See the unit tests +on [server.rs](./kv/server.rs) for more. # Deployment ## Local (Minikube) Deployment -See [Minikube Deployment](./deployment/local/README.md) for more information. +For local development and testing, the key-value store can be deployed on Minikube. See [Minikube Deployment](./deployment/local/README.md) +for more information. ## AWS Deployment -See [AWS Deployment](./deployment/aws/README.md) for more information. \ No newline at end of file +See [AWS Deployment](./deployment/aws/README.md) for more information. \ No newline at end of file From b43e6c76bf66ee7c5fdf42dd100d5ec384a7a6ba Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Thu, 7 Nov 2024 12:33:24 -0800 Subject: [PATCH 39/74] ci: increase timeout for CI for Build CLI (Windows), fix #1547 (#1549) `Compiling pythonize v0.20.0` seems to take a long time Took 36m 46s for the record, https://github.com/MingweiSamuel/hydroflow/actions/runs/11728824729/job/32673130852 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 305c9cf9eed7..7af071b226a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,7 +125,7 @@ jobs: test: name: Test Suite if: ${{ needs.pre_job.outputs.should_skip != 'true' || github.event_name != 'pull_request' }} - timeout-minutes: 35 + timeout-minutes: 50 needs: pre_job runs-on: ${{ matrix.os }} strategy: @@ -199,7 +199,7 @@ jobs: source .venv/bin/activate pip install maturin maturin develop - + - name: Build CLI (Windows) if: ${{ matrix.os == 'windows-latest' }} run: | From 5d5209b4a5556618d8a8c8219e1e2a4e837256ef Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 7 Nov 2024 22:34:17 -0800 Subject: [PATCH 40/74] feat(hydroflow_plus)!: add an explicit API for creating tick contexts (#1550) Previously, each location had a (semantic) global clock that drives ticks, and so all streams in a tick domain were all in the same atomic block. For future optimizations, we'd like developers to be able to place streams on the same location into different clocks to eliminate synchronization between them, which in turn would allow the computations in those separate clocks to be potentially decoupled across machines. --- hydroflow_plus/src/builder/mod.rs | 4 + hydroflow_plus/src/ir.rs | 10 +- hydroflow_plus/src/location/mod.rs | 171 +++--------------- hydroflow_plus/src/location/tick.rs | 168 ++++++++++++++++- hydroflow_plus/src/optional.rs | 19 +- hydroflow_plus/src/rewrites/persist_pullup.rs | 3 +- hydroflow_plus/src/rewrites/properties.rs | 3 +- hydroflow_plus/src/singleton.rs | 17 +- hydroflow_plus/src/stream.rs | 15 +- hydroflow_plus_test/src/cluster/compute_pi.rs | 1 + hydroflow_plus_test/src/cluster/map_reduce.rs | 6 +- hydroflow_plus_test/src/cluster/paxos.rs | 87 ++++++--- .../src/cluster/paxos_bench.rs | 16 +- hydroflow_plus_test/src/cluster/paxos_kv.rs | 10 +- .../src/cluster/simple_cluster.rs | 2 +- ...ter__compute_pi__tests__compute_pi_ir.snap | 4 +- ..._tests__compute_pi_ir@surface_graph_0.snap | 4 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 154 +++++++++++----- hydroflow_plus_test/src/cluster/two_pc.rs | 2 +- .../src/local/chat_app.rs | 10 +- .../src/local/compute_pi.rs | 3 +- .../src/local/count_elems.rs | 3 +- .../src/local/negation.rs | 10 +- .../src/local/teed_join.rs | 3 +- 24 files changed, 437 insertions(+), 288 deletions(-) diff --git a/hydroflow_plus/src/builder/mod.rs b/hydroflow_plus/src/builder/mod.rs index 5b9fabc52428..a15cea44dd66 100644 --- a/hydroflow_plus/src/builder/mod.rs +++ b/hydroflow_plus/src/builder/mod.rs @@ -24,6 +24,9 @@ pub struct FlowStateInner { /// Counters for generating identifiers for cycles. pub(crate) cycle_counts: HashMap, + + /// Counters for clock IDs. + pub(crate) next_clock_id: usize, } pub type FlowState = Rc>; @@ -73,6 +76,7 @@ impl<'a> FlowBuilder<'a> { leaves: Some(vec![]), next_external_out: 0, cycle_counts: HashMap::new(), + next_clock_id: 0, })), nodes: RefCell::new(vec![]), clusters: RefCell::new(vec![]), diff --git a/hydroflow_plus/src/ir.rs b/hydroflow_plus/src/ir.rs index 557eb8076e65..8f18bf552c86 100644 --- a/hydroflow_plus/src/ir.rs +++ b/hydroflow_plus/src/ir.rs @@ -186,9 +186,10 @@ impl HfPlusLeaf { let (input_ident, input_location_id) = input.emit(graph_builders, built_tees, next_stmt_id); - let location_id = match location_kind { + let location_id = match location_kind.root() { LocationId::Process(id) => id, LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), LocationId::ExternalProcess(_) => panic!(), }; @@ -586,6 +587,7 @@ impl<'a> HfPlusNode { let location_id = match location_kind { LocationId::Process(id) => id, LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), LocationId::ExternalProcess(id) => id, }; @@ -635,9 +637,10 @@ impl<'a> HfPlusNode { ident, location_kind, } => { - let location_id = match location_kind { + let location_id = match location_kind.root() { LocationId::Process(id) => id, LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), LocationId::ExternalProcess(_) => panic!(), }; @@ -1161,6 +1164,7 @@ impl<'a> HfPlusNode { let to_id = match to_location { LocationId::Process(id) => id, LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), LocationId::ExternalProcess(id) => id, }; @@ -1355,6 +1359,8 @@ fn instantiate_network<'a, D: Deploy<'a> + 'a>( (LocationId::Cluster(_from), LocationId::ExternalProcess(_to)) => { todo!("NYI") } + (LocationId::Tick(_, _), _) => panic!(), + (_, LocationId::Tick(_, _)) => panic!(), }; (sink, source, connect_fn) } diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index 8bd3f0ea5a32..d8dd16750e8d 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -8,12 +8,9 @@ use proc_macro2::Span; use stageleft::{q, Quoted}; use super::builder::FlowState; -use crate::cycle::{ - CycleCollection, CycleCollectionWithInitial, DeferTick, ForwardRef, HfCycle, HfForwardRef, - TickCycle, -}; +use crate::cycle::{CycleCollection, ForwardRef, HfForwardRef}; use crate::ir::{HfPlusNode, HfPlusSource}; -use crate::{Bounded, Optional, Singleton, Stream, Unbounded}; +use crate::{Singleton, Stream, Unbounded}; pub mod external_process; pub use external_process::ExternalProcess; @@ -30,18 +27,29 @@ pub use can_send::CanSend; pub mod tick; pub use tick::{NoTick, Tick}; -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Debug)] pub enum LocationId { Process(usize), Cluster(usize), + Tick(usize, Box), ExternalProcess(usize), } impl LocationId { + pub fn root(&self) -> &LocationId { + match self { + LocationId::Process(_) => self, + LocationId::Cluster(_) => self, + LocationId::Tick(_, id) => id.root(), + LocationId::ExternalProcess(_) => self, + } + } + pub fn raw_id(&self) -> usize { match self { LocationId::Process(id) => *id, LocationId::Cluster(id) => *id, + LocationId::Tick(_, _) => panic!("cannot get raw id for tick"), LocationId::ExternalProcess(id) => *id, } } @@ -58,11 +66,16 @@ pub trait Location<'a>: Clone { fn is_top_level() -> bool; - fn nest(&self) -> Tick + fn tick(&self) -> Tick where Self: NoTick, { - Tick { l: self.clone() } + let next_id = self.flow_state().borrow_mut().next_clock_id; + self.flow_state().borrow_mut().next_clock_id += 1; + Tick { + id: next_id, + l: self.clone(), + } } fn spin(&self) -> Stream<(), Unbounded, Self> @@ -78,19 +91,6 @@ pub trait Location<'a>: Clone { ) } - fn spin_batch( - &self, - batch_size: impl Quoted<'a, usize> + Copy + 'a, - ) -> Stream<(), Bounded, Tick> - where - Self: Sized + NoTick, - { - self.spin() - .flat_map(q!(move |_| 0..batch_size)) - .map(q!(|_| ())) - .tick_batch() - } - fn source_stream + Unpin>( &self, e: impl Quoted<'a, E>, @@ -153,35 +153,6 @@ pub trait Location<'a>: Clone { ) } - fn singleton_each_tick( - &self, - e: impl Quoted<'a, T>, - ) -> Singleton> - where - Self: Sized + NoTick, - { - self.singleton(e).latest_tick() - } - - fn singleton_first_tick( - &self, - e: impl Quoted<'a, T>, - ) -> Optional> - where - Self: Sized + NoTick, - { - let e_arr = q!([e]); - let e = e_arr.splice_untyped(); - - Optional::new( - self.clone().nest(), - HfPlusNode::Source { - source: HfPlusSource::Iter(e.into()), - location_kind: self.id(), - }, - ) - } - fn source_interval( &self, interval: impl Quoted<'a, Duration> + Copy + 'a, @@ -217,6 +188,7 @@ pub trait Location<'a>: Clone { let on_id = match self.id() { LocationId::Process(id) => id, LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), LocationId::ExternalProcess(_) => panic!(), }; @@ -238,103 +210,4 @@ pub trait Location<'a>: Clone { S::create_source(ident, self.clone()), ) } - - fn tick_forward_ref>>( - &self, - ) -> (HfForwardRef<'a, S>, S) - where - Self: NoTick, - { - let next_id = { - let on_id = match self.id() { - LocationId::Process(id) => id, - LocationId::Cluster(id) => id, - LocationId::ExternalProcess(_) => panic!(), - }; - - let mut flow_state = self.flow_state().borrow_mut(); - let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); - - let id = *next_id_entry; - *next_id_entry += 1; - id - }; - - let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); - - ( - HfForwardRef { - ident: ident.clone(), - _phantom: PhantomData, - }, - S::create_source(ident, self.nest().clone()), - ) - } - - fn tick_cycle> + DeferTick>( - &self, - ) -> (HfCycle<'a, S>, S) - where - Self: NoTick, - { - let next_id = { - let on_id = match self.id() { - LocationId::Process(id) => id, - LocationId::Cluster(id) => id, - LocationId::ExternalProcess(_) => panic!(), - }; - - let mut flow_state = self.flow_state().borrow_mut(); - let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); - - let id = *next_id_entry; - *next_id_entry += 1; - id - }; - - let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); - - ( - HfCycle { - ident: ident.clone(), - _phantom: PhantomData, - }, - S::create_source(ident, self.nest().clone()), - ) - } - - fn tick_cycle_with_initial< - S: CycleCollectionWithInitial<'a, TickCycle, Location = Tick> + DeferTick, - >( - &self, - initial: S, - ) -> (HfCycle<'a, S>, S) - where - Self: NoTick, - { - let next_id = { - let on_id = match self.id() { - LocationId::Process(id) => id, - LocationId::Cluster(id) => id, - LocationId::ExternalProcess(_) => panic!(), - }; - - let mut flow_state = self.flow_state().borrow_mut(); - let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); - - let id = *next_id_entry; - *next_id_entry += 1; - id - }; - - let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); - - ( - HfCycle { - ident: ident.clone(), - _phantom: PhantomData, - }, - S::create_source(ident, initial, self.nest().clone()), - ) - } } diff --git a/hydroflow_plus/src/location/tick.rs b/hydroflow_plus/src/location/tick.rs index acdf26e72756..895ae45559ad 100644 --- a/hydroflow_plus/src/location/tick.rs +++ b/hydroflow_plus/src/location/tick.rs @@ -1,5 +1,16 @@ +use std::marker::PhantomData; + +use proc_macro2::Span; +use stageleft::{q, Quoted}; + use super::{Cluster, Location, LocationId, Process}; use crate::builder::FlowState; +use crate::cycle::{ + CycleCollection, CycleCollectionWithInitial, DeferTick, ForwardRef, HfCycle, HfForwardRef, + TickCycle, +}; +use crate::ir::{HfPlusNode, HfPlusSource}; +use crate::{Bounded, Optional, Singleton, Stream}; pub trait NoTick {} impl NoTick for Process<'_, T> {} @@ -8,18 +19,13 @@ impl NoTick for Cluster<'_, T> {} /// Marks the stream as being inside the single global clock domain. #[derive(Clone)] pub struct Tick { + pub(crate) id: usize, pub(crate) l: L, } -impl<'a, L: Location<'a>> Tick { - pub fn outer(&self) -> &L { - &self.l - } -} - impl<'a, L: Location<'a>> Location<'a> for Tick { fn id(&self) -> LocationId { - self.l.id() + LocationId::Tick(self.id, Box::new(self.l.id())) } fn flow_state(&self) -> &FlowState { @@ -30,3 +36,151 @@ impl<'a, L: Location<'a>> Location<'a> for Tick { false } } + +impl<'a, L: Location<'a>> Tick { + pub fn outer(&self) -> &L { + &self.l + } + + pub fn spin_batch( + &self, + batch_size: impl Quoted<'a, usize> + Copy + 'a, + ) -> Stream<(), Bounded, Self> + where + L: NoTick, + { + self.l + .spin() + .flat_map(q!(move |_| 0..batch_size)) + .map(q!(|_| ())) + .tick_batch(self) + } + + pub fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton + where + L: NoTick, + { + self.outer().singleton(e).latest_tick(self) + } + + pub fn singleton_first_tick( + &self, + e: impl Quoted<'a, T>, + ) -> Optional + where + L: NoTick, + { + let e_arr = q!([e]); + let e = e_arr.splice_untyped(); + + Optional::new( + self.clone(), + HfPlusNode::Source { + source: HfPlusSource::Iter(e.into()), + location_kind: self.l.id(), + }, + ) + } + + pub fn forward_ref>( + &self, + ) -> (HfForwardRef<'a, S>, S) + where + L: NoTick, + { + let next_id = { + let on_id = match self.l.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + HfForwardRef { + ident: ident.clone(), + _phantom: PhantomData, + }, + S::create_source(ident, self.clone()), + ) + } + + pub fn cycle + DeferTick>( + &self, + ) -> (HfCycle<'a, S>, S) + where + L: NoTick, + { + let next_id = { + let on_id = match self.l.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + HfCycle { + ident: ident.clone(), + _phantom: PhantomData, + }, + S::create_source(ident, self.clone()), + ) + } + + pub fn cycle_with_initial< + S: CycleCollectionWithInitial<'a, TickCycle, Location = Self> + DeferTick, + >( + &self, + initial: S, + ) -> (HfCycle<'a, S>, S) + where + L: NoTick, + { + let next_id = { + let on_id = match self.l.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + HfCycle { + ident: ident.clone(), + _phantom: PhantomData, + }, + S::create_source(ident, initial, self.clone()), + ) + } +} diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index 71eab96cbd0f..7ca84bf6be1f 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -299,7 +299,7 @@ impl<'a, T, W, N: Location<'a>> Optional { let none: syn::Expr = parse_quote!([::std::option::Option::None]); let core_ir = HfPlusNode::Persist(Box::new(HfPlusNode::Source { source: HfPlusSource::Iter(none.into()), - location_kind: self.location.id(), + location_kind: self.location.id().root().clone(), })); let none_singleton = if N::is_top_level() { @@ -330,27 +330,28 @@ impl<'a, T, N: Location<'a>> Optional { } impl<'a, T, B, N: Location<'a> + NoTick> Optional { - pub fn latest_tick(self) -> Optional> { + pub fn latest_tick(self, tick: &Tick) -> Optional> { Optional::new( - self.location.nest(), + tick.clone(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } pub fn tick_samples(self) -> Stream { - self.latest_tick().all_ticks() + let tick = self.location.tick(); + self.latest_tick(&tick).all_ticks() } pub fn sample_every( self, interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, ) -> Stream { - let samples = self.location.source_interval(interval).tick_batch(); + let samples = self.location.source_interval(interval); + let tick = self.location.tick(); - self.latest_tick() - .continue_if(samples.first()) - .latest() - .tick_samples() + self.latest_tick(&tick) + .continue_if(samples.tick_batch(&tick).first()) + .all_ticks() } } diff --git a/hydroflow_plus/src/rewrites/persist_pullup.rs b/hydroflow_plus/src/rewrites/persist_pullup.rs index caafcb13836a..50ead7f9e2f4 100644 --- a/hydroflow_plus/src/rewrites/persist_pullup.rs +++ b/hydroflow_plus/src/rewrites/persist_pullup.rs @@ -167,7 +167,8 @@ mod tests { let flow = crate::builder::FlowBuilder::new(); let process = flow.process::<()>(); - let before_tee = process.source_iter(q!(0..10)).tick_batch().persist(); + let tick = process.tick(); + let before_tee = process.source_iter(q!(0..10)).tick_batch(&tick).persist(); before_tee .clone() diff --git a/hydroflow_plus/src/rewrites/properties.rs b/hydroflow_plus/src/rewrites/properties.rs index ca557c0ef67c..ecb6bcbe42a4 100644 --- a/hydroflow_plus/src/rewrites/properties.rs +++ b/hydroflow_plus/src/rewrites/properties.rs @@ -104,6 +104,7 @@ mod tests { let mut database = PropertyDatabase::default(); let process = flow.process::<()>(); + let tick = process.tick(); let counter_func = q!(|count: &mut i32, _| *count += 1); let _ = database.add_commutative_tag(counter_func); @@ -111,7 +112,7 @@ mod tests { process .source_iter(q!(vec![])) .map(q!(|string: String| (string, ()))) - .tick_batch() + .tick_batch(&tick) .fold_keyed(q!(|| 0), counter_func) .all_ticks() .for_each(q!(|(string, count)| println!("{}: {}", string, count))); diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 7271cc4a0854..b153a0947152 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -227,27 +227,28 @@ impl<'a, T, N: Location<'a>> Singleton { } impl<'a, T, B, N: Location<'a> + NoTick> Singleton { - pub fn latest_tick(self) -> Singleton> { + pub fn latest_tick(self, tick: &Tick) -> Singleton> { Singleton::new( - self.location.nest(), + tick.clone(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } pub fn tick_samples(self) -> Stream { - self.latest_tick().all_ticks() + let tick = self.location.tick(); + self.latest_tick(&tick).all_ticks() } pub fn sample_every( self, interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, ) -> Stream { - let samples = self.location.source_interval(interval).tick_batch(); + let samples = self.location.source_interval(interval); + let tick = self.location.tick(); - self.latest_tick() - .continue_if(samples.first()) - .latest() - .tick_samples() + self.latest_tick(&tick) + .continue_if(samples.tick_batch(&tick).first()) + .all_ticks() } } diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 065635d2d9f7..3f1e1803cc94 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -467,26 +467,29 @@ impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { } impl<'a, T, W, N: Location<'a> + NoTick> Stream { - pub fn tick_batch(self) -> Stream> { + pub fn tick_batch(self, tick: &Tick) -> Stream> { Stream::new( - self.location.nest(), + tick.clone(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } - pub fn tick_prefix(self) -> Stream> + pub fn tick_prefix(self, tick: &Tick) -> Stream> where T: Clone, { - self.tick_batch().persist() + self.tick_batch(tick).persist() } pub fn sample_every( self, interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, ) -> Stream { - let samples = self.location.source_interval(interval).tick_batch(); - self.tick_batch().continue_if(samples.first()).all_ticks() + let samples = self.location.source_interval(interval); + let tick = self.location.tick(); + self.tick_batch(&tick) + .continue_if(samples.tick_batch(&tick).first()) + .all_ticks() } pub fn for_each(self, f: impl IntoQuotedMut<'a, F>) { diff --git a/hydroflow_plus_test/src/cluster/compute_pi.rs b/hydroflow_plus_test/src/cluster/compute_pi.rs index 106c39fecfdc..4f4010a3d5fc 100644 --- a/hydroflow_plus_test/src/cluster/compute_pi.rs +++ b/hydroflow_plus_test/src/cluster/compute_pi.rs @@ -14,6 +14,7 @@ pub fn compute_pi<'a>( let process = flow.process(); let trials = cluster + .tick() .spin_batch(q!(batch_size)) .map(q!(|_| rand::random::<(f64, f64)>())) .map(q!(|(x, y)| x * x + y * y < 1.0)) diff --git a/hydroflow_plus_test/src/cluster/map_reduce.rs b/hydroflow_plus_test/src/cluster/map_reduce.rs index 95696d5cbcf0..192403e423b1 100644 --- a/hydroflow_plus_test/src/cluster/map_reduce.rs +++ b/hydroflow_plus_test/src/cluster/map_reduce.rs @@ -14,7 +14,7 @@ pub fn map_reduce<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, Leader>, Cluster<' let all_ids_vec = cluster.members(); let words_partitioned = words - .tick_batch() + .tick_batch(&process.tick()) .enumerate() .map(q!(|(i, w)| ( ClusterId::from_raw((i % all_ids_vec.len()) as u32), @@ -25,7 +25,7 @@ pub fn map_reduce<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, Leader>, Cluster<' words_partitioned .send_bincode(&cluster) .map(q!(|string| (string, ()))) - .tick_batch() + .tick_batch(&cluster.tick()) .fold_keyed(q!(|| 0), q!(|count, _| *count += 1)) .inspect(q!(|(string, count)| println!( "partition count: {} - {}", @@ -33,7 +33,7 @@ pub fn map_reduce<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, Leader>, Cluster<' ))) .all_ticks() .send_bincode_interleaved(&process) - .tick_batch() + .tick_batch(&process.tick()) .persist() .reduce_keyed(q!(|total, count| *total += count)) .all_ticks() diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 170046d6fdbb..a5d14f794063 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -80,15 +80,20 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( .source_iter(q!(["Acceptors say hello"])) .for_each(q!(|s| println!("{}", s))); + let proposer_tick = proposers.tick(); + let acceptor_tick = acceptors.tick(); + let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b_forward_reference) = proposers.forward_ref::, _, _>>(); let (a_log_complete_cycle, a_log_forward_reference) = - acceptors - .tick_forward_ref::, HashMap>), _, _>>(); + acceptor_tick + .forward_ref::, HashMap>), _, _>>(); let (p_ballot_num, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( proposers, acceptors, + &proposer_tick, + &acceptor_tick, f, i_am_leader_send_timeout, i_am_leader_check_timeout, @@ -117,6 +122,8 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( let (p_to_replicas, a_log, a_to_proposers_p2b) = sequence_payload( proposers, acceptors, + &proposer_tick, + &acceptor_tick, c_to_proposers, r_to_acceptors_checkpoint, p_ballot_num, @@ -141,6 +148,8 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, + proposer_tick: &Tick>, + acceptor_tick: &Tick>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, @@ -158,7 +167,7 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader_forward_ref) = proposers.forward_ref::>(); let (p_is_leader_complete_cycle, p_is_leader_forward_ref) = - proposers.tick_forward_ref::>(); + proposer_tick.forward_ref::>(); // a_to_proposers_p2b.clone().for_each(q!(|(_, p2b): (u32, P2b)| println!("Proposer received P2b: {:?}", p2b))); // p_to_proposers_i_am_leader.clone().for_each(q!(|ballot: Ballot| println!("Proposer received I am leader: {:?}", ballot))); // c_to_proposers.clone().for_each(q!(|payload: ClientPayload| println!("Client sent proposer payload: {:?}", payload))); @@ -169,11 +178,15 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( p_received_p2b_ballots, p_to_proposers_i_am_leader_forward_ref, ); - let (p_ballot_num, p_has_largest_ballot) = - p_ballot_calc(proposers, p_received_max_ballot.latest_tick()); + let (p_ballot_num, p_has_largest_ballot) = p_ballot_calc( + proposers, + proposer_tick, + p_received_max_ballot.latest_tick(proposer_tick), + ); let (p_to_proposers_i_am_leader, p_trigger_election) = p_leader_heartbeat( proposers, + proposer_tick, p_is_leader_forward_ref, p_ballot_num.clone(), i_am_leader_send_timeout, @@ -191,11 +204,12 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( ); let (a_max_ballot, a_to_proposers_p1b) = - acceptor_p1(acceptors, p_to_acceptors_p1a, a_log, proposers); + acceptor_p1(acceptor_tick, p_to_acceptors_p1a, a_log, proposers); a_to_proposers_p1b_complete_cycle.complete(a_to_proposers_p1b.clone()); let (p_is_leader, p_relevant_p1bs) = p_p1b( proposers, + proposer_tick, a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), p_ballot_num.clone(), p_has_largest_ballot, @@ -227,6 +241,7 @@ fn p_max_ballot<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_ballot_calc<'a>( proposers: &Cluster<'a, Proposer>, + proposer_tick: &Tick>, p_received_max_ballot: Singleton>>, ) -> ( Singleton>>, @@ -234,7 +249,7 @@ fn p_ballot_calc<'a>( ) { let p_id = proposers.self_id(); let (p_ballot_num_complete_cycle, p_ballot_num) = - proposers.tick_cycle_with_initial(proposers.singleton_each_tick(q!(0))); + proposer_tick.cycle_with_initial(proposer_tick.singleton(q!(0))); let p_new_ballot_num = p_received_max_ballot .clone() @@ -269,6 +284,7 @@ fn p_ballot_calc<'a>( } fn p_leader_expired<'a>( + proposer_tick: &Tick>, p_to_proposers_i_am_leader: Stream>, p_is_leader: Optional>>, i_am_leader_check_timeout: u64, // How often to check if heartbeat expired @@ -282,7 +298,7 @@ fn p_leader_expired<'a>( ); p_latest_received_i_am_leader - .latest_tick() + .latest_tick(proposer_tick) .continue_unless(p_is_leader) .filter(q!(move |latest_received_i_am_leader| { if let Some(latest_received_i_am_leader) = latest_received_i_am_leader { @@ -297,6 +313,7 @@ fn p_leader_expired<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_leader_heartbeat<'a>( proposers: &Cluster<'a, Proposer>, + proposer_tick: &Tick>, p_is_leader: Optional>>, p_ballot_num: Singleton>>, i_am_leader_send_timeout: u64, // How often to heartbeat @@ -319,6 +336,7 @@ fn p_leader_heartbeat<'a>( .broadcast_bincode_interleaved(proposers); let p_leader_expired = p_leader_expired( + proposer_tick, p_to_proposers_i_am_leader.clone(), p_is_leader, i_am_leader_check_timeout, @@ -333,7 +351,7 @@ fn p_leader_heartbeat<'a>( )), q!(Duration::from_secs(i_am_leader_check_timeout)), ) - .tick_batch() + .tick_batch(proposer_tick) .first(), ); (p_to_proposers_i_am_leader, p_trigger_election) @@ -363,7 +381,7 @@ fn p_p1a<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( - acceptors: &Cluster<'a, Acceptor>, + acceptor_tick: &Tick>, p_to_acceptors_p1a: Stream>, a_log: Singleton>>, proposers: &Cluster<'a, Proposer>, @@ -371,14 +389,14 @@ fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( Singleton>>, Stream, Unbounded, Cluster<'a, Proposer>>, ) { - let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(); + let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(acceptor_tick); let a_max_ballot = p_to_acceptors_p1a .clone() .inspect(q!(|p1a| println!("Acceptor received P1a: {:?}", p1a))) .persist() .map(q!(|p1a| p1a.ballot)) .max() - .unwrap_or(acceptors.singleton_each_tick(q!(Ballot { + .unwrap_or(acceptor_tick.singleton(q!(Ballot { num: 0, proposer_id: ClusterId::from_raw(0) }))); @@ -405,6 +423,7 @@ fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( proposers: &Cluster<'a, Proposer>, + proposer_tick: &Tick>, a_to_proposers_p1b: Stream, Unbounded, Cluster<'a, Proposer>>, p_ballot_num: Singleton>>, p_has_largest_ballot: Optional<(Ballot, u32), Bounded, Tick>>, @@ -415,7 +434,7 @@ fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( ) { let p_id = proposers.self_id(); let p_relevant_p1bs = a_to_proposers_p1b - .tick_prefix() + .tick_prefix(proposer_tick) // NOTE: because `p_ballot_num` grows monotonically across ticks, we could garbage gollect // but we don't do that here since leader election is a rare event .cross_singleton(p_ballot_num.clone()) @@ -520,6 +539,8 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( fn sequence_payload<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, + proposer_tick: &Tick>, + acceptor_tick: &Tick>, c_to_proposers: Stream>, r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Unbounded, Cluster<'a, Acceptor>>, @@ -538,6 +559,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( ) { let p_to_acceptors_p2a = p_p2a( proposers, + proposer_tick, p_max_slot, c_to_proposers, p_ballot_num.clone(), @@ -549,6 +571,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( // Acceptors. // p_to_acceptors_p2a.clone().for_each(q!(|p2a: P2a| println!("Acceptor received P2a: {:?}", p2a))); let (a_log, a_to_proposers_p2b) = acceptor_p2( + acceptor_tick, a_max_ballot.clone(), p_to_acceptors_p2a, r_to_acceptors_checkpoint, @@ -556,7 +579,7 @@ fn sequence_payload<'a, P: PaxosPayload, R>( f, ); - let p_to_replicas = p_p2b(proposers, a_to_proposers_p2b.clone(), f); + let p_to_replicas = p_p2b(proposer_tick, a_to_proposers_p2b.clone(), f); (p_to_replicas, a_log, a_to_proposers_p2b) } @@ -568,8 +591,10 @@ enum CheckpointOrP2a

{ } // Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. +#[expect(clippy::too_many_arguments, reason = "internal paxos code // TODO")] fn p_p2a<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, + proposer_tick: &Tick>, p_max_slot: Optional>>, c_to_proposers: Stream>, p_ballot_num: Singleton>>, @@ -578,16 +603,16 @@ fn p_p2a<'a, P: PaxosPayload>( acceptors: &Cluster<'a, Acceptor>, ) -> Stream, Unbounded, Cluster<'a, Acceptor>> { let p_id = proposers.self_id(); - let (p_next_slot_complete_cycle, p_next_slot) = proposers.tick_cycle::>(); + let (p_next_slot_complete_cycle, p_next_slot) = proposer_tick.cycle::>(); let p_next_slot_after_reconciling_p1bs = p_max_slot .map(q!(|max_slot| max_slot + 1)) - .unwrap_or(proposers.singleton_each_tick(q!(0))) + .unwrap_or(proposer_tick.singleton(q!(0))) // .inspect(q!(|max_slot| println!("{} p_max_slot: {:?}", context.current_tick(), max_slot))) .continue_unless(p_next_slot.clone()); // Send p2as let p_indexed_payloads = c_to_proposers - .tick_batch() + .tick_batch(proposer_tick) .enumerate() .cross_singleton(p_next_slot.clone()) // .inspect(q!(|next| println!("{} p_indexed_payloads next slot: {}", context.current_tick(), next)))) @@ -623,6 +648,7 @@ fn p_p2a<'a, P: PaxosPayload>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p2<'a, P: PaxosPayload, R>( + acceptor_tick: &Tick>, a_max_ballot: Singleton>>, p_to_acceptors_p2a: Stream, Unbounded, Cluster<'a, Acceptor>>, r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Unbounded, Cluster<'a, Acceptor>>, @@ -632,17 +658,16 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( Singleton<(Option, HashMap>), Bounded, Tick>>, Stream, Unbounded, Cluster<'a, Proposer>>, ) { - let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(); + let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(acceptor_tick); // Get the latest checkpoint sequence per replica - let a_checkpoint_largest_seqs = - r_to_acceptors_checkpoint - .tick_prefix() - .reduce_keyed(q!(|curr_seq, seq| { - if seq > *curr_seq { - *curr_seq = seq; - } - })); + let a_checkpoint_largest_seqs = r_to_acceptors_checkpoint + .tick_prefix(acceptor_tick) + .reduce_keyed(q!(|curr_seq, seq| { + if seq > *curr_seq { + *curr_seq = seq; + } + })); let a_checkpoints_quorum_reached = a_checkpoint_largest_seqs.clone().count().filter_map(q!( move |num_received| if num_received == f + 1 { Some(true) @@ -721,13 +746,15 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( } fn p_p2b<'a, P: PaxosPayload>( - proposers: &Cluster<'a, Proposer>, + proposer_tick: &Tick>, a_to_proposers_p2b: Stream, Unbounded, Cluster<'a, Proposer>>, f: usize, ) -> Stream<(usize, Option

), Unbounded, Cluster<'a, Proposer>> { - let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposers.tick_cycle(); - let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposers.tick_cycle(); - let p_p2b = a_to_proposers_p2b.tick_batch().union(p_persisted_p2bs); + let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposer_tick.cycle(); + let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposer_tick.cycle(); + let p_p2b = a_to_proposers_p2b + .tick_batch(proposer_tick) + .union(p_persisted_p2bs); let p_count_matching_p2bs = p_p2b .clone() .filter_map(q!(|p2b| if p2b.ballot == p2b.max_ballot { diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 0af5e6ce350a..e9c59925b274 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -88,6 +88,7 @@ fn bench_client<'a>( median_latency_window_size: usize, f: usize, ) { + let client_tick = clients.tick(); let c_id = clients.self_id(); // r_to_clients_payload_applied.clone().inspect(q!(|payload: &(u32, ReplicaPayload)| println!("Client received payload: {:?}", payload))); // Only keep the latest leader @@ -97,7 +98,7 @@ fn bench_client<'a>( ballot ))) .max(); - let c_new_leader_ballot = current_leader.clone().latest_tick().delta(); + let c_new_leader_ballot = current_leader.clone().latest_tick(&client_tick).delta(); // Whenever the leader changes, make all clients send a message let c_new_payloads_when_leader_elected = c_new_leader_ballot @@ -116,10 +117,9 @@ fn bench_client<'a>( let transaction_results = transaction_cycle(c_to_proposers); // Whenever replicas confirm that a payload was committed, collected it and wait for a quorum - let (c_pending_quorum_payloads_complete_cycle, c_pending_quorum_payloads) = - clients.tick_cycle(); + let (c_pending_quorum_payloads_complete_cycle, c_pending_quorum_payloads) = client_tick.cycle(); let c_received_payloads = transaction_results - .tick_batch() + .tick_batch(&client_tick) .map(q!(|(sender, replica_payload)| ( replica_payload.key, sender @@ -146,7 +146,7 @@ fn bench_client<'a>( // Whenever all replicas confirm that a payload was committed, send another payload let c_new_payloads_when_committed = c_received_quorum_payloads .clone() - .cross_singleton(current_leader.clone().latest_tick()) + .cross_singleton(current_leader.clone().latest_tick(&client_tick)) .map(q!(move |(key, cur_leader)| ( cur_leader, KvPayload { key, value: c_id } @@ -159,7 +159,7 @@ fn bench_client<'a>( // Track statistics let (c_timers_complete_cycle, c_timers) = - clients.tick_cycle::>(); + client_tick.cycle::>(); let c_new_timers_when_leader_elected = c_new_leader_ballot .map(q!(|_| SystemTime::now())) .flat_map(q!( @@ -181,7 +181,7 @@ fn bench_client<'a>( let c_stats_output_timer = clients .source_interval(q!(Duration::from_secs(1))) - .tick_batch() + .tick_batch(&client_tick) .first(); let c_latency_reset = c_stats_output_timer.clone().map(q!(|_| None)).defer_tick(); @@ -243,7 +243,7 @@ fn bench_client<'a>( c_latencies .zip(c_throughput) - .latest_tick() + .latest_tick(&client_tick) .continue_if(c_stats_output_timer) .all_ticks() .for_each(q!(move |(latencies, throughput)| { diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 320643a4befb..840e30cc9af7 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -98,15 +98,17 @@ pub fn replica<'a, K: KvKey, V: KvValue>( Stream>, Stream, Unbounded, Cluster<'a, Replica>>, ) { - let (r_buffered_payloads_complete_cycle, r_buffered_payloads) = replicas.tick_cycle(); + let replica_tick = replicas.tick(); + + let (r_buffered_payloads_complete_cycle, r_buffered_payloads) = replica_tick.cycle(); // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); let r_sorted_payloads = p_to_replicas - .tick_batch() + .tick_batch(&replica_tick) .union(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet .sort(); // Create a cycle since we'll use this seq before we define it let (r_highest_seq_complete_cycle, r_highest_seq) = - replicas.tick_cycle::>(); + replica_tick.cycle::>(); // Find highest the sequence number of any payload that can be processed in this tick. This is the payload right before a hole. let r_highest_seq_processable_payload = r_sorted_payloads .clone() @@ -160,7 +162,7 @@ pub fn replica<'a, K: KvKey, V: KvValue>( // Send checkpoints to the acceptors when we've processed enough payloads let (r_checkpointed_seqs_complete_cycle, r_checkpointed_seqs) = - replicas.tick_cycle::>(); + replica_tick.cycle::>(); let r_max_checkpointed_seq = r_checkpointed_seqs.persist().max().into_singleton(); let r_checkpoint_seq_new = r_max_checkpointed_seq diff --git a/hydroflow_plus_test/src/cluster/simple_cluster.rs b/hydroflow_plus_test/src/cluster/simple_cluster.rs index 771a7ed1c97b..f3c7062b348d 100644 --- a/hydroflow_plus_test/src/cluster/simple_cluster.rs +++ b/hydroflow_plus_test/src/cluster/simple_cluster.rs @@ -40,7 +40,7 @@ pub fn simple_cluster<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, ()>, Cluster<' ids.cross_product(numbers) .map(q!(|(id, n)| (id, (id, n)))) .send_bincode(&cluster) - .tick_batch() + .tick_batch(&cluster.tick()) .inspect(q!(move |n| println!( "cluster received: {:?} (self cluster id: {})", n, cluster_self_id diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap index eb4e28cb096b..a9bac21e29da 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap @@ -51,9 +51,9 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , (f64 , f64) > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | _ | rand :: random :: < (f64 , f64) > () }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: location :: * ; | _ | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; | _ | () }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: ops :: Range < usize > > ({ use hydroflow_plus :: __staged :: location :: * ; let batch_size = { use crate :: __staged :: cluster :: compute_pi :: * ; let batch_size = 8192usize ; batch_size } ; move | _ | 0 .. batch_size }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: ops :: Range < usize > > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; let batch_size = { use crate :: __staged :: cluster :: compute_pi :: * ; let batch_size = 8192usize ; batch_size } ; move | _ | 0 .. batch_size }), input: Source { source: Spin, location_kind: Cluster( diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_0.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_0.snap index 41e4f54f9479..0df52aa01c95 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_0.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_0.snap @@ -3,8 +3,8 @@ source: hydroflow_plus_test/src/cluster/compute_pi.rs expression: ir.surface_syntax_string() --- 1v1 = spin (); -2v1 = flat_map (stageleft :: runtime_support :: fn1_type_hint :: < () , std :: ops :: Range < usize > > ({ use hydroflow_plus :: __staged :: location :: * ; let batch_size = { use crate :: __staged :: cluster :: compute_pi :: * ; let batch_size = 8192usize ; batch_size } ; move | _ | 0 .. batch_size })); -3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: location :: * ; | _ | () })); +2v1 = flat_map (stageleft :: runtime_support :: fn1_type_hint :: < () , std :: ops :: Range < usize > > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; let batch_size = { use crate :: __staged :: cluster :: compute_pi :: * ; let batch_size = 8192usize ; batch_size } ; move | _ | 0 .. batch_size })); +3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; | _ | () })); 4v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < () , (f64 , f64) > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | _ | rand :: random :: < (f64 , f64) > () })); 5v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (f64 , f64) , bool > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (x , y) | x * x + y * y < 1.0 })); 6v1 = fold :: < 'tick > (stageleft :: runtime_support :: fn0_type_hint :: < (u64 , u64) > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | | (0u64 , 0u64) }) , stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u64 , u64) , bool , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) , sample_inside | { if sample_inside { * inside += 1 ; } * total += 1 ; } })); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 2b9f3f5cf572..df35cf749539 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -29,8 +29,11 @@ expression: built.ir() ident: Ident { sym: cycle_4, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), input: DeferTick( Map { @@ -95,8 +98,11 @@ expression: built.ir() ident: Ident { sym: cycle_4, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), }, Persist( @@ -175,8 +181,11 @@ expression: built.ir() ident: Ident { sym: cycle_3, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), }, }, @@ -383,8 +392,11 @@ expression: built.ir() ident: Ident { sym: cycle_0, }, - location_kind: Cluster( - 1, + location_kind: Tick( + 2, + Cluster( + 1, + ), ), }, }, @@ -398,8 +410,11 @@ expression: built.ir() ident: Ident { sym: cycle_3, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), input: Tee { inner: : Map { @@ -457,8 +472,11 @@ expression: built.ir() ident: Ident { sym: cycle_5, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), input: DeferTick( Map { @@ -515,8 +533,11 @@ expression: built.ir() ident: Ident { sym: cycle_5, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), }, }, @@ -613,8 +634,11 @@ expression: built.ir() ident: Ident { sym: cycle_6, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), input: DeferTick( Difference( @@ -804,8 +828,11 @@ expression: built.ir() ident: Ident { sym: cycle_7, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), }, ), @@ -832,8 +859,11 @@ expression: built.ir() ident: Ident { sym: cycle_7, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), input: DeferTick( Map { @@ -856,8 +886,11 @@ expression: built.ir() ident: Ident { sym: cycle_0, }, - location_kind: Cluster( - 1, + location_kind: Tick( + 2, + Cluster( + 1, + ), ), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), @@ -972,8 +1005,11 @@ expression: built.ir() ident: Ident { sym: cycle_1, }, - location_kind: Cluster( - 3, + location_kind: Tick( + 4, + Cluster( + 3, + ), ), input: DeferTick( Map { @@ -1028,8 +1064,11 @@ expression: built.ir() ident: Ident { sym: cycle_6, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 1, + Cluster( + 0, + ), ), }, ), @@ -1041,8 +1080,11 @@ expression: built.ir() ident: Ident { sym: cycle_1, }, - location_kind: Cluster( - 3, + location_kind: Tick( + 4, + Cluster( + 3, + ), ), }, ), @@ -1065,8 +1107,11 @@ expression: built.ir() ident: Ident { sym: cycle_2, }, - location_kind: Cluster( - 3, + location_kind: Tick( + 4, + Cluster( + 3, + ), ), }, }, @@ -1094,8 +1139,11 @@ expression: built.ir() ident: Ident { sym: cycle_2, }, - location_kind: Cluster( - 3, + location_kind: Tick( + 4, + Cluster( + 3, + ), ), input: DeferTick( Tee { @@ -1131,8 +1179,11 @@ expression: built.ir() ident: Ident { sym: cycle_3, }, - location_kind: Cluster( - 3, + location_kind: Tick( + 4, + Cluster( + 3, + ), ), input: DeferTick( Tee { @@ -1149,8 +1200,11 @@ expression: built.ir() ident: Ident { sym: cycle_3, }, - location_kind: Cluster( - 3, + location_kind: Tick( + 4, + Cluster( + 3, + ), ), }, ), @@ -1241,8 +1295,11 @@ expression: built.ir() ident: Ident { sym: cycle_2, }, - location_kind: Cluster( - 2, + location_kind: Tick( + 0, + Cluster( + 2, + ), ), input: DeferTick( AntiJoin( @@ -1295,8 +1352,11 @@ expression: built.ir() ident: Ident { sym: cycle_2, }, - location_kind: Cluster( - 2, + location_kind: Tick( + 0, + Cluster( + 2, + ), ), }, ), @@ -1366,8 +1426,11 @@ expression: built.ir() ident: Ident { sym: cycle_3, }, - location_kind: Cluster( - 2, + location_kind: Tick( + 0, + Cluster( + 2, + ), ), input: DeferTick( ReduceKeyed { @@ -1379,8 +1442,11 @@ expression: built.ir() ident: Ident { sym: cycle_3, }, - location_kind: Cluster( - 2, + location_kind: Tick( + 0, + Cluster( + 2, + ), ), }, }, diff --git a/hydroflow_plus_test/src/cluster/two_pc.rs b/hydroflow_plus_test/src/cluster/two_pc.rs index 42f944cb419f..5e11100ffc95 100644 --- a/hydroflow_plus_test/src/cluster/two_pc.rs +++ b/hydroflow_plus_test/src/cluster/two_pc.rs @@ -69,7 +69,7 @@ pub fn two_pc<'a>( .map(q!(|(id, (t, _reply))| (t, id))) // fold_keyed: 1 input stream of type (K, V1), 1 output stream of type (K, V2). // The output will have one tuple for each distinct K, with an accumulated value of type V2. - .tick_batch().fold_keyed(q!(|| 0), q!(|old: &mut u32, _| *old += 1)).filter_map(q!(move |(t, count)| { + .tick_batch(&coordinator.tick()).fold_keyed(q!(|| 0), q!(|old: &mut u32, _| *old += 1)).filter_map(q!(move |(t, count)| { // here I set the participant to 3. If want more or less participant, fix line 26 of examples/broadcast.rs if count == num_participants { Some(t) diff --git a/hydroflow_plus_test_local/src/local/chat_app.rs b/hydroflow_plus_test_local/src/local/chat_app.rs index 6ede979ea63f..dc87f8a680c1 100644 --- a/hydroflow_plus_test_local/src/local/chat_app.rs +++ b/hydroflow_plus_test_local/src/local/chat_app.rs @@ -13,13 +13,17 @@ pub fn chat_app<'a>( replay_messages: bool, ) -> impl Quoted<'a, Hydroflow<'a>> { let process = flow.process::<()>(); + let tick = process.tick(); - let users = process.source_stream(users_stream).tick_batch().persist(); + let users = process + .source_stream(users_stream) + .tick_batch(&tick) + .persist(); let messages = process.source_stream(messages); let messages = if replay_messages { - messages.tick_batch().persist() + messages.tick_batch(&tick).persist() } else { - messages.tick_batch() + messages.tick_batch(&tick) }; // do this after the persist to test pullup diff --git a/hydroflow_plus_test_local/src/local/compute_pi.rs b/hydroflow_plus_test_local/src/local/compute_pi.rs index af4882fa2cfa..97b850714e45 100644 --- a/hydroflow_plus_test_local/src/local/compute_pi.rs +++ b/hydroflow_plus_test_local/src/local/compute_pi.rs @@ -6,8 +6,9 @@ use stageleft::*; pub fn compute_pi<'a>(flow: &FlowBuilder<'a>, batch_size: RuntimeData) -> Process<'a, ()> { let process = flow.process(); + let tick = process.tick(); - let trials = process + let trials = tick .spin_batch(q!(batch_size)) .map(q!(|_| rand::random::<(f64, f64)>())) .map(q!(|(x, y)| x * x + y * y < 1.0)) diff --git a/hydroflow_plus_test_local/src/local/count_elems.rs b/hydroflow_plus_test_local/src/local/count_elems.rs index 04818fed51ad..de967ca18bc0 100644 --- a/hydroflow_plus_test_local/src/local/count_elems.rs +++ b/hydroflow_plus_test_local/src/local/count_elems.rs @@ -10,11 +10,12 @@ pub fn count_elems_generic<'a, T: 'a>( output: RuntimeData<&'a UnboundedSender>, ) -> impl Quoted<'a, Hydroflow<'a>> { let process = flow.process::<()>(); + let tick = process.tick(); let source = process.source_stream(input_stream); let count = source .map(q!(|_| 1)) - .tick_batch() + .tick_batch(&tick) .fold(q!(|| 0), q!(|a, b| *a += b)) .all_ticks(); diff --git a/hydroflow_plus_test_local/src/local/negation.rs b/hydroflow_plus_test_local/src/local/negation.rs index 9fb75b678198..667bec86c572 100644 --- a/hydroflow_plus_test_local/src/local/negation.rs +++ b/hydroflow_plus_test_local/src/local/negation.rs @@ -11,13 +11,14 @@ pub fn test_difference<'a>( persist2: bool, ) -> impl Quoted<'a, Hydroflow<'a>> { let process = flow.process::<()>(); + let tick = process.tick(); - let mut source = process.source_iter(q!(0..5)).tick_batch(); + let mut source = process.source_iter(q!(0..5)).tick_batch(&tick); if persist1 { source = source.persist(); } - let mut source2 = process.source_iter(q!(3..6)).tick_batch(); + let mut source2 = process.source_iter(q!(3..6)).tick_batch(&tick); if persist2 { source2 = source2.persist(); } @@ -38,16 +39,17 @@ pub fn test_anti_join<'a>( persist2: bool, ) -> impl Quoted<'a, Hydroflow<'a>> { let process = flow.process::<()>(); + let tick = process.tick(); let mut source = process .source_iter(q!(0..5)) .map(q!(|v| (v, v))) - .tick_batch(); + .tick_batch(&tick); if persist1 { source = source.persist(); } - let mut source2 = process.source_iter(q!(3..6)).tick_batch(); + let mut source2 = process.source_iter(q!(3..6)).tick_batch(&tick); if persist2 { source2 = source2.persist(); } diff --git a/hydroflow_plus_test_local/src/local/teed_join.rs b/hydroflow_plus_test_local/src/local/teed_join.rs index 951ad9608874..3d1283528af9 100644 --- a/hydroflow_plus_test_local/src/local/teed_join.rs +++ b/hydroflow_plus_test_local/src/local/teed_join.rs @@ -18,8 +18,9 @@ pub fn teed_join<'a, S: Stream + Unpin + 'a>( ) -> impl Quoted<'a, Hydroflow<'a>> { let node_zero = flow.process::(); let node_one = flow.process::(); + let n0_tick = node_zero.tick(); - let source = node_zero.source_stream(input_stream).tick_batch(); + let source = node_zero.source_stream(input_stream).tick_batch(&n0_tick); let map1 = source.clone().map(q!(|v| (v + 1, ()))); let map2 = source.map(q!(|v| (v - 1, ()))); From c7b5f32625584b7eedef1e05276c74ee8de5e4a7 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 7 Nov 2024 22:35:39 -0800 Subject: [PATCH 41/74] fix(docs): add a note saying that HF+ docs are WIP (#1552) --- docs/docs/hydroflow_plus/index.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/docs/hydroflow_plus/index.mdx b/docs/docs/hydroflow_plus/index.mdx index fe0edfd2bef4..c1c4d1805d32 100644 --- a/docs/docs/hydroflow_plus/index.mdx +++ b/docs/docs/hydroflow_plus/index.mdx @@ -5,6 +5,12 @@ sidebar_position: 0 # Introduction Hydroflow+ layers a high-level Rust API over the Hydroflow IR, making it possible to write dataflow programs that span multiple processes with straightline, functional Rust code. Hydroflow+ is built on top of [Stageleft](./stageleft.mdx), which allows Hydroflow+ to emit regular Hydroflow programs that are compiled into efficient Rust binaries. It also integrates with [Hydro Deploy](../deploy/index.md) to make it easy to deploy and run Hydroflow+ programs on a cluster. +:::caution + +The docs for Hydroflow+ are still a work in progress. If you have any questions or run into bugs, please file an issue on the [Hydroflow GitHub repository](https://github.com/hydro-project/hydroflow). + +::: + The main logic of Hydroflow+ programs manipulates **streams**, which capture infinite ordered sequences of elements. Streams are transformed using classic functional operators such as `map`, `filter`, and `fold`, as well as relational operators such as `join`. To build **distributed** dataflow programs, Hydroflow+ also introduces the concept of **processes**, which capture _where_ a stream is being processed. ## Setup From 9107841700db0ae72de6269ab6f132be0ae51cd9 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 7 Nov 2024 22:36:35 -0800 Subject: [PATCH 42/74] refactor(hydroflow_plus)!: location type parameter before boundedness (#1551) When looking at a prefix in an IDE, the location type argument is generally more useful. --- .../src/location/external_process.rs | 4 +- hydroflow_plus/src/location/mod.rs | 12 +- hydroflow_plus/src/location/tick.rs | 6 +- hydroflow_plus/src/optional.rs | 110 +++++----- hydroflow_plus/src/singleton.rs | 102 ++++----- hydroflow_plus/src/stream.rs | 202 +++++++++--------- hydroflow_plus_test/src/cluster/paxos.rs | 128 +++++------ .../src/cluster/paxos_bench.rs | 6 +- hydroflow_plus_test/src/cluster/paxos_kv.rs | 12 +- 9 files changed, 294 insertions(+), 288 deletions(-) diff --git a/hydroflow_plus/src/location/external_process.rs b/hydroflow_plus/src/location/external_process.rs index de347154722b..0735083fd328 100644 --- a/hydroflow_plus/src/location/external_process.rs +++ b/hydroflow_plus/src/location/external_process.rs @@ -62,7 +62,7 @@ impl<'a, P> ExternalProcess<'a, P> { pub fn source_external_bytes + NoTick>( &self, to: &L, - ) -> (ExternalBytesPort, Stream) { + ) -> (ExternalBytesPort, Stream) { let next_external_port_id = { let mut flow_state = self.flow_state.borrow_mut(); let id = flow_state.next_external_out; @@ -97,7 +97,7 @@ impl<'a, P> ExternalProcess<'a, P> { pub fn source_external_bincode + NoTick, T: Serialize + DeserializeOwned>( &self, to: &L, - ) -> (ExternalBincodeSink, Stream) { + ) -> (ExternalBincodeSink, Stream) { let next_external_port_id = { let mut flow_state = self.flow_state.borrow_mut(); let id = flow_state.next_external_out; diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index d8dd16750e8d..8908f5827647 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -78,7 +78,7 @@ pub trait Location<'a>: Clone { } } - fn spin(&self) -> Stream<(), Unbounded, Self> + fn spin(&self) -> Stream<(), Self, Unbounded> where Self: Sized + NoTick, { @@ -94,7 +94,7 @@ pub trait Location<'a>: Clone { fn source_stream + Unpin>( &self, e: impl Quoted<'a, E>, - ) -> Stream + ) -> Stream where Self: Sized + NoTick, { @@ -112,7 +112,7 @@ pub trait Location<'a>: Clone { fn source_iter>( &self, e: impl Quoted<'a, E>, - ) -> Stream + ) -> Stream where Self: Sized + NoTick, { @@ -129,7 +129,7 @@ pub trait Location<'a>: Clone { ) } - fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton + fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton where Self: Sized + NoTick, { @@ -156,7 +156,7 @@ pub trait Location<'a>: Clone { fn source_interval( &self, interval: impl Quoted<'a, Duration> + Copy + 'a, - ) -> Stream + ) -> Stream where Self: Sized + NoTick, { @@ -169,7 +169,7 @@ pub trait Location<'a>: Clone { &self, delay: impl Quoted<'a, Duration> + Copy + 'a, interval: impl Quoted<'a, Duration> + Copy + 'a, - ) -> Stream + ) -> Stream where Self: Sized + NoTick, { diff --git a/hydroflow_plus/src/location/tick.rs b/hydroflow_plus/src/location/tick.rs index 895ae45559ad..d974eb14acca 100644 --- a/hydroflow_plus/src/location/tick.rs +++ b/hydroflow_plus/src/location/tick.rs @@ -45,7 +45,7 @@ impl<'a, L: Location<'a>> Tick { pub fn spin_batch( &self, batch_size: impl Quoted<'a, usize> + Copy + 'a, - ) -> Stream<(), Bounded, Self> + ) -> Stream<(), Self, Bounded> where L: NoTick, { @@ -56,7 +56,7 @@ impl<'a, L: Location<'a>> Tick { .tick_batch(self) } - pub fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton + pub fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton where L: NoTick, { @@ -66,7 +66,7 @@ impl<'a, L: Location<'a>> Tick { pub fn singleton_first_tick( &self, e: impl Quoted<'a, T>, - ) -> Optional + ) -> Optional where L: NoTick, { diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index 7ca84bf6be1f..1494f8a667f7 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -12,15 +12,15 @@ use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; use crate::location::{check_matching_location, LocationId, NoTick}; use crate::{Bounded, Location, Singleton, Stream, Tick, Unbounded}; -pub struct Optional { - pub(crate) location: N, +pub struct Optional { + pub(crate) location: L, pub(crate) ir_node: RefCell, - _phantom: PhantomData<(T, N, W)>, + _phantom: PhantomData<(T, L, B)>, } -impl<'a, T, W, N: Location<'a>> Optional { - pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { +impl<'a, T, L: Location<'a>, B> Optional { + pub(crate) fn new(location: L, ir_node: HfPlusNode) -> Self { Optional { location, ir_node: RefCell::new(ir_node), @@ -28,7 +28,7 @@ impl<'a, T, W, N: Location<'a>> Optional { } } - pub fn some(singleton: Singleton) -> Self { + pub fn some(singleton: Singleton) -> Self { Optional::new(singleton.location, singleton.ir_node.into_inner()) } @@ -37,16 +37,16 @@ impl<'a, T, W, N: Location<'a>> Optional { } } -impl<'a, T, N: Location<'a>> DeferTick for Optional> { +impl<'a, T, L: Location<'a>> DeferTick for Optional, Bounded> { fn defer_tick(self) -> Self { Optional::defer_tick(self) } } -impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> { - type Location = Tick; +impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycle> for Optional, Bounded> { + type Location = Tick; - fn create_source(ident: syn::Ident, location: Tick) -> Self { + fn create_source(ident: syn::Ident, location: Tick) -> Self { let location_id = location.id(); Optional::new( location, @@ -58,7 +58,7 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Optional> CycleComplete<'a, TickCycle> for Optional> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycle> for Optional, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -74,10 +74,10 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Optional> CycleCollection<'a, ForwardRef> for Optional> { - type Location = Tick; +impl<'a, T, L: Location<'a>> CycleCollection<'a, ForwardRef> for Optional, Bounded> { + type Location = Tick; - fn create_source(ident: syn::Ident, location: Tick) -> Self { + fn create_source(ident: syn::Ident, location: Tick) -> Self { let location_id = location.id(); Optional::new( location, @@ -89,7 +89,7 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Optional> CycleComplete<'a, ForwardRef> for Optional> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, ForwardRef> for Optional, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -105,10 +105,10 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, ForwardRef> for Optional + NoTick> CycleCollection<'a, ForwardRef> for Optional { - type Location = N; +impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRef> for Optional { + type Location = L; - fn create_source(ident: syn::Ident, location: N) -> Self { + fn create_source(ident: syn::Ident, location: L) -> Self { let location_id = location.id(); Optional::new( location, @@ -120,7 +120,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Opt } } -impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Optional { +impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRef> for Optional { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -136,13 +136,19 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Optio } } -impl<'a, T, W, N: Location<'a>> From> for Optional { - fn from(singleton: Singleton) -> Self { +impl<'a, T, L: Location<'a>> From> for Optional { + fn from(singleton: Optional) -> Self { + Optional::new(singleton.location, singleton.ir_node.into_inner()) + } +} + +impl<'a, T, L: Location<'a>, B> From> for Optional { + fn from(singleton: Singleton) -> Self { Optional::some(singleton) } } -impl<'a, T: Clone, W, N: Location<'a>> Clone for Optional { +impl<'a, T: Clone, L: Location<'a>, B> Clone for Optional { fn clone(&self) -> Self { if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); @@ -166,17 +172,17 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Optional { } } -impl<'a, T, W, N: Location<'a>> Optional { +impl<'a, T, L: Location<'a>, B> Optional { // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream - pub fn into_stream(self) -> Stream { - if N::is_top_level() { + pub fn into_stream(self) -> Stream { + if L::is_top_level() { panic!("Converting an optional to a stream is not yet supported at the top level"); } Stream::new(self.location, self.ir_node.into_inner()) } - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( self.location, HfPlusNode::Map { @@ -189,7 +195,7 @@ impl<'a, T, W, N: Location<'a>> Optional { pub fn flat_map, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location, HfPlusNode::FlatMap { @@ -199,7 +205,7 @@ impl<'a, T, W, N: Location<'a>> Optional { ) } - pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { + pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( self.location, HfPlusNode::Filter { @@ -212,7 +218,7 @@ impl<'a, T, W, N: Location<'a>> Optional { pub fn filter_map Option + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Optional { + ) -> Optional { Optional::new( self.location, HfPlusNode::FilterMap { @@ -222,10 +228,10 @@ impl<'a, T, W, N: Location<'a>> Optional { ) } - pub fn union(self, other: Optional) -> Optional { + pub fn union(self, other: Optional) -> Optional { check_matching_location(&self.location, &other.location); - if N::is_top_level() { + if L::is_top_level() { Optional::new( self.location, HfPlusNode::Persist(Box::new(HfPlusNode::Union( @@ -244,14 +250,14 @@ impl<'a, T, W, N: Location<'a>> Optional { } } - pub fn zip(self, other: impl Into>) -> Optional<(T, O), W, N> + pub fn zip(self, other: impl Into>) -> Optional<(T, O), L, B> where O: Clone, { - let other: Optional = other.into(); + let other: Optional = other.into(); check_matching_location(&self.location, &other.location); - if N::is_top_level() { + if L::is_top_level() { Optional::new( self.location, HfPlusNode::Persist(Box::new(HfPlusNode::CrossSingleton( @@ -270,10 +276,10 @@ impl<'a, T, W, N: Location<'a>> Optional { } } - pub fn unwrap_or(self, other: Singleton) -> Singleton { + pub fn unwrap_or(self, other: Singleton) -> Singleton { check_matching_location(&self.location, &other.location); - if N::is_top_level() { + if L::is_top_level() { Singleton::new( self.location, HfPlusNode::Persist(Box::new(HfPlusNode::Union( @@ -292,7 +298,7 @@ impl<'a, T, W, N: Location<'a>> Optional { } } - pub fn into_singleton(self) -> Singleton, W, N> + pub fn into_singleton(self) -> Singleton, L, B> where T: Clone, { @@ -302,7 +308,7 @@ impl<'a, T, W, N: Location<'a>> Optional { location_kind: self.location.id().root().clone(), })); - let none_singleton = if N::is_top_level() { + let none_singleton = if L::is_top_level() { Singleton::new( self.location.clone(), HfPlusNode::Persist(Box::new(core_ir)), @@ -315,29 +321,29 @@ impl<'a, T, W, N: Location<'a>> Optional { } } -impl<'a, T, N: Location<'a>> Optional { - pub fn continue_if(self, signal: Optional) -> Optional { +impl<'a, T, L: Location<'a>> Optional { + pub fn continue_if(self, signal: Optional) -> Optional { self.zip(signal.map(q!(|_u| ()))).map(q!(|(d, _signal)| d)) } - pub fn continue_unless(self, other: Optional) -> Optional { + pub fn continue_unless(self, other: Optional) -> Optional { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } - pub fn then(self, value: Singleton) -> Optional { + pub fn then(self, value: Singleton) -> Optional { value.continue_if(self) } } -impl<'a, T, B, N: Location<'a> + NoTick> Optional { - pub fn latest_tick(self, tick: &Tick) -> Optional> { +impl<'a, T, L: Location<'a> + NoTick, B> Optional { + pub fn latest_tick(self, tick: &Tick) -> Optional, Bounded> { Optional::new( tick.clone(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } - pub fn tick_samples(self) -> Stream { + pub fn tick_samples(self) -> Stream { let tick = self.location.tick(); self.latest_tick(&tick).all_ticks() } @@ -345,7 +351,7 @@ impl<'a, T, B, N: Location<'a> + NoTick> Optional { pub fn sample_every( self, interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { + ) -> Stream { let samples = self.location.source_interval(interval); let tick = self.location.tick(); @@ -355,36 +361,36 @@ impl<'a, T, B, N: Location<'a> + NoTick> Optional { } } -impl<'a, T, N: Location<'a>> Optional> { - pub fn all_ticks(self) -> Stream { +impl<'a, T, L: Location<'a>> Optional, Bounded> { + pub fn all_ticks(self) -> Stream { Stream::new( self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn latest(self) -> Optional { + pub fn latest(self) -> Optional { Optional::new( self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn defer_tick(self) -> Optional> { + pub fn defer_tick(self) -> Optional, Bounded> { Optional::new( self.location, HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) } - pub fn persist(self) -> Stream> { + pub fn persist(self) -> Stream, Bounded> { Stream::new( self.location, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn delta(self) -> Optional> { + pub fn delta(self) -> Optional, Bounded> { Optional::new( self.location, HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index b153a0947152..394aae8c0fa2 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -14,15 +14,15 @@ use crate::location::{check_matching_location, Location, LocationId, NoTick, Tic use crate::stream::{Bounded, Unbounded}; use crate::{Optional, Stream}; -pub struct Singleton { - pub(crate) location: N, +pub struct Singleton { + pub(crate) location: L, pub(crate) ir_node: RefCell, - _phantom: PhantomData<(T, N, W)>, + _phantom: PhantomData<(T, L, B)>, } -impl<'a, T, W, N: Location<'a>> Singleton { - pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { +impl<'a, T, L: Location<'a>, B> Singleton { + pub(crate) fn new(location: L, ir_node: HfPlusNode) -> Self { Singleton { location, ir_node: RefCell::new(ir_node), @@ -35,24 +35,24 @@ impl<'a, T, W, N: Location<'a>> Singleton { } } -impl<'a, T, N: Location<'a>> From> for Singleton { - fn from(singleton: Singleton) -> Self { +impl<'a, T, L: Location<'a>> From> for Singleton { + fn from(singleton: Singleton) -> Self { Singleton::new(singleton.location, singleton.ir_node.into_inner()) } } -impl<'a, T, N: Location<'a>> DeferTick for Singleton> { +impl<'a, T, L: Location<'a>> DeferTick for Singleton, Bounded> { fn defer_tick(self) -> Self { Singleton::defer_tick(self) } } -impl<'a, T, N: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> - for Singleton> +impl<'a, T, L: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> + for Singleton, Bounded> { - type Location = Tick; + type Location = Tick; - fn create_source(ident: syn::Ident, initial: Self, location: Tick) -> Self { + fn create_source(ident: syn::Ident, initial: Self, location: Tick) -> Self { let location_id = location.id(); Singleton::new( location, @@ -67,7 +67,7 @@ impl<'a, T, N: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> } } -impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycle> for Singleton, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -83,10 +83,10 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> CycleCollection<'a, ForwardRef> for Singleton> { - type Location = Tick; +impl<'a, T, L: Location<'a>> CycleCollection<'a, ForwardRef> for Singleton, Bounded> { + type Location = Tick; - fn create_source(ident: syn::Ident, location: Tick) -> Self { + fn create_source(ident: syn::Ident, location: Tick) -> Self { let location_id = location.id(); Singleton::new( location, @@ -98,7 +98,7 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, ForwardRef> for Singleton> CycleComplete<'a, ForwardRef> for Singleton> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, ForwardRef> for Singleton, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -114,7 +114,7 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, ForwardRef> for Singleton> Clone for Singleton { +impl<'a, T: Clone, L: Location<'a>, B> Clone for Singleton { fn clone(&self) -> Self { if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); @@ -138,13 +138,13 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Singleton { } } -impl<'a, T, W, N: Location<'a>> Singleton { +impl<'a, T, L: Location<'a>, B> Singleton { // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream - pub fn into_stream(self) -> Stream { + pub fn into_stream(self) -> Stream { Stream::new(self.location, self.ir_node.into_inner()) } - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Singleton { + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Singleton { Singleton::new( self.location, HfPlusNode::Map { @@ -157,7 +157,7 @@ impl<'a, T, W, N: Location<'a>> Singleton { pub fn flat_map, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location, HfPlusNode::FlatMap { @@ -167,7 +167,7 @@ impl<'a, T, W, N: Location<'a>> Singleton { ) } - pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { + pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { Optional::new( self.location, HfPlusNode::Filter { @@ -180,7 +180,7 @@ impl<'a, T, W, N: Location<'a>> Singleton { pub fn filter_map Option + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Optional { + ) -> Optional { Optional::new( self.location, HfPlusNode::FilterMap { @@ -192,11 +192,11 @@ impl<'a, T, W, N: Location<'a>> Singleton { pub fn zip(self, other: Other) -> >::Out where - Self: ZipResult<'a, Other, Location = N>, + Self: ZipResult<'a, Other, Location = L>, { check_matching_location(&self.location, &Self::other_location(&other)); - if N::is_top_level() { + if L::is_top_level() { Self::make( self.location, HfPlusNode::Persist(Box::new(HfPlusNode::CrossSingleton( @@ -216,25 +216,25 @@ impl<'a, T, W, N: Location<'a>> Singleton { } } -impl<'a, T, N: Location<'a>> Singleton { - pub fn continue_if(self, signal: Optional) -> Optional { +impl<'a, T, L: Location<'a>> Singleton { + pub fn continue_if(self, signal: Optional) -> Optional { self.zip(signal.map(q!(|_u| ()))).map(q!(|(d, _signal)| d)) } - pub fn continue_unless(self, other: Optional) -> Optional { + pub fn continue_unless(self, other: Optional) -> Optional { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } } -impl<'a, T, B, N: Location<'a> + NoTick> Singleton { - pub fn latest_tick(self, tick: &Tick) -> Singleton> { +impl<'a, T, L: Location<'a> + NoTick, B> Singleton { + pub fn latest_tick(self, tick: &Tick) -> Singleton, Bounded> { Singleton::new( tick.clone(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } - pub fn tick_samples(self) -> Stream { + pub fn tick_samples(self) -> Stream { let tick = self.location.tick(); self.latest_tick(&tick).all_ticks() } @@ -242,7 +242,7 @@ impl<'a, T, B, N: Location<'a> + NoTick> Singleton { pub fn sample_every( self, interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { + ) -> Stream { let samples = self.location.source_interval(interval); let tick = self.location.tick(); @@ -252,36 +252,36 @@ impl<'a, T, B, N: Location<'a> + NoTick> Singleton { } } -impl<'a, T, N: Location<'a>> Singleton> { - pub fn all_ticks(self) -> Stream { +impl<'a, T, L: Location<'a>> Singleton, Bounded> { + pub fn all_ticks(self) -> Stream { Stream::new( self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn latest(self) -> Singleton { + pub fn latest(self) -> Singleton { Singleton::new( self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn defer_tick(self) -> Singleton> { + pub fn defer_tick(self) -> Singleton, Bounded> { Singleton::new( self.location, HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) } - pub fn persist(self) -> Stream> { + pub fn persist(self) -> Stream, Bounded> { Stream::new( self.location, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn delta(self) -> Optional> { + pub fn delta(self) -> Optional, Bounded> { Optional::new( self.location, HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), @@ -299,36 +299,36 @@ pub trait ZipResult<'a, Other> { fn make(location: Self::Location, ir_node: HfPlusNode) -> Self::Out; } -impl<'a, T, U: Clone, W, N: Location<'a>> ZipResult<'a, Singleton> for Singleton { - type Out = Singleton<(T, U), W, N>; - type Location = N; +impl<'a, T, U: Clone, L: Location<'a>, B> ZipResult<'a, Singleton> for Singleton { + type Out = Singleton<(T, U), L, B>; + type Location = L; - fn other_location(other: &Singleton) -> N { + fn other_location(other: &Singleton) -> L { other.location.clone() } - fn other_ir_node(other: Singleton) -> HfPlusNode { + fn other_ir_node(other: Singleton) -> HfPlusNode { other.ir_node.into_inner() } - fn make(location: N, ir_node: HfPlusNode) -> Self::Out { + fn make(location: L, ir_node: HfPlusNode) -> Self::Out { Singleton::new(location, ir_node) } } -impl<'a, T, U: Clone, W, N: Location<'a>> ZipResult<'a, Optional> for Singleton { - type Out = Optional<(T, U), W, N>; - type Location = N; +impl<'a, T, U: Clone, L: Location<'a>, B> ZipResult<'a, Optional> for Singleton { + type Out = Optional<(T, U), L, B>; + type Location = L; - fn other_location(other: &Optional) -> N { + fn other_location(other: &Optional) -> L { other.location.clone() } - fn other_ir_node(other: Optional) -> HfPlusNode { + fn other_ir_node(other: Optional) -> HfPlusNode { other.ir_node.into_inner() } - fn make(location: N, ir_node: HfPlusNode) -> Self::Out { + fn make(location: L, ir_node: HfPlusNode) -> Self::Out { Optional::new(location, ir_node) } } diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 3f1e1803cc94..f85eb32aa2dc 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -31,36 +31,36 @@ pub enum Unbounded {} /// to be complete in finite time. pub enum Bounded {} -/// An infinite stream of elements of type `T`. +/// An ordered sequence stream of elements of type `T`. /// /// Type Parameters: /// - `T`: the type of elements in the stream +/// - `L`: the location where the stream is being materialized /// - `B`: the boundedness of the stream, which is either [`Bounded`] /// or [`Unbounded`] -/// - `N`: the type of the node that the stream is materialized on -pub struct Stream { - location: N, +pub struct Stream { + location: L, pub(crate) ir_node: RefCell, - _phantom: PhantomData<(T, B, N)>, + _phantom: PhantomData<(T, L, B)>, } -impl<'a, T, W, N: Location<'a>> Stream { +impl<'a, T, L: Location<'a>, B> Stream { fn location_kind(&self) -> LocationId { self.location.id() } } -impl<'a, T, N: Location<'a>> DeferTick for Stream> { +impl<'a, T, L: Location<'a>> DeferTick for Stream, Bounded> { fn defer_tick(self) -> Self { Stream::defer_tick(self) } } -impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream> { - type Location = Tick; +impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycle> for Stream, Bounded> { + type Location = Tick; - fn create_source(ident: syn::Ident, location: Tick) -> Self { + fn create_source(ident: syn::Ident, location: Tick) -> Self { let location_id = location.id(); Stream::new( location, @@ -72,7 +72,7 @@ impl<'a, T, N: Location<'a>> CycleCollection<'a, TickCycle> for Stream> CycleComplete<'a, TickCycle> for Stream> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycle> for Stream, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -88,10 +88,10 @@ impl<'a, T, N: Location<'a>> CycleComplete<'a, TickCycle> for Stream + NoTick> CycleCollection<'a, ForwardRef> for Stream { - type Location = N; +impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRef> for Stream { + type Location = L; - fn create_source(ident: syn::Ident, location: N) -> Self { + fn create_source(ident: syn::Ident, location: L) -> Self { let location_id = location.id(); Stream::new( location, @@ -103,7 +103,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleCollection<'a, ForwardRef> for Str } } -impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Stream { +impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRef> for Stream { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -119,8 +119,8 @@ impl<'a, T, W, N: Location<'a> + NoTick> CycleComplete<'a, ForwardRef> for Strea } } -impl<'a, T, W, N: Location<'a>> Stream { - pub(crate) fn new(location: N, ir_node: HfPlusNode) -> Self { +impl<'a, T, L: Location<'a>, B> Stream { + pub(crate) fn new(location: L, ir_node: HfPlusNode) -> Self { Stream { location, ir_node: RefCell::new(ir_node), @@ -129,7 +129,7 @@ impl<'a, T, W, N: Location<'a>> Stream { } } -impl<'a, T: Clone, W, N: Location<'a>> Clone for Stream { +impl<'a, T: Clone, L: Location<'a>, B> Clone for Stream { fn clone(&self) -> Self { if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); @@ -153,8 +153,8 @@ impl<'a, T: Clone, W, N: Location<'a>> Clone for Stream { } } -impl<'a, T, W, N: Location<'a>> Stream { - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { +impl<'a, T, L: Location<'a>, B> Stream { + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( self.location, HfPlusNode::Map { @@ -164,7 +164,7 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - pub fn cloned(self) -> Stream + pub fn cloned(self) -> Stream where T: Clone, { @@ -174,7 +174,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn flat_map, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location, HfPlusNode::FlatMap { @@ -184,14 +184,14 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - pub fn flatten(self) -> Stream + pub fn flatten(self) -> Stream where T: IntoIterator, { self.flat_map(q!(|d| d)) } - pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { + pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { Stream::new( self.location, HfPlusNode::Filter { @@ -204,7 +204,7 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn filter_map Option + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location, HfPlusNode::FilterMap { @@ -216,12 +216,12 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn cross_singleton( self, - other: impl Into>, - ) -> Stream<(T, O), W, N> + other: impl Into>, + ) -> Stream<(T, O), L, B> where O: Clone, { - let other: Optional = other.into(); + let other: Optional = other.into(); check_matching_location(&self.location, &other.location); Stream::new( @@ -234,17 +234,17 @@ impl<'a, T, W, N: Location<'a>> Stream { } /// Allow this stream through if the other stream has elements, otherwise the output is empty. - pub fn continue_if(self, signal: Optional) -> Stream { + pub fn continue_if(self, signal: Optional) -> Stream { self.cross_singleton(signal.map(q!(|_u| ()))) .map(q!(|(d, _signal)| d)) } /// Allow this stream through if the other stream is empty, otherwise the output is empty. - pub fn continue_unless(self, other: Optional) -> Stream { + pub fn continue_unless(self, other: Optional) -> Stream { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } - pub fn cross_product(self, other: Stream) -> Stream<(T, O), W, N> + pub fn cross_product(self, other: Stream) -> Stream<(T, O), L, B> where T: Clone, O: Clone, @@ -260,7 +260,7 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - pub fn union(self, other: Stream) -> Stream { + pub fn union(self, other: Stream) -> Stream { check_matching_location(&self.location, &other.location); Stream::new( @@ -272,14 +272,14 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - pub fn enumerate(self) -> Stream<(usize, T), W, N> { + pub fn enumerate(self) -> Stream<(usize, T), L, B> { Stream::new( self.location, HfPlusNode::Enumerate(Box::new(self.ir_node.into_inner())), ) } - pub fn unique(self) -> Stream + pub fn unique(self) -> Stream where T: Eq + Hash, { @@ -289,7 +289,7 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - pub fn filter_not_in(self, other: Stream) -> Stream + pub fn filter_not_in(self, other: Stream) -> Stream where T: Eq + Hash, { @@ -304,12 +304,12 @@ impl<'a, T, W, N: Location<'a>> Stream { ) } - pub fn first(self) -> Optional { + pub fn first(self) -> Optional { Optional::new(self.location, self.ir_node.into_inner()) } - pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { - if N::is_top_level() { + pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { + if L::is_top_level() { Stream::new( self.location, HfPlusNode::Persist(Box::new(HfPlusNode::Inspect { @@ -332,14 +332,14 @@ impl<'a, T, W, N: Location<'a>> Stream { self, init: impl IntoQuotedMut<'a, I>, comb: impl IntoQuotedMut<'a, F>, - ) -> Singleton { + ) -> Singleton { let mut core = HfPlusNode::Fold { init: init.splice_fn0().into(), acc: comb.splice_fn2_borrow_mut().into(), input: Box::new(self.ir_node.into_inner()), }; - if N::is_top_level() { + if L::is_top_level() { // top-level (possibly unbounded) singletons are represented as // a stream which produces all values from all ticks every tick, // so Unpersist will always give the lastest aggregation @@ -352,20 +352,20 @@ impl<'a, T, W, N: Location<'a>> Stream { pub fn reduce( self, comb: impl IntoQuotedMut<'a, F>, - ) -> Optional { + ) -> Optional { let mut core = HfPlusNode::Reduce { f: comb.splice_fn2_borrow_mut().into(), input: Box::new(self.ir_node.into_inner()), }; - if N::is_top_level() { + if L::is_top_level() { core = HfPlusNode::Persist(Box::new(core)); } Optional::new(self.location, core) } - pub fn max(self) -> Optional + pub fn max(self) -> Optional where T: Ord, { @@ -376,7 +376,7 @@ impl<'a, T, W, N: Location<'a>> Stream { })) } - pub fn min(self) -> Optional + pub fn min(self) -> Optional where T: Ord, { @@ -387,13 +387,13 @@ impl<'a, T, W, N: Location<'a>> Stream { })) } - pub fn count(self) -> Singleton { + pub fn count(self) -> Singleton { self.fold(q!(|| 0usize), q!(|count, _| *count += 1)) } } -impl<'a, T, N: Location<'a>> Stream { - pub fn sort(self) -> Stream +impl<'a, T, L: Location<'a>> Stream { + pub fn sort(self) -> Stream where T: Ord, { @@ -404,8 +404,8 @@ impl<'a, T, N: Location<'a>> Stream { } } -impl<'a, K, V1, W, N: Location<'a>> Stream<(K, V1), W, N> { - pub fn join(self, n: Stream<(K, V2), W, N>) -> Stream<(K, (V1, V2)), W, N> +impl<'a, K, V1, L: Location<'a>, B> Stream<(K, V1), L, B> { + pub fn join(self, n: Stream<(K, V2), L, B>) -> Stream<(K, (V1, V2)), L, B> where K: Eq + Hash, { @@ -420,7 +420,7 @@ impl<'a, K, V1, W, N: Location<'a>> Stream<(K, V1), W, N> { ) } - pub fn anti_join(self, n: Stream) -> Stream<(K, V1), W, N> + pub fn anti_join(self, n: Stream) -> Stream<(K, V1), L, B> where K: Eq + Hash, { @@ -436,12 +436,12 @@ impl<'a, K, V1, W, N: Location<'a>> Stream<(K, V1), W, N> { } } -impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { +impl<'a, K: Eq + Hash, V, L: Location<'a>> Stream<(K, V), Tick, Bounded> { pub fn fold_keyed A + 'a, F: Fn(&mut A, V) + 'a>( self, init: impl IntoQuotedMut<'a, I>, comb: impl IntoQuotedMut<'a, F>, - ) -> Stream<(K, A), Bounded, Tick> { + ) -> Stream<(K, A), Tick, Bounded> { Stream::new( self.location, HfPlusNode::FoldKeyed { @@ -455,7 +455,7 @@ impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { pub fn reduce_keyed( self, comb: impl IntoQuotedMut<'a, F>, - ) -> Stream<(K, V), Bounded, Tick> { + ) -> Stream<(K, V), Tick, Bounded> { Stream::new( self.location, HfPlusNode::ReduceKeyed { @@ -466,15 +466,15 @@ impl<'a, K: Eq + Hash, V, N: Location<'a>> Stream<(K, V), Bounded, Tick> { } } -impl<'a, T, W, N: Location<'a> + NoTick> Stream { - pub fn tick_batch(self, tick: &Tick) -> Stream> { +impl<'a, T, L: Location<'a> + NoTick, B> Stream { + pub fn tick_batch(self, tick: &Tick) -> Stream, Bounded> { Stream::new( tick.clone(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } - pub fn tick_prefix(self, tick: &Tick) -> Stream> + pub fn tick_prefix(self, tick: &Tick) -> Stream, Bounded> where T: Clone, { @@ -484,7 +484,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn sample_every( self, interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { + ) -> Stream { let samples = self.location.source_interval(interval); let tick = self.location.tick(); self.tick_batch(&tick) @@ -519,15 +519,15 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { } } -impl<'a, T, N: Location<'a>> Stream> { - pub fn all_ticks(self) -> Stream { +impl<'a, T, L: Location<'a>> Stream, Bounded> { + pub fn all_ticks(self) -> Stream { Stream::new( self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn persist(self) -> Stream> + pub fn persist(self) -> Stream, Bounded> where T: Clone, { @@ -537,14 +537,14 @@ impl<'a, T, N: Location<'a>> Stream> { ) } - pub fn defer_tick(self) -> Stream> { + pub fn defer_tick(self) -> Stream, Bounded> { Stream::new( self.location, HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) } - pub fn delta(self) -> Stream> { + pub fn delta(self) -> Stream, Bounded> { Stream::new( self.location, HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), @@ -593,13 +593,13 @@ pub(super) fn deserialize_bincode(tagged: Option } } -impl<'a, T, W, N: Location<'a> + NoTick> Stream { +impl<'a, T, L: Location<'a> + NoTick, B> Stream { pub fn decouple_process( self, other: &Process<'a, P2>, - ) -> Stream> + ) -> Stream, Unbounded> where - N: CanSend<'a, Process<'a, P2>, In = T, Out = T>, + L: CanSend<'a, Process<'a, P2>, In = T, Out = T>, T: Clone + Serialize + DeserializeOwned, { self.send_bincode::, T>(other) @@ -608,9 +608,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn decouple_cluster( self, other: &Cluster<'a, C2>, - ) -> Stream> + ) -> Stream, Unbounded> where - N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)>, + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)>, T: Clone + Serialize + DeserializeOwned, { let self_node_id = match self.location_kind() { @@ -625,17 +625,17 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { .send_bincode_interleaved(other) } - pub fn send_bincode, CoreType>( + pub fn send_bincode, CoreType>( self, - other: &N2, - ) -> Stream, Unbounded, N2> + other: &L2, + ) -> Stream, L2, Unbounded> where - N: CanSend<'a, N2, In = T>, + L: CanSend<'a, L2, In = T>, CoreType: Serialize + DeserializeOwned, { - let serialize_pipeline = Some(serialize_bincode::(N::is_demux())); + let serialize_pipeline = Some(serialize_bincode::(L::is_demux())); - let deserialize_pipeline = Some(deserialize_bincode::(N::tagged_type())); + let deserialize_pipeline = Some(deserialize_bincode::(L::tagged_type())); Stream::new( other.clone(), @@ -652,16 +652,16 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { ) } - pub fn send_bincode_external( + pub fn send_bincode_external( self, - other: &ExternalProcess, - ) -> ExternalBincodeStream> + other: &ExternalProcess, + ) -> ExternalBincodeStream> where - N: CanSend<'a, ExternalProcess<'a, N2>, In = T, Out = CoreType>, + L: CanSend<'a, ExternalProcess<'a, L2>, In = T, Out = CoreType>, CoreType: Serialize + DeserializeOwned, // for now, we restirct Out to be CoreType, which means no tagged cluster -> external { - let serialize_pipeline = Some(serialize_bincode::(N::is_demux())); + let serialize_pipeline = Some(serialize_bincode::(L::is_demux())); let mut flow_state_borrow = self.location.flow_state().borrow_mut(); @@ -693,9 +693,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { } } - pub fn send_bytes>(self, other: &N2) -> Stream, Unbounded, N2> + pub fn send_bytes>(self, other: &L2) -> Stream, L2, Unbounded> where - N: CanSend<'a, N2, In = T>, + L: CanSend<'a, L2, In = T>, { let root = get_this_crate(); Stream::new( @@ -707,7 +707,7 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { to_key: None, serialize_pipeline: None, instantiate_fn: DebugInstantiate::Building(), - deserialize_pipeline: if let Some(c_type) = N::tagged_type() { + deserialize_pipeline: if let Some(c_type) = L::tagged_type() { Some( parse_quote!(map(|(id, b)| (#root::ClusterId<#c_type>::from_raw(id), b.unwrap().freeze()))), ) @@ -719,9 +719,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { ) } - pub fn send_bytes_external(self, other: &ExternalProcess) -> ExternalBytesPort + pub fn send_bytes_external(self, other: &ExternalProcess) -> ExternalBytesPort where - N: CanSend<'a, ExternalProcess<'a, N2>, In = T, Out = Bytes>, + L: CanSend<'a, ExternalProcess<'a, L2>, In = T, Out = Bytes>, { let mut flow_state_borrow = self.location.flow_state().borrow_mut(); let external_key = flow_state_borrow.next_external_out; @@ -751,33 +751,33 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { } } - pub fn send_bincode_interleaved, Tag, CoreType>( + pub fn send_bincode_interleaved, Tag, CoreType>( self, - other: &N2, - ) -> Stream + other: &L2, + ) -> Stream where - N: CanSend<'a, N2, In = T, Out = (Tag, CoreType)>, + L: CanSend<'a, L2, In = T, Out = (Tag, CoreType)>, CoreType: Serialize + DeserializeOwned, { - self.send_bincode::(other).map(q!(|(_, b)| b)) + self.send_bincode::(other).map(q!(|(_, b)| b)) } - pub fn send_bytes_interleaved, Tag>( + pub fn send_bytes_interleaved, Tag>( self, - other: &N2, - ) -> Stream + other: &L2, + ) -> Stream where - N: CanSend<'a, N2, In = T, Out = (Tag, Bytes)>, + L: CanSend<'a, L2, In = T, Out = (Tag, Bytes)>, { - self.send_bytes::(other).map(q!(|(_, b)| b)) + self.send_bytes::(other).map(q!(|(_, b)| b)) } pub fn broadcast_bincode( self, other: &Cluster<'a, C2>, - ) -> Stream, Unbounded, Cluster<'a, C2>> + ) -> Stream, Cluster<'a, C2>, Unbounded> where - N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, T: Clone + Serialize + DeserializeOwned, { let ids = other.members(); @@ -792,9 +792,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn broadcast_bincode_interleaved( self, other: &Cluster<'a, C2>, - ) -> Stream> + ) -> Stream, Unbounded> where - N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, T: Clone + Serialize + DeserializeOwned, { self.broadcast_bincode(other).map(q!(|(_, b)| b)) @@ -803,9 +803,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn broadcast_bytes( self, other: &Cluster<'a, C2>, - ) -> Stream, Unbounded, Cluster<'a, C2>> + ) -> Stream, Cluster<'a, C2>, Unbounded> where - N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, T: Clone, { let ids = other.members(); @@ -820,9 +820,9 @@ impl<'a, T, W, N: Location<'a> + NoTick> Stream { pub fn broadcast_bytes_interleaved( self, other: &Cluster<'a, C2>, - ) -> Stream> + ) -> Stream, Unbounded> where - N: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + 'a, T: Clone, { diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index a5d14f794063..85b9e4c06940 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -62,15 +62,15 @@ struct P2b

{ pub fn paxos_core<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, - r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Unbounded, Cluster<'a, Acceptor>>, - c_to_proposers: Stream>, + r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Cluster<'a, Acceptor>, Unbounded>, + c_to_proposers: Stream, Unbounded>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, ) -> ( - Stream<(), Unbounded, Cluster<'a, Proposer>>, - Stream<(usize, Option

), Unbounded, Cluster<'a, Proposer>>, + Stream<(), Cluster<'a, Proposer>, Unbounded>, + Stream<(usize, Option

), Cluster<'a, Proposer>, Unbounded>, ) { proposers .source_iter(q!(["Proposers say hello"])) @@ -154,13 +154,13 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, - p_received_p2b_ballots: Stream>, - a_log: Singleton>>, + p_received_p2b_ballots: Stream, Unbounded>, + a_log: Singleton>, Bounded>, ) -> ( - Singleton>>, - Optional>>, - Stream, Bounded, Tick>>, - Singleton>>, + Singleton>, Bounded>, + Optional>, Bounded>, + Stream, Tick>, Bounded>, + Singleton>, Bounded>, ) { let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b_forward_ref) = proposers.forward_ref::, _, _>>(); @@ -223,10 +223,10 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( // Proposer logic to calculate the largest ballot received so far. fn p_max_ballot<'a>( proposers: &Cluster<'a, Proposer>, - p_received_p1b_ballots: Stream>, - p_received_p2b_ballots: Stream>, - p_to_proposers_i_am_leader: Stream>, -) -> Singleton> { + p_received_p1b_ballots: Stream, Unbounded>, + p_received_p2b_ballots: Stream, Unbounded>, + p_to_proposers_i_am_leader: Stream, Unbounded>, +) -> Singleton, Unbounded> { p_received_p1b_ballots .union(p_received_p2b_ballots) .union(p_to_proposers_i_am_leader) @@ -242,10 +242,10 @@ fn p_max_ballot<'a>( fn p_ballot_calc<'a>( proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, - p_received_max_ballot: Singleton>>, + p_received_max_ballot: Singleton>, Bounded>, ) -> ( - Singleton>>, - Optional<(Ballot, u32), Bounded, Tick>>, + Singleton>, Bounded>, + Optional<(Ballot, u32), Tick>, Bounded>, ) { let p_id = proposers.self_id(); let (p_ballot_num_complete_cycle, p_ballot_num) = @@ -285,10 +285,10 @@ fn p_ballot_calc<'a>( fn p_leader_expired<'a>( proposer_tick: &Tick>, - p_to_proposers_i_am_leader: Stream>, - p_is_leader: Optional>>, + p_to_proposers_i_am_leader: Stream, Unbounded>, + p_is_leader: Optional>, Bounded>, i_am_leader_check_timeout: u64, // How often to check if heartbeat expired -) -> Optional, Bounded, Tick>> { +) -> Optional, Tick>, Bounded> { let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold( q!(|| None), q!(|latest, _| { @@ -314,14 +314,14 @@ fn p_leader_expired<'a>( fn p_leader_heartbeat<'a>( proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, - p_is_leader: Optional>>, - p_ballot_num: Singleton>>, + p_is_leader: Optional>, Bounded>, + p_ballot_num: Singleton>, Bounded>, i_am_leader_send_timeout: u64, // How often to heartbeat i_am_leader_check_timeout: u64, // How often to check if heartbeat expired i_am_leader_check_timeout_delay_multiplier: usize, /* Initial delay, multiplied by proposer pid, to stagger proposers checking for timeouts */ ) -> ( - Stream>, - Optional, Bounded, Tick>>, + Stream, Unbounded>, + Optional, Tick>, Bounded>, ) { let p_id = proposers.self_id(); let p_to_proposers_i_am_leader = p_is_leader @@ -359,11 +359,11 @@ fn p_leader_heartbeat<'a>( // Proposer logic to send "I am leader" messages periodically to other proposers, or send p1a to acceptors if other leaders expired. fn p_p1a<'a>( - p_ballot_num: Singleton>>, - p_trigger_election: Optional, Bounded, Tick>>, + p_ballot_num: Singleton>, Bounded>, + p_trigger_election: Optional, Tick>, Bounded>, proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, -) -> Stream> { +) -> Stream, Unbounded> { let p_id = proposers.self_id(); p_trigger_election @@ -382,12 +382,12 @@ fn p_p1a<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( acceptor_tick: &Tick>, - p_to_acceptors_p1a: Stream>, - a_log: Singleton>>, + p_to_acceptors_p1a: Stream, Unbounded>, + a_log: Singleton>, Bounded>, proposers: &Cluster<'a, Proposer>, ) -> ( - Singleton>>, - Stream, Unbounded, Cluster<'a, Proposer>>, + Singleton>, Bounded>, + Stream, Cluster<'a, Proposer>, Unbounded>, ) { let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(acceptor_tick); let a_max_ballot = p_to_acceptors_p1a @@ -424,13 +424,13 @@ fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, - a_to_proposers_p1b: Stream, Unbounded, Cluster<'a, Proposer>>, - p_ballot_num: Singleton>>, - p_has_largest_ballot: Optional<(Ballot, u32), Bounded, Tick>>, + a_to_proposers_p1b: Stream, Cluster<'a, Proposer>, Unbounded>, + p_ballot_num: Singleton>, Bounded>, + p_has_largest_ballot: Optional<(Ballot, u32), Tick>, Bounded>, f: usize, ) -> ( - Optional>>, - Stream, Bounded, Tick>>, + Optional>, Bounded>, + Stream, Tick>, Bounded>, ) { let p_id = proposers.self_id(); let p_relevant_p1bs = a_to_proposers_p1b @@ -457,13 +457,13 @@ fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn recommit_after_leader_election<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, - p_relevant_p1bs: Stream>>, Bounded, Tick>>, - p_ballot_num: Singleton>>, + p_relevant_p1bs: Stream>>, Tick>, Bounded>, + p_ballot_num: Singleton>, Bounded>, f: usize, ) -> ( - Stream, Bounded, Tick>>, - Optional>>, - Stream, Bounded, Tick>>, + Stream, Tick>, Bounded>, + Optional>, Bounded>, + Stream, Tick>, Bounded>, ) { let p_id = proposers.self_id(); @@ -541,21 +541,21 @@ fn sequence_payload<'a, P: PaxosPayload, R>( acceptors: &Cluster<'a, Acceptor>, proposer_tick: &Tick>, acceptor_tick: &Tick>, - c_to_proposers: Stream>, - r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Unbounded, Cluster<'a, Acceptor>>, + c_to_proposers: Stream, Unbounded>, + r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Cluster<'a, Acceptor>, Unbounded>, - p_ballot_num: Singleton>>, - p_is_leader: Optional>>, - p_max_slot: Optional>>, + p_ballot_num: Singleton>, Bounded>, + p_is_leader: Optional>, Bounded>, + p_max_slot: Optional>, Bounded>, - p_log_to_recommit: Stream, Bounded, Tick>>, + p_log_to_recommit: Stream, Tick>, Bounded>, f: usize, - a_max_ballot: Singleton>>, + a_max_ballot: Singleton>, Bounded>, ) -> ( - Stream<(usize, Option

), Unbounded, Cluster<'a, Proposer>>, - Singleton<(Option, HashMap>), Bounded, Tick>>, - Stream, Unbounded, Cluster<'a, Proposer>>, + Stream<(usize, Option

), Cluster<'a, Proposer>, Unbounded>, + Singleton<(Option, HashMap>), Tick>, Bounded>, + Stream, Cluster<'a, Proposer>, Unbounded>, ) { let p_to_acceptors_p2a = p_p2a( proposers, @@ -595,13 +595,13 @@ enum CheckpointOrP2a

{ fn p_p2a<'a, P: PaxosPayload>( proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, - p_max_slot: Optional>>, - c_to_proposers: Stream>, - p_ballot_num: Singleton>>, - p_log_to_recommit: Stream, Bounded, Tick>>, - p_is_leader: Optional>>, + p_max_slot: Optional>, Bounded>, + c_to_proposers: Stream, Unbounded>, + p_ballot_num: Singleton>, Bounded>, + p_log_to_recommit: Stream, Tick>, Bounded>, + p_is_leader: Optional>, Bounded>, acceptors: &Cluster<'a, Acceptor>, -) -> Stream, Unbounded, Cluster<'a, Acceptor>> { +) -> Stream, Cluster<'a, Acceptor>, Unbounded> { let p_id = proposers.self_id(); let (p_next_slot_complete_cycle, p_next_slot) = proposer_tick.cycle::>(); let p_next_slot_after_reconciling_p1bs = p_max_slot @@ -649,14 +649,14 @@ fn p_p2a<'a, P: PaxosPayload>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p2<'a, P: PaxosPayload, R>( acceptor_tick: &Tick>, - a_max_ballot: Singleton>>, - p_to_acceptors_p2a: Stream, Unbounded, Cluster<'a, Acceptor>>, - r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Unbounded, Cluster<'a, Acceptor>>, + a_max_ballot: Singleton>, Bounded>, + p_to_acceptors_p2a: Stream, Cluster<'a, Acceptor>, Unbounded>, + r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Cluster<'a, Acceptor>, Unbounded>, proposers: &Cluster<'a, Proposer>, f: usize, ) -> ( - Singleton<(Option, HashMap>), Bounded, Tick>>, - Stream, Unbounded, Cluster<'a, Proposer>>, + Singleton<(Option, HashMap>), Tick>, Bounded>, + Stream, Cluster<'a, Proposer>, Unbounded>, ) { let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(acceptor_tick); @@ -747,9 +747,9 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( fn p_p2b<'a, P: PaxosPayload>( proposer_tick: &Tick>, - a_to_proposers_p2b: Stream, Unbounded, Cluster<'a, Proposer>>, + a_to_proposers_p2b: Stream, Cluster<'a, Proposer>, Unbounded>, f: usize, -) -> Stream<(usize, Option

), Unbounded, Cluster<'a, Proposer>> { +) -> Stream<(usize, Option

), Cluster<'a, Proposer>, Unbounded> { let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposer_tick.cycle(); let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposer_tick.cycle(); let p_p2b = a_to_proposers_p2b diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index e9c59925b274..e3db1458a809 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -72,17 +72,17 @@ pub fn paxos_bench<'a>( // Clients. All relations for clients will be prefixed with c. All ClientPayloads will contain the virtual client number as key and the client's machine ID (to string) as value. Expects p_to_clients_leader_elected containing Ballots whenever the leader is elected, and r_to_clients_payload_applied containing ReplicaPayloads whenever a payload is committed. Outputs (leader address, ClientPayload) when a new leader is elected or when the previous payload is committed. fn bench_client<'a>( clients: &Cluster<'a, Client>, - p_to_clients_leader_elected: Stream, Unbounded, Cluster<'a, Client>>, + p_to_clients_leader_elected: Stream, Cluster<'a, Client>, Unbounded>, transaction_cycle: impl FnOnce( Stream< (ClusterId, KvPayload>), - Unbounded, Cluster<'a, Client>, + Unbounded, >, ) -> Stream< (ClusterId, KvPayload>), - Unbounded, Cluster<'a, Client>, + Unbounded, >, num_clients_per_node: usize, median_latency_window_size: usize, diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 840e30cc9af7..9aeef0d9b7c4 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -51,15 +51,15 @@ pub fn paxos_kv<'a, K: KvKey, V: KvValue>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, replicas: &Cluster<'a, Replica>, - c_to_proposers: Stream, Unbounded, Cluster<'a, Proposer>>, + c_to_proposers: Stream, Cluster<'a, Proposer>, Unbounded>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, checkpoint_frequency: usize, ) -> ( - Stream<(), Unbounded, Cluster<'a, Proposer>>, - Stream, Unbounded, Cluster<'a, Replica>>, + Stream<(), Cluster<'a, Proposer>, Unbounded>, + Stream, Cluster<'a, Replica>, Unbounded>, ) { let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = replicas.forward_ref::>(); @@ -92,11 +92,11 @@ pub fn paxos_kv<'a, K: KvKey, V: KvValue>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] pub fn replica<'a, K: KvKey, V: KvValue>( replicas: &Cluster<'a, Replica>, - p_to_replicas: Stream, Unbounded, Cluster<'a, Replica>>, + p_to_replicas: Stream, Cluster<'a, Replica>, Unbounded>, checkpoint_frequency: usize, ) -> ( - Stream>, - Stream, Unbounded, Cluster<'a, Replica>>, + Stream, Unbounded>, + Stream, Cluster<'a, Replica>, Unbounded>, ) { let replica_tick = replicas.tick(); From baedf23eaa056bc0dad8331d116bb71176764206 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 8 Nov 2024 08:10:48 -0800 Subject: [PATCH 43/74] feat(hydroflow_plus, docs): improve quickstart ergonomics (#1553) --- docs/docs/hydroflow_plus/index.mdx | 28 ++------ .../hydroflow_plus/quickstart/clusters.mdx | 6 +- .../quickstart/first-dataflow.mdx | 45 ++++++++++++ docs/docs/hydroflow_plus/quickstart/index.mdx | 6 +- .../hydroflow_plus/quickstart/structure.mdx | 58 ---------------- hydroflow_plus/src/builder/built.rs | 4 +- hydroflow_plus/src/builder/deploy.rs | 14 ++-- hydroflow_plus/src/deploy/deploy_graph.rs | 68 +++++++++++++++++-- hydroflow_plus/src/deploy/mod.rs | 12 ++++ hydroflow_plus/src/lib.rs | 1 + hydroflow_plus/src/location/process.rs | 2 +- hydroflow_plus/src/stream.rs | 11 ++- hydroflow_plus_test/examples/compute_pi.rs | 4 +- .../examples/first_ten_distributed.rs | 2 +- hydroflow_plus_test/examples/map_reduce.rs | 4 +- hydroflow_plus_test/examples/paxos.rs | 12 ++-- .../examples/perf_compute_pi.rs | 28 ++++---- .../examples/simple_cluster.rs | 4 +- hydroflow_plus_test/examples/two_pc.rs | 4 +- hydroflow_plus_test/src/cluster/compute_pi.rs | 1 - .../src/cluster/many_to_many.rs | 10 +-- hydroflow_plus_test/src/cluster/map_reduce.rs | 1 - hydroflow_plus_test/src/cluster/paxos.rs | 1 - .../src/cluster/paxos_bench.rs | 1 - hydroflow_plus_test/src/cluster/paxos_kv.rs | 1 - .../src/cluster/simple_cluster.rs | 30 ++------ hydroflow_plus_test/src/cluster/two_pc.rs | 1 - .../src/distributed/first_ten.rs | 13 ++-- .../src/local/compute_pi.rs | 1 - .../src/local/first_ten.rs | 1 - .../src/local/graph_reachability.rs | 1 - template/hydroflow_plus/README.md | 7 +- template/hydroflow_plus/examples/first_ten.rs | 17 +++++ .../examples/first_ten_distributed.rs | 5 +- template/hydroflow_plus/src/first_ten.rs | 7 ++ .../src/first_ten_distributed.rs | 7 +- template/hydroflow_plus/src/lib.rs | 1 + 37 files changed, 214 insertions(+), 205 deletions(-) create mode 100644 docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx delete mode 100644 docs/docs/hydroflow_plus/quickstart/structure.mdx create mode 100644 template/hydroflow_plus/examples/first_ten.rs create mode 100644 template/hydroflow_plus/src/first_ten.rs diff --git a/docs/docs/hydroflow_plus/index.mdx b/docs/docs/hydroflow_plus/index.mdx index c1c4d1805d32..77a9ee57362c 100644 --- a/docs/docs/hydroflow_plus/index.mdx +++ b/docs/docs/hydroflow_plus/index.mdx @@ -3,32 +3,14 @@ sidebar_position: 0 --- # Introduction -Hydroflow+ layers a high-level Rust API over the Hydroflow IR, making it possible to write dataflow programs that span multiple processes with straightline, functional Rust code. Hydroflow+ is built on top of [Stageleft](./stageleft.mdx), which allows Hydroflow+ to emit regular Hydroflow programs that are compiled into efficient Rust binaries. It also integrates with [Hydro Deploy](../deploy/index.md) to make it easy to deploy and run Hydroflow+ programs on a cluster. +Hydroflow+ is a high-level distributed streaming framework for Rust powered by the [Hydroflow runtime](../hydroflow/index.mdx). Unlike traditional architectures such as actors or RPCs, Hydroflow+ offers _choreographic_ APIs, where expressions and functions can describe computation that takes place across many locations. It also integrates with [Hydro Deploy](../deploy/index.md) to make it easy to deploy and run Hydroflow+ programs to the cloud. + +Hydroflow+ uses a two-stage compilation approach. HF+ programs are standard Rust programs, which first run on the developer's laptop to generate a _deployment plan_. This plan is then compiled to individual binaries for each machine in the distributed system (enabling zero-overhead abstractions), and are then deployed to the cloud using the generated plan along with specifications of cloud resources. + +Hydroflow+ has been used to write a variety of high-performance distributed system, including implementations of classic distributed protocols such as two-phase commit and Paxos. Work is ongoing to develop a distributed systems standard library that will offer these protocols and more as reusable components. :::caution The docs for Hydroflow+ are still a work in progress. If you have any questions or run into bugs, please file an issue on the [Hydroflow GitHub repository](https://github.com/hydro-project/hydroflow). ::: - -The main logic of Hydroflow+ programs manipulates **streams**, which capture infinite ordered sequences of elements. Streams are transformed using classic functional operators such as `map`, `filter`, and `fold`, as well as relational operators such as `join`. To build **distributed** dataflow programs, Hydroflow+ also introduces the concept of **processes**, which capture _where_ a stream is being processed. - -## Setup -Hydroflow+ requires a particular workspace setup, as any crate that uses Hydroflow+ must have an supporting macro crate to drive the code generation. To get started, we recommend using the Hydroflow+ template. - -```bash -#shell-command-next-line -cargo install cargo-generate -#shell-command-next-line -cargo generate hydro-project/hydroflow template/hydroflow_plus -``` - -`cd` into the generated folder, ensure the correct nightly version of rust is installed, and test the generated project: -```bash -#shell-command-next-line -cd -#shell-command-next-line -rustup update -#shell-command-next-line -cargo test -``` diff --git a/docs/docs/hydroflow_plus/quickstart/clusters.mdx b/docs/docs/hydroflow_plus/quickstart/clusters.mdx index 4155c13e68a0..77c1dba62e65 100644 --- a/docs/docs/hydroflow_plus/quickstart/clusters.mdx +++ b/docs/docs/hydroflow_plus/quickstart/clusters.mdx @@ -23,7 +23,6 @@ pub mod broadcast; ```rust title="src/broadcast.rs" use hydroflow_plus::*; -use stageleft::*; pub struct Leader {} pub struct Workers {} @@ -69,10 +68,9 @@ async fn main() { let (leader, workers) = flow::broadcast::broadcast(&builder); flow.with_default_optimize() - .with_process(&leader, TrybuildHost::new(deployment.Localhost())) + .with_process(&leader, deployment.Localhost()) .with_cluster(&workers, (0..2) - .map(|idx| TrybuildHost::new(deployment.Localhost())) - .collect::>() + .map(|idx| deployment.Localhost()) ) .deploy(&mut deployment); diff --git a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx new file mode 100644 index 000000000000..a1699536f434 --- /dev/null +++ b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx @@ -0,0 +1,45 @@ +--- +sidebar_position: 1 +--- + +import CodeBlock from '@theme/CodeBlock'; +import firstTenSrc from '!!raw-loader!../../../../template/hydroflow_plus/src/first_ten.rs'; +import firstTenExampleSrc from '!!raw-loader!../../../../template/hydroflow_plus/examples/first_ten.rs'; +import { getLines, extractOutput } from '../../../src/util'; + +# Your First Dataflow +Let's look a minimal example of a Hydroflow+ program. We'll start with a simple dataflow that prints out the first 10 natural numbers. + +:::tip + +We recommend using the Hydroflow+ template to get started with a new project: + +```bash +#shell-command-next-line +cargo install cargo-generate +#shell-command-next-line +cargo generate gh:hydro-project/hydroflow template/hydroflow_plus +``` + +::: + +## Writing a Dataflow + +Hydroflow+ programs are _explicit_ about where computation takes place. So our dataflow program takes a single `&Process` parameter which is a handle to the single machine our program will run on. We can use this handle to materialize a stream using `source_iter` (which emits values from a provided collection), and then print out the values using `for_each`. + +{getLines(firstTenSrc, 1, 7)} + +You'll notice that the arguments to `source_iter` and `for_each` are wrapped in `q!` macros. This is because Hydroflow+ uses a two-stage compilation process, where the first stage generates a deployment plan that is then compiled to individual binaries for each machine in the distributed system. The `q!` macro is used to mark Rust code that will be executed in the second stage ("runtime" code). This generally includes snippets of Rust code that are used to define static sources of data or closures that transform them. + +## Running the Dataflow +Next, let's launch the dataflow program we just wrote. To do this, we'll need to write a bit more code in `examples/first_ten.rs` to configure our deployment. + +{getLines(firstTenExampleSrc, 1, 17)} + +First, we initialize a new [Hydro Deploy](../../deploy/index.md) deployment with `Deployment::new()`. Then, we create a `FlowBuilder` which will store the entire dataflow program and manage its compilation. + +To get the `&Process` we provide to `first_ten`, we can call `flow.process()`. After the dataflow has been created, we optimize it using `flow.with_default_optimize()`. Then, we map our virtual `Process` to a physical deployment target using `flow.with_process` (in this case we deploy to localhost). + +Finally, we call `flow.deploy(&mut deployment)` to provision the dataflow program on the target machine. This returns a struct with handles to the instantiated machines, which we must store in the `_nodes` variable to prevent them from being dropped. Then, we can start the dataflow program and block until `Ctrl-C` using `deployment.run_ctrl_c()`. + +In the next section, we will look at how to distribute this program across multiple processes. diff --git a/docs/docs/hydroflow_plus/quickstart/index.mdx b/docs/docs/hydroflow_plus/quickstart/index.mdx index d366e2f61046..29868a1c7503 100644 --- a/docs/docs/hydroflow_plus/quickstart/index.mdx +++ b/docs/docs/hydroflow_plus/quickstart/index.mdx @@ -1,7 +1,7 @@ # Quickstart In this tutorial, we'll walk through the basics of Hydroflow+ by building a simple dataflow that prints out the first 10 natural numbers. We'll start with a single process, then pipeline the computation, and finally distribute it across a cluster. -To get started with a new project, we'll use the Hydroflow+ template. The template comes with a pre-configured build system and an implementation of the following examples. +To get started with a new project, we'll use the Hydroflow+ template. The template comes with a simple distributed program. ```bash #shell-command-next-line @@ -13,10 +13,8 @@ cargo generate gh:hydro-project/hydroflow template/hydroflow_plus cd my-example-project ``` -After `cd`ing into the generated folder, ensure the correct nightly version of rust is installed and test the generated project: +After `cd`ing into the generated folder, we can run tests for the included sample: ```bash #shell-command-next-line -rustup update -#shell-command-next-line cargo test ``` \ No newline at end of file diff --git a/docs/docs/hydroflow_plus/quickstart/structure.mdx b/docs/docs/hydroflow_plus/quickstart/structure.mdx deleted file mode 100644 index 54c13f1588a0..000000000000 --- a/docs/docs/hydroflow_plus/quickstart/structure.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -sidebar_position: 1 ---- -import CodeBlock from '@theme/CodeBlock'; -import firstTenSrc from '!!raw-loader!../../../../template/hydroflow_plus/src/first_ten_distributed.rs'; -import { getLines, extractOutput } from '../../../src/util'; - -# Your First Dataflow -Hydroflow+ programs require special structure to support code generation and distributed deployments. There are two main components of a Hydroflow+ program: -- The **flow graph** describes the dataflow logic of the program. -- The **deployment** describes how to map the flow graph to a physical deployment. - -:::tip - -We recommend using the Hydroflow+ template to get started with a new project. The template comes with a pre-configured build system and the following example pre-implemented. - -```bash -#shell-command-next-line -cargo install cargo-generate -#shell-command-next-line -cargo generate gh:hydro-project/hydroflow template/hydroflow_plus -``` - -`cd` into the generated folder, ensure the correct nightly version of rust is installed, and test the generated project: -```bash -#shell-command-next-line -cd -#shell-command-next-line -rustup update -#shell-command-next-line -cargo test -``` - -::: - - -Let's look a minimal example of a Hydroflow+ program. We'll start with a simple flow graph that prints out the first 10 natural numbers. First, we'll define the **flow graph**. - - -## The Flow Graph - -{getLines(firstTenSrc, 1, 17)} - -To build a Hydroflow+ application, we need to define a dataflow that spans multiple processes. The `FlowBuilder` parameter captures the global dataflow, and we can instantiate processes to define boundaries between distributed logic. When defining a process, we also pass in a type parameter to a "tag" that identifies the process. When transforming streams, the Rust type system will guarantee that we are operating on streams on the same process. - -{getLines(firstTenSrc, 8, 9)} - -Now, we can build out the dataflow to run on this process. Every dataflow starts at a source that is bound to a specific process. First, we instantiate a stream that emits the first 10 natural numbers. - -{getLines(firstTenSrc, 11)} - -In Hydroflow+, whenever there are snippets of Rust code passed to operators (like `source_iter`, `map`, or `for_each`), we use the `q!` macro to mark them. For example, we may use Rust snippets to define static sources of data or closures that transform them. - -To print out these numbers, we can use the `for_each` operator (note that the body of `for_each` is a closure wrapped in `q!`): - -{getLines(firstTenSrc, 12, 14)} - -In the next section, we will look at how to deploy this program to run on multiple processs. diff --git a/hydroflow_plus/src/builder/built.rs b/hydroflow_plus/src/builder/built.rs index ee196b3f3016..9beb37b6da4a 100644 --- a/hydroflow_plus/src/builder/built.rs +++ b/hydroflow_plus/src/builder/built.rs @@ -5,7 +5,7 @@ use hydroflow_lang::graph::{eliminate_extra_unions_tees, HydroflowGraph}; use super::compiled::HfCompiled; use super::deploy::{DeployFlow, DeployResult}; -use crate::deploy::{ClusterSpec, Deploy, ExternalSpec, LocalDeploy, ProcessSpec}; +use crate::deploy::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec, LocalDeploy}; use crate::ir::HfPlusLeaf; use crate::location::{Cluster, ExternalProcess, Process}; @@ -109,7 +109,7 @@ impl<'a> BuiltFlow<'a> { pub fn with_process>( self, process: &Process

, - spec: impl ProcessSpec<'a, D>, + spec: impl IntoProcessSpec<'a, D>, ) -> DeployFlow<'a, D> { self.into_deploy().with_process(process, spec) } diff --git a/hydroflow_plus/src/builder/deploy.rs b/hydroflow_plus/src/builder/deploy.rs index e441b76eaed6..8cdaf5b9e1b9 100644 --- a/hydroflow_plus/src/builder/deploy.rs +++ b/hydroflow_plus/src/builder/deploy.rs @@ -12,7 +12,7 @@ use stageleft::Quoted; use super::built::build_inner; use super::compiled::HfCompiled; -use crate::deploy::{ExternalSpec, LocalDeploy, Node, RegisterPort}; +use crate::deploy::{ExternalSpec, IntoProcessSpec, LocalDeploy, Node, RegisterPort}; use crate::ir::HfPlusLeaf; use crate::location::external_process::{ ExternalBincodeSink, ExternalBincodeStream, ExternalBytesPort, @@ -39,10 +39,16 @@ impl<'a, D: LocalDeploy<'a>> Drop for DeployFlow<'a, D> { } impl<'a, D: LocalDeploy<'a>> DeployFlow<'a, D> { - pub fn with_process

(mut self, process: &Process

, spec: impl ProcessSpec<'a, D>) -> Self { + pub fn with_process

( + mut self, + process: &Process

, + spec: impl IntoProcessSpec<'a, D>, + ) -> Self { let tag_name = std::any::type_name::

().to_string(); - self.nodes - .insert(process.id, spec.build(process.id, &tag_name)); + self.nodes.insert( + process.id, + spec.into_process_spec().build(process.id, &tag_name), + ); self } diff --git a/hydroflow_plus/src/deploy/deploy_graph.rs b/hydroflow_plus/src/deploy/deploy_graph.rs index 218fbda3f5b5..ba5a91ee4a76 100644 --- a/hydroflow_plus/src/deploy/deploy_graph.rs +++ b/hydroflow_plus/src/deploy/deploy_graph.rs @@ -26,7 +26,7 @@ use trybuild_internals_api::path; use super::deploy_runtime::*; use super::trybuild::{compile_graph_trybuild, create_trybuild}; -use super::{ClusterSpec, Deploy, ExternalSpec, Node, ProcessSpec, RegisterPort}; +use super::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec, Node, ProcessSpec, RegisterPort}; use crate::futures::SinkExt; use crate::lang::graph::HydroflowGraph; @@ -409,6 +409,32 @@ pub struct TrybuildHost { pub cluster_idx: Option, } +impl From> for TrybuildHost { + fn from(host: Arc) -> Self { + Self { + host, + display_name: None, + rustflags: None, + tracing: None, + name_hint: None, + cluster_idx: None, + } + } +} + +impl From> for TrybuildHost { + fn from(host: Arc) -> Self { + Self { + host, + display_name: None, + rustflags: None, + tracing: None, + name_hint: None, + cluster_idx: None, + } + } +} + impl TrybuildHost { pub fn new(host: Arc) -> Self { Self { @@ -455,10 +481,25 @@ impl TrybuildHost { } } -impl From> for TrybuildHost { - fn from(h: Arc) -> Self { - Self { - host: h, +impl IntoProcessSpec<'_, HydroDeploy> for Arc { + type ProcessSpec = TrybuildHost; + fn into_process_spec(self) -> TrybuildHost { + TrybuildHost { + host: self, + display_name: None, + rustflags: None, + tracing: None, + name_hint: None, + cluster_idx: None, + } + } +} + +impl IntoProcessSpec<'_, HydroDeploy> for Arc { + type ProcessSpec = TrybuildHost; + fn into_process_spec(self) -> TrybuildHost { + TrybuildHost { + host: self, display_name: None, rustflags: None, tracing: None, @@ -586,6 +627,18 @@ impl ExternalSpec<'_, HydroDeploy> for Arc { } } +impl ExternalSpec<'_, HydroDeploy> for Arc { + fn build(self, _id: usize, _name_hint: &str) -> DeployExternal { + DeployExternal { + next_port: Rc::new(RefCell::new(0)), + host: self, + underlying: Rc::new(RefCell::new(None)), + allocated_ports: Rc::new(RefCell::new(HashMap::new())), + client_ports: Rc::new(RefCell::new(HashMap::new())), + } + } +} + pub enum CrateOrTrybuild { Crate(HydroflowCrate), Trybuild(TrybuildHost), @@ -799,7 +852,7 @@ impl ClusterSpec<'_, HydroDeploy> for DeployClusterSpec { } } -impl ClusterSpec<'_, HydroDeploy> for Vec { +impl, I: IntoIterator> ClusterSpec<'_, HydroDeploy> for I { fn build(self, id: usize, name_hint: &str) -> DeployCluster { let name_hint = format!("{} (cluster {id})", name_hint); DeployCluster { @@ -808,7 +861,8 @@ impl ClusterSpec<'_, HydroDeploy> for Vec { cluster_spec: Rc::new(RefCell::new(Some( self.into_iter() .enumerate() - .map(|(idx, mut b)| { + .map(|(idx, b)| { + let mut b = b.into(); b.name_hint = Some(name_hint.clone()); b.cluster_idx = Some(idx); CrateOrTrybuild::Trybuild(b) diff --git a/hydroflow_plus/src/deploy/mod.rs b/hydroflow_plus/src/deploy/mod.rs index 995f2f0f69cc..d447bd23722a 100644 --- a/hydroflow_plus/src/deploy/mod.rs +++ b/hydroflow_plus/src/deploy/mod.rs @@ -208,6 +208,18 @@ pub trait ProcessSpec<'a, D: LocalDeploy<'a> + ?Sized> { fn build(self, id: usize, name_hint: &str) -> D::Process; } +pub trait IntoProcessSpec<'a, D: LocalDeploy<'a> + ?Sized> { + type ProcessSpec: ProcessSpec<'a, D>; + fn into_process_spec(self) -> Self::ProcessSpec; +} + +impl<'a, D: LocalDeploy<'a> + ?Sized, T: ProcessSpec<'a, D>> IntoProcessSpec<'a, D> for T { + type ProcessSpec = T; + fn into_process_spec(self) -> Self::ProcessSpec { + self + } +} + pub trait ClusterSpec<'a, D: LocalDeploy<'a> + ?Sized> { fn build(self, id: usize, name_hint: &str) -> D::Cluster; } diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 2e7f322ec1d8..b71cc879b8e1 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -4,6 +4,7 @@ stageleft::stageleft_no_entry_crate!(); pub use hydroflow::scheduled::graph::Hydroflow; pub use hydroflow::*; +pub use stageleft::*; pub mod runtime_support { pub use bincode; diff --git a/hydroflow_plus/src/location/process.rs b/hydroflow_plus/src/location/process.rs index 301d7ce6c2a8..fe28157ff768 100644 --- a/hydroflow_plus/src/location/process.rs +++ b/hydroflow_plus/src/location/process.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use super::{Location, LocationId}; use crate::builder::FlowState; -pub struct Process<'a, P> { +pub struct Process<'a, P = ()> { pub(crate) id: usize, pub(crate) flow_state: FlowState, pub(crate) _phantom: PhantomData<&'a &'a mut P>, diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index f85eb32aa2dc..81c8f1ec0573 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -832,14 +832,11 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { #[cfg(test)] mod tests { - use std::sync::Arc; - - use hydro_deploy::{Deployment, Host}; + use hydro_deploy::Deployment; use hydroflow::futures::StreamExt; use serde::{Deserialize, Serialize}; use stageleft::q; - use crate::deploy::TrybuildHost; use crate::location::Location; use crate::FlowBuilder; @@ -868,9 +865,9 @@ mod tests { let nodes = flow .with_default_optimize() - .with_process(&first_node, TrybuildHost::new(deployment.Localhost())) - .with_process(&second_node, TrybuildHost::new(deployment.Localhost())) - .with_external(&external, deployment.Localhost() as Arc) + .with_process(&first_node, deployment.Localhost()) + .with_process(&second_node, deployment.Localhost()) + .with_external(&external, deployment.Localhost()) .deploy(&mut deployment); deployment.deploy().await.unwrap(); diff --git a/hydroflow_plus_test/examples/compute_pi.rs b/hydroflow_plus_test/examples/compute_pi.rs index 37c02da439f3..e7844e1c2c4e 100644 --- a/hydroflow_plus_test/examples/compute_pi.rs +++ b/hydroflow_plus_test/examples/compute_pi.rs @@ -49,9 +49,7 @@ async fn main() { ) .with_cluster( &cluster, - (0..8) - .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)) - .collect::>(), + (0..8).map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)), ) .deploy(&mut deployment); diff --git a/hydroflow_plus_test/examples/first_ten_distributed.rs b/hydroflow_plus_test/examples/first_ten_distributed.rs index 6716c6fa52b4..ddbcd5c83e6d 100644 --- a/hydroflow_plus_test/examples/first_ten_distributed.rs +++ b/hydroflow_plus_test/examples/first_ten_distributed.rs @@ -52,7 +52,7 @@ async fn main() { &p2, TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags), ) - .with_external(&external_process, deployment.Localhost() as Arc) + .with_external(&external_process, deployment.Localhost()) .deploy(&mut deployment); deployment.deploy().await.unwrap(); diff --git a/hydroflow_plus_test/examples/map_reduce.rs b/hydroflow_plus_test/examples/map_reduce.rs index c18d9c59bb52..3ccc8ea48969 100644 --- a/hydroflow_plus_test/examples/map_reduce.rs +++ b/hydroflow_plus_test/examples/map_reduce.rs @@ -48,9 +48,7 @@ async fn main() { ) .with_cluster( &cluster, - (0..2) - .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)) - .collect::>(), + (0..2).map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)), ) .deploy(&mut deployment); diff --git a/hydroflow_plus_test/examples/paxos.rs b/hydroflow_plus_test/examples/paxos.rs index 31c34cb39beb..06efd212cdf3 100644 --- a/hydroflow_plus_test/examples/paxos.rs +++ b/hydroflow_plus_test/examples/paxos.rs @@ -60,26 +60,22 @@ async fn main() { .with_cluster( &proposers, (0..f + 1) - .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)) - .collect::>(), + .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)), ) .with_cluster( &acceptors, (0..2 * f + 1) - .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)) - .collect::>(), + .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)), ) .with_cluster( &clients, (0..num_clients) - .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)) - .collect::>(), + .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)), ) .with_cluster( &replicas, (0..f + 1) - .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)) - .collect::>(), + .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)), ) .deploy(&mut deployment); diff --git a/hydroflow_plus_test/examples/perf_compute_pi.rs b/hydroflow_plus_test/examples/perf_compute_pi.rs index 56d8db5a9746..d92c77c4dd4d 100644 --- a/hydroflow_plus_test/examples/perf_compute_pi.rs +++ b/hydroflow_plus_test/examples/perf_compute_pi.rs @@ -68,21 +68,19 @@ async fn main() { ) .with_cluster( &cluster, - (0..8) - .map(|idx| { - TrybuildHost::new(create_host(&mut deployment)) - .rustflags(rustflags) - .tracing( - TracingOptions::builder() - .perf_raw_outfile(format!("cluster{}.perf.data", idx)) - .dtrace_outfile(format!("cluster{}.leader.stacks", idx)) - .fold_outfile(format!("cluster{}.data.folded", idx)) - .flamegraph_outfile(format!("cluster{}.svg", idx)) - .frequency(128) - .build(), - ) - }) - .collect::>(), + (0..8).map(|idx| { + TrybuildHost::new(create_host(&mut deployment)) + .rustflags(rustflags) + .tracing( + TracingOptions::builder() + .perf_raw_outfile(format!("cluster{}.perf.data", idx)) + .dtrace_outfile(format!("cluster{}.leader.stacks", idx)) + .fold_outfile(format!("cluster{}.data.folded", idx)) + .flamegraph_outfile(format!("cluster{}.svg", idx)) + .frequency(128) + .build(), + ) + }), ) .deploy(&mut deployment); deployment.run_ctrl_c().await.unwrap(); diff --git a/hydroflow_plus_test/examples/simple_cluster.rs b/hydroflow_plus_test/examples/simple_cluster.rs index e1c22b702f45..8fb08e5b6a7d 100644 --- a/hydroflow_plus_test/examples/simple_cluster.rs +++ b/hydroflow_plus_test/examples/simple_cluster.rs @@ -49,9 +49,7 @@ async fn main() { ) .with_cluster( &cluster, - (0..2) - .map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)) - .collect::>(), + (0..2).map(|_| TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags)), ) .deploy(&mut deployment); deployment.run_ctrl_c().await.unwrap(); diff --git a/hydroflow_plus_test/examples/two_pc.rs b/hydroflow_plus_test/examples/two_pc.rs index df946a70613c..23f7a6b64239 100644 --- a/hydroflow_plus_test/examples/two_pc.rs +++ b/hydroflow_plus_test/examples/two_pc.rs @@ -19,9 +19,7 @@ async fn main() { .with_process(&coordinator, TrybuildHost::new(deployment.Localhost())) .with_cluster( &participants, - (0..num_participants) - .map(|_| TrybuildHost::new(deployment.Localhost())) - .collect::>(), + (0..num_participants).map(|_| TrybuildHost::new(deployment.Localhost())), ) .with_process(&client, TrybuildHost::new(deployment.Localhost())) .deploy(&mut deployment); diff --git a/hydroflow_plus_test/src/cluster/compute_pi.rs b/hydroflow_plus_test/src/cluster/compute_pi.rs index 4f4010a3d5fc..fbbf2a4d2671 100644 --- a/hydroflow_plus_test/src/cluster/compute_pi.rs +++ b/hydroflow_plus_test/src/cluster/compute_pi.rs @@ -1,7 +1,6 @@ use std::time::Duration; use hydroflow_plus::*; -use stageleft::*; pub struct Worker {} pub struct Leader {} diff --git a/hydroflow_plus_test/src/cluster/many_to_many.rs b/hydroflow_plus_test/src/cluster/many_to_many.rs index 90adbf03f09b..91cda6f10ca1 100644 --- a/hydroflow_plus_test/src/cluster/many_to_many.rs +++ b/hydroflow_plus_test/src/cluster/many_to_many.rs @@ -1,5 +1,4 @@ use hydroflow_plus::*; -use stageleft::*; pub fn many_to_many<'a>(flow: &FlowBuilder<'a>) -> Cluster<'a, ()> { let cluster = flow.cluster(); @@ -14,7 +13,7 @@ pub fn many_to_many<'a>(flow: &FlowBuilder<'a>) -> Cluster<'a, ()> { #[cfg(test)] mod tests { use hydro_deploy::Deployment; - use hydroflow_plus::deploy::{DeployCrateWrapper, TrybuildHost}; + use hydroflow_plus::deploy::DeployCrateWrapper; #[tokio::test] async fn many_to_many() { @@ -27,12 +26,7 @@ mod tests { insta::assert_debug_snapshot!(built.ir()); let nodes = built - .with_cluster( - &cluster, - (0..2) - .map(|_| TrybuildHost::new(deployment.Localhost())) - .collect::>(), - ) + .with_cluster(&cluster, (0..2).map(|_| deployment.Localhost())) .deploy(&mut deployment); deployment.deploy().await.unwrap(); diff --git a/hydroflow_plus_test/src/cluster/map_reduce.rs b/hydroflow_plus_test/src/cluster/map_reduce.rs index 192403e423b1..cf9e558c3daa 100644 --- a/hydroflow_plus_test/src/cluster/map_reduce.rs +++ b/hydroflow_plus_test/src/cluster/map_reduce.rs @@ -1,5 +1,4 @@ use hydroflow_plus::*; -use stageleft::*; pub struct Leader {} pub struct Worker {} diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 85b9e4c06940..26ac4a535675 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -5,7 +5,6 @@ use std::time::Duration; use hydroflow_plus::*; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use stageleft::*; use tokio::time::Instant; pub struct Proposer {} diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index e3db1458a809..a651eebe02c8 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use std::time::{Duration, SystemTime}; use hydroflow_plus::*; -use stageleft::*; use super::paxos::{Acceptor, Proposer}; use super::paxos_kv::{paxos_kv, KvPayload, Replica}; diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 9aeef0d9b7c4..42048ff104ac 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -5,7 +5,6 @@ use std::hash::Hash; use hydroflow_plus::*; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use stageleft::*; use super::paxos::{paxos_core, Acceptor, Proposer}; diff --git a/hydroflow_plus_test/src/cluster/simple_cluster.rs b/hydroflow_plus_test/src/cluster/simple_cluster.rs index f3c7062b348d..074230631b69 100644 --- a/hydroflow_plus_test/src/cluster/simple_cluster.rs +++ b/hydroflow_plus_test/src/cluster/simple_cluster.rs @@ -1,5 +1,4 @@ use hydroflow_plus::*; -use stageleft::*; pub fn decouple_cluster<'a>(flow: &FlowBuilder<'a>) -> (Cluster<'a, ()>, Cluster<'a, ()>) { let cluster1 = flow.cluster(); @@ -55,7 +54,7 @@ pub fn simple_cluster<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, ()>, Cluster<' #[cfg(test)] mod tests { use hydro_deploy::Deployment; - use hydroflow_plus::deploy::{DeployCrateWrapper, TrybuildHost}; + use hydroflow_plus::deploy::DeployCrateWrapper; #[tokio::test] async fn simple_cluster() { @@ -68,13 +67,8 @@ mod tests { insta::assert_debug_snapshot!(built.ir()); let nodes = built - .with_process(&node, TrybuildHost::new(deployment.Localhost())) - .with_cluster( - &cluster, - (0..2) - .map(|_| TrybuildHost::new(deployment.Localhost())) - .collect::>(), - ) + .with_process(&node, deployment.Localhost()) + .with_cluster(&cluster, (0..2).map(|_| deployment.Localhost())) .deploy(&mut deployment); deployment.deploy().await.unwrap(); @@ -128,8 +122,8 @@ mod tests { let built = builder.with_default_optimize(); let nodes = built - .with_process(&process1, TrybuildHost::new(deployment.Localhost())) - .with_process(&process2, TrybuildHost::new(deployment.Localhost())) + .with_process(&process1, deployment.Localhost()) + .with_process(&process2, deployment.Localhost()) .deploy(&mut deployment); deployment.deploy().await.unwrap(); @@ -150,18 +144,8 @@ mod tests { let built = builder.with_default_optimize(); let nodes = built - .with_cluster( - &cluster1, - (0..3) - .map(|_| TrybuildHost::new(deployment.Localhost())) - .collect::>(), - ) - .with_cluster( - &cluster2, - (0..3) - .map(|_| TrybuildHost::new(deployment.Localhost())) - .collect::>(), - ) + .with_cluster(&cluster1, (0..3).map(|_| deployment.Localhost())) + .with_cluster(&cluster2, (0..3).map(|_| deployment.Localhost())) .deploy(&mut deployment); deployment.deploy().await.unwrap(); diff --git a/hydroflow_plus_test/src/cluster/two_pc.rs b/hydroflow_plus_test/src/cluster/two_pc.rs index 5e11100ffc95..79e400ae38a2 100644 --- a/hydroflow_plus_test/src/cluster/two_pc.rs +++ b/hydroflow_plus_test/src/cluster/two_pc.rs @@ -1,5 +1,4 @@ use hydroflow_plus::*; -use stageleft::*; // if the variable start with p, that means current work is at the participant side. if start with c, at coordinator side. // diff --git a/hydroflow_plus_test/src/distributed/first_ten.rs b/hydroflow_plus_test/src/distributed/first_ten.rs index cead08979018..aacce28f290f 100644 --- a/hydroflow_plus_test/src/distributed/first_ten.rs +++ b/hydroflow_plus_test/src/distributed/first_ten.rs @@ -2,7 +2,6 @@ use hydroflow_plus::*; use location::external_process::ExternalBincodeSink; use location::ExternalProcess; use serde::{Deserialize, Serialize}; -use stageleft::*; #[derive(Serialize, Deserialize)] struct SendOverNetwork { @@ -44,11 +43,9 @@ pub fn first_ten_distributed<'a>( #[cfg(test)] mod tests { - use std::sync::Arc; - use futures::SinkExt; - use hydro_deploy::{Deployment, Host}; - use hydroflow_plus::deploy::{DeployCrateWrapper, TrybuildHost}; + use hydro_deploy::Deployment; + use hydroflow_plus::deploy::DeployCrateWrapper; #[tokio::test] async fn first_ten_distributed() { @@ -63,9 +60,9 @@ mod tests { insta::assert_debug_snapshot!(built.ir()); let nodes = built - .with_process(&first_node, TrybuildHost::new(deployment.Localhost())) - .with_process(&second_node, TrybuildHost::new(deployment.Localhost())) - .with_external(&external_process, deployment.Localhost() as Arc) + .with_process(&first_node, deployment.Localhost()) + .with_process(&second_node, deployment.Localhost()) + .with_external(&external_process, deployment.Localhost()) .deploy(&mut deployment); deployment.deploy().await.unwrap(); diff --git a/hydroflow_plus_test_local/src/local/compute_pi.rs b/hydroflow_plus_test_local/src/local/compute_pi.rs index 97b850714e45..c0cc525e647e 100644 --- a/hydroflow_plus_test_local/src/local/compute_pi.rs +++ b/hydroflow_plus_test_local/src/local/compute_pi.rs @@ -2,7 +2,6 @@ use std::time::Duration; use hydroflow_plus::deploy::SingleProcessGraph; use hydroflow_plus::*; -use stageleft::*; pub fn compute_pi<'a>(flow: &FlowBuilder<'a>, batch_size: RuntimeData) -> Process<'a, ()> { let process = flow.process(); diff --git a/hydroflow_plus_test_local/src/local/first_ten.rs b/hydroflow_plus_test_local/src/local/first_ten.rs index 8da36aa823c6..20c674a691e4 100644 --- a/hydroflow_plus_test_local/src/local/first_ten.rs +++ b/hydroflow_plus_test_local/src/local/first_ten.rs @@ -1,6 +1,5 @@ use hydroflow_plus::deploy::SingleProcessGraph; use hydroflow_plus::*; -use stageleft::*; pub fn first_ten(flow: &FlowBuilder) { let process = flow.process::<()>(); diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index 97f48660e852..928a9157c2d1 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -2,7 +2,6 @@ use hydroflow_plus::deploy::SingleProcessGraph; use hydroflow_plus::tokio::sync::mpsc::UnboundedSender; use hydroflow_plus::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::*; -use stageleft::*; #[stageleft::entry] pub fn graph_reachability<'a>( diff --git a/template/hydroflow_plus/README.md b/template/hydroflow_plus/README.md index d999d91e6a8f..a4f1058c359d 100644 --- a/template/hydroflow_plus/README.md +++ b/template/hydroflow_plus/README.md @@ -7,16 +7,15 @@ cargo generate gh:hydro-project/hydroflow template/hydroflow_plus cd ``` -After `cd`ing into the workspace, ensure the correct nightly version of rust is installed: -```bash -rustup update -``` +After `cd`ing into the workspace, run the sample tests Then test the project: ```bash cargo test ``` +To learn more about the template, see the [Hydroflow+ Quickstart](https://hydro.run/docs/hydroflow_plus/quickstart/first-dataflow). + ## Project Structure The template includes a sample program `first_ten_distributed`. diff --git a/template/hydroflow_plus/examples/first_ten.rs b/template/hydroflow_plus/examples/first_ten.rs new file mode 100644 index 000000000000..ada07db93fbf --- /dev/null +++ b/template/hydroflow_plus/examples/first_ten.rs @@ -0,0 +1,17 @@ +use hydro_deploy::Deployment; + +#[tokio::main] +async fn main() { + let mut deployment = Deployment::new(); + + let flow = hydroflow_plus::FlowBuilder::new(); + let process = flow.process(); + hydroflow_plus_template::first_ten::first_ten(&process); + + let _nodes = flow + .with_default_optimize() + .with_process(&process, deployment.Localhost()) + .deploy(&mut deployment); + + deployment.run_ctrl_c().await.unwrap(); +} diff --git a/template/hydroflow_plus/examples/first_ten_distributed.rs b/template/hydroflow_plus/examples/first_ten_distributed.rs index 6cae922a757d..e75bdbb1ecd6 100644 --- a/template/hydroflow_plus/examples/first_ten_distributed.rs +++ b/template/hydroflow_plus/examples/first_ten_distributed.rs @@ -1,5 +1,4 @@ use hydro_deploy::Deployment; -use hydroflow_plus::deploy::TrybuildHost; #[tokio::main] async fn main() { @@ -10,8 +9,8 @@ async fn main() { let _nodes = flow .with_default_optimize() - .with_process(&p1, TrybuildHost::new(deployment.Localhost())) - .with_process(&p2, TrybuildHost::new(deployment.Localhost())) + .with_process(&p1, deployment.Localhost()) + .with_process(&p2, deployment.Localhost()) .deploy(&mut deployment); deployment.run_ctrl_c().await.unwrap(); diff --git a/template/hydroflow_plus/src/first_ten.rs b/template/hydroflow_plus/src/first_ten.rs new file mode 100644 index 000000000000..86fe2feb7528 --- /dev/null +++ b/template/hydroflow_plus/src/first_ten.rs @@ -0,0 +1,7 @@ +use hydroflow_plus::*; + +pub fn first_ten(process: &Process) { + process + .source_iter(q!(0..10)) + .for_each(q!(|n| println!("{}", n))); +} diff --git a/template/hydroflow_plus/src/first_ten_distributed.rs b/template/hydroflow_plus/src/first_ten_distributed.rs index 5fb1c4b48fff..c4d8d1cbcccc 100644 --- a/template/hydroflow_plus/src/first_ten_distributed.rs +++ b/template/hydroflow_plus/src/first_ten_distributed.rs @@ -1,5 +1,4 @@ use hydroflow_plus::*; -use stageleft::*; pub struct P1 {} pub struct P2 {} @@ -19,7 +18,7 @@ pub fn first_ten_distributed<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, P1>, Pr #[cfg(test)] mod tests { use hydro_deploy::Deployment; - use hydroflow_plus::deploy::{DeployCrateWrapper, TrybuildHost}; + use hydroflow_plus::deploy::DeployCrateWrapper; use hydroflow_plus::futures::StreamExt; use tokio_stream::wrappers::UnboundedReceiverStream; @@ -33,8 +32,8 @@ mod tests { let nodes = flow .with_default_optimize() - .with_process(&p1, TrybuildHost::new(localhost.clone())) - .with_process(&p2, TrybuildHost::new(localhost.clone())) + .with_process(&p1, localhost.clone()) + .with_process(&p2, localhost.clone()) .deploy(&mut deployment); deployment.deploy().await.unwrap(); diff --git a/template/hydroflow_plus/src/lib.rs b/template/hydroflow_plus/src/lib.rs index 17e53184ca7e..37c22f9c1c6b 100644 --- a/template/hydroflow_plus/src/lib.rs +++ b/template/hydroflow_plus/src/lib.rs @@ -1,3 +1,4 @@ stageleft::stageleft_no_entry_crate!(); +pub mod first_ten; pub mod first_ten_distributed; From 0bd3a2d2230cbef24210f71a3ea83d82d1cc7244 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 8 Nov 2024 09:32:50 -0800 Subject: [PATCH 44/74] refactor(hydroflow_plus)!: eliminate remaining `Hf` name prefixes (#1554) --- hydroflow_plus/src/builder/built.rs | 8 ++++---- hydroflow_plus/src/builder/compiled.rs | 20 ++++++++++---------- hydroflow_plus/src/builder/deploy.rs | 6 +++--- hydroflow_plus/src/cycle.rs | 12 ++++++------ hydroflow_plus/src/location/mod.rs | 8 ++++---- hydroflow_plus/src/location/tick.rs | 22 +++++++++++----------- hydroflow_plus/src/optional.rs | 20 +++++++++++++------- hydroflow_plus/src/singleton.rs | 15 ++++++++++----- hydroflow_plus/src/stream.rs | 10 +++++----- 9 files changed, 66 insertions(+), 55 deletions(-) diff --git a/hydroflow_plus/src/builder/built.rs b/hydroflow_plus/src/builder/built.rs index 9beb37b6da4a..997c5388d373 100644 --- a/hydroflow_plus/src/builder/built.rs +++ b/hydroflow_plus/src/builder/built.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use hydroflow_lang::graph::{eliminate_extra_unions_tees, HydroflowGraph}; -use super::compiled::HfCompiled; +use super::compiled::CompiledFlow; use super::deploy::{DeployFlow, DeployResult}; use crate::deploy::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec, LocalDeploy}; use crate::ir::HfPlusLeaf; @@ -62,10 +62,10 @@ pub(crate) fn build_inner(ir: &mut Vec) -> BTreeMap BuiltFlow<'a> { - pub fn compile_no_network>(mut self) -> HfCompiled<'a, D::GraphId> { + pub fn compile_no_network>(mut self) -> CompiledFlow<'a, D::GraphId> { self.used = true; - HfCompiled { + CompiledFlow { hydroflow_ir: build_inner(&mut self.ir), extra_stmts: BTreeMap::new(), _phantom: PhantomData, @@ -130,7 +130,7 @@ impl<'a> BuiltFlow<'a> { self.into_deploy().with_cluster(cluster, spec) } - pub fn compile + 'a>(self, env: &D::CompileEnv) -> HfCompiled<'a, D::GraphId> { + pub fn compile + 'a>(self, env: &D::CompileEnv) -> CompiledFlow<'a, D::GraphId> { self.into_deploy::().compile(env) } diff --git a/hydroflow_plus/src/builder/compiled.rs b/hydroflow_plus/src/builder/compiled.rs index 0d16884e0166..ce92789763a3 100644 --- a/hydroflow_plus/src/builder/compiled.rs +++ b/hydroflow_plus/src/builder/compiled.rs @@ -8,13 +8,13 @@ use quote::quote; use stageleft::runtime_support::FreeVariable; use stageleft::Quoted; -pub struct HfCompiled<'a, ID> { +pub struct CompiledFlow<'a, ID> { pub(super) hydroflow_ir: BTreeMap, pub(super) extra_stmts: BTreeMap>, pub(super) _phantom: PhantomData<&'a mut &'a ID>, } -impl HfCompiled<'_, ID> { +impl CompiledFlow<'_, ID> { pub fn hydroflow_ir(&self) -> &BTreeMap { &self.hydroflow_ir } @@ -24,8 +24,8 @@ impl HfCompiled<'_, ID> { } } -impl<'a> HfCompiled<'a, usize> { - pub fn with_dynamic_id(self, id: impl Quoted<'a, usize>) -> HfBuiltWithId<'a> { +impl<'a> CompiledFlow<'a, usize> { + pub fn with_dynamic_id(self, id: impl Quoted<'a, usize>) -> CompiledFlowWithId<'a> { let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") .expect("hydroflow_plus should be present in `Cargo.toml`"); let root = match hydroflow_crate { @@ -68,7 +68,7 @@ impl<'a> HfCompiled<'a, usize> { let conditioned_tokens: TokenStream = conditioned_tokens.unwrap(); let id = id.splice_untyped(); - HfBuiltWithId { + CompiledFlowWithId { tokens: syn::parse_quote!({ let __given_id = #id; #conditioned_tokens else { @@ -80,9 +80,9 @@ impl<'a> HfCompiled<'a, usize> { } } -impl<'a> Quoted<'a, Hydroflow<'a>> for HfCompiled<'a, ()> {} +impl<'a> Quoted<'a, Hydroflow<'a>> for CompiledFlow<'a, ()> {} -impl<'a> FreeVariable> for HfCompiled<'a, ()> { +impl<'a> FreeVariable> for CompiledFlow<'a, ()> { fn to_tokens(mut self) -> (Option, Option) { let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") .expect("hydroflow_plus should be present in `Cargo.toml`"); @@ -109,14 +109,14 @@ impl<'a> FreeVariable> for HfCompiled<'a, ()> { } } -pub struct HfBuiltWithId<'a> { +pub struct CompiledFlowWithId<'a> { tokens: TokenStream, _phantom: PhantomData<&'a mut &'a ()>, } -impl<'a> Quoted<'a, Hydroflow<'a>> for HfBuiltWithId<'a> {} +impl<'a> Quoted<'a, Hydroflow<'a>> for CompiledFlowWithId<'a> {} -impl<'a> FreeVariable> for HfBuiltWithId<'a> { +impl<'a> FreeVariable> for CompiledFlowWithId<'a> { fn to_tokens(self) -> (Option, Option) { (None, Some(self.tokens)) } diff --git a/hydroflow_plus/src/builder/deploy.rs b/hydroflow_plus/src/builder/deploy.rs index 8cdaf5b9e1b9..07353dd0fd77 100644 --- a/hydroflow_plus/src/builder/deploy.rs +++ b/hydroflow_plus/src/builder/deploy.rs @@ -11,7 +11,7 @@ use serde::Serialize; use stageleft::Quoted; use super::built::build_inner; -use super::compiled::HfCompiled; +use super::compiled::CompiledFlow; use crate::deploy::{ExternalSpec, IntoProcessSpec, LocalDeploy, Node, RegisterPort}; use crate::ir::HfPlusLeaf; use crate::location::external_process::{ @@ -72,7 +72,7 @@ impl<'a, D: LocalDeploy<'a>> DeployFlow<'a, D> { } impl<'a, D: Deploy<'a>> DeployFlow<'a, D> { - pub fn compile(mut self, env: &D::CompileEnv) -> HfCompiled<'a, D::GraphId> { + pub fn compile(mut self, env: &D::CompileEnv) -> CompiledFlow<'a, D::GraphId> { self.used = true; let mut seen_tees: HashMap<_, _> = HashMap::new(); @@ -91,7 +91,7 @@ impl<'a, D: Deploy<'a>> DeployFlow<'a, D> { let extra_stmts = self.extra_stmts(env); - HfCompiled { + CompiledFlow { hydroflow_ir: build_inner(&mut flow_state_networked), extra_stmts, _phantom: PhantomData, diff --git a/hydroflow_plus/src/cycle.rs b/hydroflow_plus/src/cycle.rs index 5f6bb98dd55a..3a7709d56a77 100644 --- a/hydroflow_plus/src/cycle.rs +++ b/hydroflow_plus/src/cycle.rs @@ -2,8 +2,8 @@ use std::marker::PhantomData; use crate::location::Location; -pub enum ForwardRef {} -pub enum TickCycle {} +pub enum ForwardRefMarker {} +pub enum TickCycleMarker {} pub trait DeferTick { fn defer_tick(self) -> Self; @@ -29,24 +29,24 @@ pub trait CycleCollectionWithInitial<'a, T>: CycleComplete<'a, T> { /// by a stream that is not yet known. /// /// See [`crate::FlowBuilder`] for an explainer on the type parameters. -pub struct HfForwardRef<'a, S: CycleComplete<'a, ForwardRef>> { +pub struct ForwardRef<'a, S: CycleComplete<'a, ForwardRefMarker>> { pub(crate) ident: syn::Ident, pub(crate) _phantom: PhantomData<(&'a mut &'a (), S)>, } -impl<'a, S: CycleComplete<'a, ForwardRef>> HfForwardRef<'a, S> { +impl<'a, S: CycleComplete<'a, ForwardRefMarker>> ForwardRef<'a, S> { pub fn complete(self, stream: S) { let ident = self.ident; S::complete(stream, ident) } } -pub struct HfCycle<'a, S: CycleComplete<'a, TickCycle> + DeferTick> { +pub struct TickCycle<'a, S: CycleComplete<'a, TickCycleMarker> + DeferTick> { pub(crate) ident: syn::Ident, pub(crate) _phantom: PhantomData<(&'a mut &'a (), S)>, } -impl<'a, S: CycleComplete<'a, TickCycle> + DeferTick> HfCycle<'a, S> { +impl<'a, S: CycleComplete<'a, TickCycleMarker> + DeferTick> TickCycle<'a, S> { pub fn complete_next_tick(self, stream: S) { let ident = self.ident; S::complete(stream.defer_tick(), ident) diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index 8908f5827647..061dfd1016cb 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -8,7 +8,7 @@ use proc_macro2::Span; use stageleft::{q, Quoted}; use super::builder::FlowState; -use crate::cycle::{CycleCollection, ForwardRef, HfForwardRef}; +use crate::cycle::{CycleCollection, ForwardRef, ForwardRefMarker}; use crate::ir::{HfPlusNode, HfPlusSource}; use crate::{Singleton, Stream, Unbounded}; @@ -178,9 +178,9 @@ pub trait Location<'a>: Clone { ))) } - fn forward_ref>( + fn forward_ref>( &self, - ) -> (HfForwardRef<'a, S>, S) + ) -> (ForwardRef<'a, S>, S) where Self: NoTick, { @@ -203,7 +203,7 @@ pub trait Location<'a>: Clone { let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); ( - HfForwardRef { + ForwardRef { ident: ident.clone(), _phantom: PhantomData, }, diff --git a/hydroflow_plus/src/location/tick.rs b/hydroflow_plus/src/location/tick.rs index d974eb14acca..3e9e8074baf3 100644 --- a/hydroflow_plus/src/location/tick.rs +++ b/hydroflow_plus/src/location/tick.rs @@ -6,8 +6,8 @@ use stageleft::{q, Quoted}; use super::{Cluster, Location, LocationId, Process}; use crate::builder::FlowState; use crate::cycle::{ - CycleCollection, CycleCollectionWithInitial, DeferTick, ForwardRef, HfCycle, HfForwardRef, - TickCycle, + CycleCollection, CycleCollectionWithInitial, DeferTick, ForwardRef, ForwardRefMarker, + TickCycle, TickCycleMarker, }; use crate::ir::{HfPlusNode, HfPlusSource}; use crate::{Bounded, Optional, Singleton, Stream}; @@ -82,9 +82,9 @@ impl<'a, L: Location<'a>> Tick { ) } - pub fn forward_ref>( + pub fn forward_ref>( &self, - ) -> (HfForwardRef<'a, S>, S) + ) -> (ForwardRef<'a, S>, S) where L: NoTick, { @@ -107,7 +107,7 @@ impl<'a, L: Location<'a>> Tick { let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); ( - HfForwardRef { + ForwardRef { ident: ident.clone(), _phantom: PhantomData, }, @@ -115,9 +115,9 @@ impl<'a, L: Location<'a>> Tick { ) } - pub fn cycle + DeferTick>( + pub fn cycle + DeferTick>( &self, - ) -> (HfCycle<'a, S>, S) + ) -> (TickCycle<'a, S>, S) where L: NoTick, { @@ -140,7 +140,7 @@ impl<'a, L: Location<'a>> Tick { let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); ( - HfCycle { + TickCycle { ident: ident.clone(), _phantom: PhantomData, }, @@ -149,11 +149,11 @@ impl<'a, L: Location<'a>> Tick { } pub fn cycle_with_initial< - S: CycleCollectionWithInitial<'a, TickCycle, Location = Self> + DeferTick, + S: CycleCollectionWithInitial<'a, TickCycleMarker, Location = Self> + DeferTick, >( &self, initial: S, - ) -> (HfCycle<'a, S>, S) + ) -> (TickCycle<'a, S>, S) where L: NoTick, { @@ -176,7 +176,7 @@ impl<'a, L: Location<'a>> Tick { let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); ( - HfCycle { + TickCycle { ident: ident.clone(), _phantom: PhantomData, }, diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index 1494f8a667f7..e5530352bd02 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -7,7 +7,7 @@ use stageleft::{q, IntoQuotedMut, Quoted}; use syn::parse_quote; use crate::builder::FLOW_USED_MESSAGE; -use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; +use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRefMarker, TickCycleMarker}; use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; use crate::location::{check_matching_location, LocationId, NoTick}; use crate::{Bounded, Location, Singleton, Stream, Tick, Unbounded}; @@ -43,7 +43,9 @@ impl<'a, T, L: Location<'a>> DeferTick for Optional, Bounded> { } } -impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycle> for Optional, Bounded> { +impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycleMarker> + for Optional, Bounded> +{ type Location = Tick; fn create_source(ident: syn::Ident, location: Tick) -> Self { @@ -58,7 +60,7 @@ impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycle> for Optional> CycleComplete<'a, TickCycle> for Optional, Bounded> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycleMarker> for Optional, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -74,7 +76,9 @@ impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycle> for Optional> CycleCollection<'a, ForwardRef> for Optional, Bounded> { +impl<'a, T, L: Location<'a>> CycleCollection<'a, ForwardRefMarker> + for Optional, Bounded> +{ type Location = Tick; fn create_source(ident: syn::Ident, location: Tick) -> Self { @@ -89,7 +93,7 @@ impl<'a, T, L: Location<'a>> CycleCollection<'a, ForwardRef> for Optional> CycleComplete<'a, ForwardRef> for Optional, Bounded> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, ForwardRefMarker> for Optional, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -105,7 +109,9 @@ impl<'a, T, L: Location<'a>> CycleComplete<'a, ForwardRef> for Optional + NoTick, B> CycleCollection<'a, ForwardRef> for Optional { +impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRefMarker> + for Optional +{ type Location = L; fn create_source(ident: syn::Ident, location: L) -> Self { @@ -120,7 +126,7 @@ impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRef> for Opt } } -impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRef> for Optional { +impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRefMarker> for Optional { fn complete(self, ident: syn::Ident) { self.location .flow_state() diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 394aae8c0fa2..b068d1d9bd1f 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -7,7 +7,8 @@ use stageleft::{q, IntoQuotedMut, Quoted}; use crate::builder::FLOW_USED_MESSAGE; use crate::cycle::{ - CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick, ForwardRef, TickCycle, + CycleCollection, CycleCollectionWithInitial, CycleComplete, DeferTick, ForwardRefMarker, + TickCycleMarker, }; use crate::ir::{HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::{check_matching_location, Location, LocationId, NoTick, Tick}; @@ -47,7 +48,7 @@ impl<'a, T, L: Location<'a>> DeferTick for Singleton, Bounded> { } } -impl<'a, T, L: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> +impl<'a, T, L: Location<'a>> CycleCollectionWithInitial<'a, TickCycleMarker> for Singleton, Bounded> { type Location = Tick; @@ -67,7 +68,7 @@ impl<'a, T, L: Location<'a>> CycleCollectionWithInitial<'a, TickCycle> } } -impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycle> for Singleton, Bounded> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycleMarker> for Singleton, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -83,7 +84,9 @@ impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycle> for Singleton> CycleCollection<'a, ForwardRef> for Singleton, Bounded> { +impl<'a, T, L: Location<'a>> CycleCollection<'a, ForwardRefMarker> + for Singleton, Bounded> +{ type Location = Tick; fn create_source(ident: syn::Ident, location: Tick) -> Self { @@ -98,7 +101,9 @@ impl<'a, T, L: Location<'a>> CycleCollection<'a, ForwardRef> for Singleton> CycleComplete<'a, ForwardRef> for Singleton, Bounded> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, ForwardRefMarker> + for Singleton, Bounded> +{ fn complete(self, ident: syn::Ident) { self.location .flow_state() diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 81c8f1ec0573..aa1193e2e859 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -13,7 +13,7 @@ use stageleft::{q, IntoQuotedMut, Quoted}; use syn::parse_quote; use crate::builder::FLOW_USED_MESSAGE; -use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRef, TickCycle}; +use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRefMarker, TickCycleMarker}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::cluster::ClusterSelfId; use crate::location::external_process::{ExternalBincodeStream, ExternalBytesPort}; @@ -57,7 +57,7 @@ impl<'a, T, L: Location<'a>> DeferTick for Stream, Bounded> { } } -impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycle> for Stream, Bounded> { +impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycleMarker> for Stream, Bounded> { type Location = Tick; fn create_source(ident: syn::Ident, location: Tick) -> Self { @@ -72,7 +72,7 @@ impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycle> for Stream> CycleComplete<'a, TickCycle> for Stream, Bounded> { +impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycleMarker> for Stream, Bounded> { fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -88,7 +88,7 @@ impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycle> for Stream, } } -impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRef> for Stream { +impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRefMarker> for Stream { type Location = L; fn create_source(ident: syn::Ident, location: L) -> Self { @@ -103,7 +103,7 @@ impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRef> for Str } } -impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRef> for Stream { +impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRefMarker> for Stream { fn complete(self, ident: syn::Ident) { self.location .flow_state() From 1d35639182b256ee51f2ff5201a86d8a05644003 Mon Sep 17 00:00:00 2001 From: Rohit Kulshreshtha Date: Fri, 8 Nov 2024 09:49:12 -0800 Subject: [PATCH 45/74] feat(Gossip KV): Make member random suffix length configurable. (#1556) --- datastores/gossip_kv/Cargo.toml | 2 +- datastores/gossip_kv/server/main.rs | 5 ++- datastores/gossip_kv/server/membership.rs | 49 +++++++++++------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/datastores/gossip_kv/Cargo.toml b/datastores/gossip_kv/Cargo.toml index b59765578698..37943072a7d1 100644 --- a/datastores/gossip_kv/Cargo.toml +++ b/datastores/gossip_kv/Cargo.toml @@ -7,7 +7,7 @@ license = "Apache-2.0" publish = false [dependencies] -clap = { version = "4.5.4", features = ["derive"] } +clap = { version = "4.5.4", features = ["derive", "env"] } config = "0.14.0" governor = "0.7.0" hostname = "0.4.0" diff --git a/datastores/gossip_kv/server/main.rs b/datastores/gossip_kv/server/main.rs index 2d2990a7e0d3..bca640e9449b 100644 --- a/datastores/gossip_kv/server/main.rs +++ b/datastores/gossip_kv/server/main.rs @@ -39,6 +39,9 @@ struct Opts { /// The duration (in seconds) between gossip rounds. #[clap(short, long, default_value = "5", value_parser = clap_duration_from_secs)] gossip_frequency: Duration, + + #[clap(env = "GOSSIP_MEMBER_SUFFIX_LEN", default_value = "4")] + member_suffix_len: usize, } /// Parse duration from float string for clap args. @@ -133,7 +136,7 @@ async fn main() { let gossip_protocol_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), opts.gossip_port); - let member_data = MemberDataBuilder::new(member_name().clone()) + let member_data = MemberDataBuilder::new(member_name(opts.member_suffix_len).clone()) .add_protocol(Protocol::new("gossip".into(), gossip_protocol_address)) .add_protocol(Protocol::new("client".into(), client_protocol_address)) .build(); diff --git a/datastores/gossip_kv/server/membership.rs b/datastores/gossip_kv/server/membership.rs index 28a3fcadf137..bdaef84b6c11 100644 --- a/datastores/gossip_kv/server/membership.rs +++ b/datastores/gossip_kv/server/membership.rs @@ -1,37 +1,34 @@ use std::sync::OnceLock; use gossip_kv::membership::MemberId; -// use rand::distributions::Distribution; -// use rand::{Rng}; +use rand::distributions::Distribution; +use rand::{thread_rng, Rng}; -// /// This is a simple distribution that generates a random lower-case alphanumeric -// struct LowercaseAlphanumeric; -// -// impl Distribution for LowercaseAlphanumeric { -// fn sample(&self, rng: &mut R) -> char { -// let choices = b"abcdefghijklmnopqrstuvwxyz0123456789"; -// choices[rng.gen_range(0..choices.len())] as char -// } -// } +/// This is a simple distribution that generates a random lower-case alphanumeric +struct LowercaseAlphanumeric; + +impl Distribution for LowercaseAlphanumeric { + fn sample(&self, rng: &mut R) -> char { + let choices = b"abcdefghijklmnopqrstuvwxyz0123456789"; + choices[rng.gen_range(0..choices.len())] as char + } +} /// Gets a name for the current process. -pub fn member_name() -> &'static MemberId { +pub fn member_name(random_suffix_len: usize) -> &'static MemberId { static MEMBER_NAME: OnceLock = OnceLock::new(); MEMBER_NAME.get_or_init(|| { - // Generate a lower-case alphanumeric suffix of length 4 - - // TODO: Random suffixes are good, but make benchmarking a pain. For now, we'll just use - // the hostname as-is. - - // let suffix: String = thread_rng() - // .sample_iter(&LowercaseAlphanumeric) - // .take(4) - // .map(char::from) - // .collect(); - - // Retrieve hostname - hostname::get().unwrap().to_str().unwrap().to_string() + let hostname = hostname::get().unwrap().to_str().unwrap().to_string(); - // format!("{}-{}", hostname, suffix) + if random_suffix_len > 0 { + let suffix: String = thread_rng() + .sample_iter(&LowercaseAlphanumeric) + .take(4) + .map(char::from) + .collect(); + format!("{}-{}", hostname, suffix) + } else { + hostname + } }) } From 8d8b4b2288746e0aa2a95329d91297820aee7586 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 8 Nov 2024 10:37:57 -0800 Subject: [PATCH 46/74] feat(hydroflow_plus)!: implicitly apply default optimizations (#1557) This also changes the behavior of `with_default_optimize` to be terminal, if users want to apply optimizations after these they should explicitly invoke the optimizations. --- .../hydroflow_plus/quickstart/clusters.mdx | 3 +- .../quickstart/first-dataflow.mdx | 12 +++-- hydroflow_plus/src/builder/built.rs | 39 +++++++--------- hydroflow_plus/src/builder/deploy.rs | 14 ++++++ hydroflow_plus/src/builder/mod.rs | 46 ++++++++++++++++++- hydroflow_plus/src/rewrites/profiler.rs | 2 +- hydroflow_plus/src/rewrites/properties.rs | 5 +- hydroflow_plus/src/stream.rs | 1 - hydroflow_plus_test/examples/compute_pi.rs | 1 - .../examples/first_ten_distributed.rs | 1 - hydroflow_plus_test/examples/map_reduce.rs | 1 - hydroflow_plus_test/examples/paxos.rs | 1 - .../examples/perf_compute_pi.rs | 1 - .../examples/simple_cluster.rs | 1 - hydroflow_plus_test/examples/two_pc.rs | 1 - hydroflow_plus_test/src/cluster/compute_pi.rs | 7 +-- hydroflow_plus_test/src/cluster/map_reduce.rs | 7 +-- .../src/cluster/paxos_bench.rs | 4 +- .../src/local/chat_app.rs | 3 +- .../src/local/compute_pi.rs | 3 +- .../src/local/count_elems.rs | 3 +- .../src/local/first_ten.rs | 3 +- .../src/local/graph_reachability.rs | 3 +- .../src/local/negation.rs | 6 +-- .../src/local/teed_join.rs | 3 +- template/hydroflow_plus/examples/first_ten.rs | 1 - .../examples/first_ten_distributed.rs | 1 - .../examples/first_ten_distributed_gcp.rs | 1 - .../src/first_ten_distributed.rs | 1 - 29 files changed, 99 insertions(+), 76 deletions(-) diff --git a/docs/docs/hydroflow_plus/quickstart/clusters.mdx b/docs/docs/hydroflow_plus/quickstart/clusters.mdx index 77c1dba62e65..cfdecec5e346 100644 --- a/docs/docs/hydroflow_plus/quickstart/clusters.mdx +++ b/docs/docs/hydroflow_plus/quickstart/clusters.mdx @@ -67,8 +67,7 @@ async fn main() { let builder = hydroflow_plus::FlowBuilder::new(); let (leader, workers) = flow::broadcast::broadcast(&builder); - flow.with_default_optimize() - .with_process(&leader, deployment.Localhost()) + flow.with_process(&leader, deployment.Localhost()) .with_cluster(&workers, (0..2) .map(|idx| deployment.Localhost()) ) diff --git a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx index a1699536f434..16d6b00bcc70 100644 --- a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx +++ b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx @@ -25,20 +25,22 @@ cargo generate gh:hydro-project/hydroflow template/hydroflow_plus ## Writing a Dataflow -Hydroflow+ programs are _explicit_ about where computation takes place. So our dataflow program takes a single `&Process` parameter which is a handle to the single machine our program will run on. We can use this handle to materialize a stream using `source_iter` (which emits values from a provided collection), and then print out the values using `for_each`. +In Hydroflow+, streams are attached to a **`Location`**, which is a virtual handle to a **single machine** (the `Process` type) or **set of machines** (the `Cluster` type). To write distributed programs, a single piece of code can use multiple locations. -{getLines(firstTenSrc, 1, 7)} +Our first dataflow will run on a single machine, so we take a `&Process` parameter. We can materialize a stream on this machine using `process.source_iter` (which emits values from a provided collection), and then print out the values using `for_each`. + +{firstTenSrc} You'll notice that the arguments to `source_iter` and `for_each` are wrapped in `q!` macros. This is because Hydroflow+ uses a two-stage compilation process, where the first stage generates a deployment plan that is then compiled to individual binaries for each machine in the distributed system. The `q!` macro is used to mark Rust code that will be executed in the second stage ("runtime" code). This generally includes snippets of Rust code that are used to define static sources of data or closures that transform them. ## Running the Dataflow -Next, let's launch the dataflow program we just wrote. To do this, we'll need to write a bit more code in `examples/first_ten.rs` to configure our deployment. +Next, let's launch the dataflow program we just wrote. To do this, we'll need to write a bit more code in `examples/first_ten.rs` to configure our deployment (generally, we will place deployment scripts in `examples` because Hydro Deploy is a dev dependency). -{getLines(firstTenExampleSrc, 1, 17)} +{firstTenExampleSrc} First, we initialize a new [Hydro Deploy](../../deploy/index.md) deployment with `Deployment::new()`. Then, we create a `FlowBuilder` which will store the entire dataflow program and manage its compilation. -To get the `&Process` we provide to `first_ten`, we can call `flow.process()`. After the dataflow has been created, we optimize it using `flow.with_default_optimize()`. Then, we map our virtual `Process` to a physical deployment target using `flow.with_process` (in this case we deploy to localhost). +To create a `Process`, we call `flow.process()`. After the dataflow has been created, we must map each instantiated `Process` to a deployment target using `flow.with_process` (in this case we deploy to localhost). Finally, we call `flow.deploy(&mut deployment)` to provision the dataflow program on the target machine. This returns a struct with handles to the instantiated machines, which we must store in the `_nodes` variable to prevent them from being dropped. Then, we can start the dataflow program and block until `Ctrl-C` using `deployment.run_ctrl_c()`. diff --git a/hydroflow_plus/src/builder/built.rs b/hydroflow_plus/src/builder/built.rs index 997c5388d373..9d07cc37bd98 100644 --- a/hydroflow_plus/src/builder/built.rs +++ b/hydroflow_plus/src/builder/built.rs @@ -26,23 +26,6 @@ impl Drop for BuiltFlow<'_> { } } -impl BuiltFlow<'_> { - pub fn ir(&self) -> &Vec { - &self.ir - } - - pub fn optimize_with(mut self, f: impl FnOnce(Vec) -> Vec) -> Self { - self.used = true; - BuiltFlow { - ir: f(std::mem::take(&mut self.ir)), - processes: std::mem::take(&mut self.processes), - clusters: std::mem::take(&mut self.clusters), - used: false, - _phantom: PhantomData, - } - } -} - pub(crate) fn build_inner(ir: &mut Vec) -> BTreeMap { let mut builders = BTreeMap::new(); let mut built_tees = HashMap::new(); @@ -62,18 +45,24 @@ pub(crate) fn build_inner(ir: &mut Vec) -> BTreeMap BuiltFlow<'a> { - pub fn compile_no_network>(mut self) -> CompiledFlow<'a, D::GraphId> { - self.used = true; + pub fn ir(&self) -> &Vec { + &self.ir + } - CompiledFlow { - hydroflow_ir: build_inner(&mut self.ir), - extra_stmts: BTreeMap::new(), + pub fn optimize_with(mut self, f: impl FnOnce(Vec) -> Vec) -> Self { + self.used = true; + BuiltFlow { + ir: f(std::mem::take(&mut self.ir)), + processes: std::mem::take(&mut self.processes), + clusters: std::mem::take(&mut self.clusters), + used: false, _phantom: PhantomData, } } - pub fn with_default_optimize(self) -> BuiltFlow<'a> { + pub fn with_default_optimize>(self) -> DeployFlow<'a, D> { self.optimize_with(crate::rewrites::persist_pullup::persist_pullup) + .into_deploy() } fn into_deploy>(mut self) -> DeployFlow<'a, D> { @@ -134,6 +123,10 @@ impl<'a> BuiltFlow<'a> { self.into_deploy::().compile(env) } + pub fn compile_no_network + 'a>(self) -> CompiledFlow<'a, D::GraphId> { + self.into_deploy::().compile_no_network() + } + pub fn deploy + 'a>( self, env: &mut D::InstantiateEnv, diff --git a/hydroflow_plus/src/builder/deploy.rs b/hydroflow_plus/src/builder/deploy.rs index 07353dd0fd77..ef24877d1563 100644 --- a/hydroflow_plus/src/builder/deploy.rs +++ b/hydroflow_plus/src/builder/deploy.rs @@ -39,6 +39,10 @@ impl<'a, D: LocalDeploy<'a>> Drop for DeployFlow<'a, D> { } impl<'a, D: LocalDeploy<'a>> DeployFlow<'a, D> { + pub fn ir(&self) -> &Vec { + &self.ir + } + pub fn with_process

( mut self, process: &Process

, @@ -69,6 +73,16 @@ impl<'a, D: LocalDeploy<'a>> DeployFlow<'a, D> { .insert(cluster.id, spec.build(cluster.id, &tag_name)); self } + + pub fn compile_no_network(mut self) -> CompiledFlow<'a, D::GraphId> { + self.used = true; + + CompiledFlow { + hydroflow_ir: build_inner(&mut self.ir), + extra_stmts: BTreeMap::new(), + _phantom: PhantomData, + } + } } impl<'a, D: Deploy<'a>> DeployFlow<'a, D> { diff --git a/hydroflow_plus/src/builder/mod.rs b/hydroflow_plus/src/builder/mod.rs index a15cea44dd66..1db3581a2081 100644 --- a/hydroflow_plus/src/builder/mod.rs +++ b/hydroflow_plus/src/builder/mod.rs @@ -3,11 +3,14 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::rc::Rc; +use compiled::CompiledFlow; +use deploy::{DeployFlow, DeployResult}; use stageleft::*; +use crate::deploy::{ExternalSpec, IntoProcessSpec, LocalDeploy}; use crate::ir::HfPlusLeaf; use crate::location::{Cluster, ExternalProcess, Process}; -use crate::RuntimeContext; +use crate::{ClusterSpec, Deploy, RuntimeContext}; pub mod built; pub mod compiled; @@ -98,7 +101,7 @@ impl<'a> FlowBuilder<'a> { } } - pub fn with_default_optimize(self) -> built::BuiltFlow<'a> { + pub fn with_default_optimize>(self) -> DeployFlow<'a, D> { self.finalize().with_default_optimize() } @@ -158,4 +161,43 @@ impl<'a> FlowBuilder<'a> { pub fn runtime_context(&self) -> RuntimeContext<'a> { RuntimeContext::new() } + + pub fn with_process>( + self, + process: &Process

, + spec: impl IntoProcessSpec<'a, D>, + ) -> DeployFlow<'a, D> { + self.with_default_optimize().with_process(process, spec) + } + + pub fn with_external>( + self, + process: &ExternalProcess

, + spec: impl ExternalSpec<'a, D>, + ) -> DeployFlow<'a, D> { + self.with_default_optimize().with_external(process, spec) + } + + pub fn with_cluster>( + self, + cluster: &Cluster, + spec: impl ClusterSpec<'a, D>, + ) -> DeployFlow<'a, D> { + self.with_default_optimize().with_cluster(cluster, spec) + } + + pub fn compile + 'a>(self, env: &D::CompileEnv) -> CompiledFlow<'a, D::GraphId> { + self.with_default_optimize::().compile(env) + } + + pub fn compile_no_network + 'a>(self) -> CompiledFlow<'a, D::GraphId> { + self.with_default_optimize::().compile_no_network() + } + + pub fn deploy + 'a>( + self, + env: &mut D::InstantiateEnv, + ) -> DeployResult<'a, D> { + self.with_default_optimize().deploy(env) + } } diff --git a/hydroflow_plus/src/rewrites/profiler.rs b/hydroflow_plus/src/rewrites/profiler.rs index a2627567653c..be9b030bd83b 100644 --- a/hydroflow_plus/src/rewrites/profiler.rs +++ b/hydroflow_plus/src/rewrites/profiler.rs @@ -105,7 +105,7 @@ mod tests { let counter_queue = RuntimeData::new("Fake"); let pushed_down = built - .with_default_optimize() + .optimize_with(crate::rewrites::persist_pullup::persist_pullup) .optimize_with(|ir| super::profiling(ir, runtime_context, counters, counter_queue)); insta::assert_debug_snapshot!(&pushed_down.ir()); diff --git a/hydroflow_plus/src/rewrites/properties.rs b/hydroflow_plus/src/rewrites/properties.rs index ecb6bcbe42a4..5d5ce0d3edf5 100644 --- a/hydroflow_plus/src/rewrites/properties.rs +++ b/hydroflow_plus/src/rewrites/properties.rs @@ -118,12 +118,11 @@ mod tests { .for_each(q!(|(string, count)| println!("{}: {}", string, count))); let built = flow - .finalize() .optimize_with(|ir| properties_optimize(ir, &database)) - .with_default_optimize(); + .with_default_optimize::(); insta::assert_debug_snapshot!(built.ir()); - let _ = built.compile_no_network::(); + let _ = built.compile_no_network(); } } diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index aa1193e2e859..c6037d2b9924 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -864,7 +864,6 @@ mod tests { .send_bincode_external(&external); let nodes = flow - .with_default_optimize() .with_process(&first_node, deployment.Localhost()) .with_process(&second_node, deployment.Localhost()) .with_external(&external, deployment.Localhost()) diff --git a/hydroflow_plus_test/examples/compute_pi.rs b/hydroflow_plus_test/examples/compute_pi.rs index e7844e1c2c4e..69ec6df97425 100644 --- a/hydroflow_plus_test/examples/compute_pi.rs +++ b/hydroflow_plus_test/examples/compute_pi.rs @@ -42,7 +42,6 @@ async fn main() { let (cluster, leader) = hydroflow_plus_test::cluster::compute_pi::compute_pi(&builder, 8192); let _nodes = builder - .with_default_optimize() .with_process( &leader, TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags), diff --git a/hydroflow_plus_test/examples/first_ten_distributed.rs b/hydroflow_plus_test/examples/first_ten_distributed.rs index ddbcd5c83e6d..109464ad4f92 100644 --- a/hydroflow_plus_test/examples/first_ten_distributed.rs +++ b/hydroflow_plus_test/examples/first_ten_distributed.rs @@ -43,7 +43,6 @@ async fn main() { let (external_process, external_port, p1, p2) = hydroflow_plus_test::distributed::first_ten::first_ten_distributed(&builder); let nodes = builder - .with_default_optimize() .with_process( &p1, TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags), diff --git a/hydroflow_plus_test/examples/map_reduce.rs b/hydroflow_plus_test/examples/map_reduce.rs index 3ccc8ea48969..960fc032277b 100644 --- a/hydroflow_plus_test/examples/map_reduce.rs +++ b/hydroflow_plus_test/examples/map_reduce.rs @@ -41,7 +41,6 @@ async fn main() { let builder = hydroflow_plus::FlowBuilder::new(); let (leader, cluster) = hydroflow_plus_test::cluster::map_reduce::map_reduce(&builder); let _nodes = builder - .with_default_optimize() .with_process( &leader, TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags), diff --git a/hydroflow_plus_test/examples/paxos.rs b/hydroflow_plus_test/examples/paxos.rs index 06efd212cdf3..90bf86299b7a 100644 --- a/hydroflow_plus_test/examples/paxos.rs +++ b/hydroflow_plus_test/examples/paxos.rs @@ -56,7 +56,6 @@ async fn main() { let rustflags = "-C opt-level=3 -C codegen-units=1 -C strip=none -C debuginfo=2 -C lto=off"; let _nodes = builder - .with_default_optimize() .with_cluster( &proposers, (0..f + 1) diff --git a/hydroflow_plus_test/examples/perf_compute_pi.rs b/hydroflow_plus_test/examples/perf_compute_pi.rs index d92c77c4dd4d..8382e0b18fc2 100644 --- a/hydroflow_plus_test/examples/perf_compute_pi.rs +++ b/hydroflow_plus_test/examples/perf_compute_pi.rs @@ -51,7 +51,6 @@ async fn main() { // .ir()); let _nodes = builder - .with_default_optimize() .with_process( &leader, TrybuildHost::new(create_host(&mut deployment)) diff --git a/hydroflow_plus_test/examples/simple_cluster.rs b/hydroflow_plus_test/examples/simple_cluster.rs index 8fb08e5b6a7d..05fe9ea71508 100644 --- a/hydroflow_plus_test/examples/simple_cluster.rs +++ b/hydroflow_plus_test/examples/simple_cluster.rs @@ -42,7 +42,6 @@ async fn main() { let (process, cluster) = hydroflow_plus_test::cluster::simple_cluster::simple_cluster(&builder); let _nodes = builder - .with_default_optimize() .with_process( &process, TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags), diff --git a/hydroflow_plus_test/examples/two_pc.rs b/hydroflow_plus_test/examples/two_pc.rs index 23f7a6b64239..aa3065571cca 100644 --- a/hydroflow_plus_test/examples/two_pc.rs +++ b/hydroflow_plus_test/examples/two_pc.rs @@ -15,7 +15,6 @@ async fn main() { let _rustflags = "-C opt-level=3 -C codegen-units=1 -C strip=none -C debuginfo=2 -C lto=off"; let _nodes = builder - .with_default_optimize() .with_process(&coordinator, TrybuildHost::new(deployment.Localhost())) .with_cluster( &participants, diff --git a/hydroflow_plus_test/src/cluster/compute_pi.rs b/hydroflow_plus_test/src/cluster/compute_pi.rs index fbbf2a4d2671..6bfdbda2764c 100644 --- a/hydroflow_plus_test/src/cluster/compute_pi.rs +++ b/hydroflow_plus_test/src/cluster/compute_pi.rs @@ -56,14 +56,11 @@ mod tests { fn compute_pi_ir() { let builder = hydroflow_plus::FlowBuilder::new(); let _ = super::compute_pi(&builder, 8192); - let built = builder.with_default_optimize(); + let built = builder.with_default_optimize::(); insta::assert_debug_snapshot!(built.ir()); - for (id, ir) in built - .compile::(&RuntimeData::new("FAKE")) - .hydroflow_ir() - { + for (id, ir) in built.compile(&RuntimeData::new("FAKE")).hydroflow_ir() { insta::with_settings!({snapshot_suffix => format!("surface_graph_{id}")}, { insta::assert_snapshot!(ir.surface_syntax_string()); }); diff --git a/hydroflow_plus_test/src/cluster/map_reduce.rs b/hydroflow_plus_test/src/cluster/map_reduce.rs index cf9e558c3daa..27ebdc7eb598 100644 --- a/hydroflow_plus_test/src/cluster/map_reduce.rs +++ b/hydroflow_plus_test/src/cluster/map_reduce.rs @@ -50,14 +50,11 @@ mod tests { fn map_reduce_ir() { let builder = hydroflow_plus::FlowBuilder::new(); let _ = super::map_reduce(&builder); - let built = builder.with_default_optimize(); + let built = builder.with_default_optimize::(); insta::assert_debug_snapshot!(built.ir()); - for (id, ir) in built - .compile::(&RuntimeData::new("FAKE")) - .hydroflow_ir() - { + for (id, ir) in built.compile(&RuntimeData::new("FAKE")).hydroflow_ir() { insta::with_settings!({snapshot_suffix => format!("surface_graph_{id}")}, { insta::assert_snapshot!(ir.surface_syntax_string()); }); diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index a651eebe02c8..c602512bf2f4 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -267,12 +267,12 @@ mod tests { fn paxos_ir() { let builder = hydroflow_plus::FlowBuilder::new(); let _ = super::paxos_bench(&builder, 1, 1, 1, 1, 1, 1, 1); - let built = builder.with_default_optimize(); + let built = builder.with_default_optimize::(); hydroflow_plus::ir::dbg_dedup_tee(|| { insta::assert_debug_snapshot!(built.ir()); }); - let _ = built.compile::(&RuntimeData::new("FAKE")); + let _ = built.compile(&RuntimeData::new("FAKE")); } } diff --git a/hydroflow_plus_test_local/src/local/chat_app.rs b/hydroflow_plus_test_local/src/local/chat_app.rs index dc87f8a680c1..2c1d82f9ce3f 100644 --- a/hydroflow_plus_test_local/src/local/chat_app.rs +++ b/hydroflow_plus_test_local/src/local/chat_app.rs @@ -38,8 +38,7 @@ pub fn chat_app<'a>( output.send(t).unwrap(); })); - flow.with_default_optimize() - .compile_no_network::() + flow.compile_no_network::() } #[stageleft::runtime] diff --git a/hydroflow_plus_test_local/src/local/compute_pi.rs b/hydroflow_plus_test_local/src/local/compute_pi.rs index c0cc525e647e..131c529111bf 100644 --- a/hydroflow_plus_test_local/src/local/compute_pi.rs +++ b/hydroflow_plus_test_local/src/local/compute_pi.rs @@ -46,6 +46,5 @@ pub fn compute_pi_runtime<'a>( batch_size: RuntimeData, ) -> impl Quoted<'a, Hydroflow<'a>> { let _ = compute_pi(&flow, batch_size); - flow.with_default_optimize() - .compile_no_network::() + flow.compile_no_network::() } diff --git a/hydroflow_plus_test_local/src/local/count_elems.rs b/hydroflow_plus_test_local/src/local/count_elems.rs index de967ca18bc0..275c956d7e52 100644 --- a/hydroflow_plus_test_local/src/local/count_elems.rs +++ b/hydroflow_plus_test_local/src/local/count_elems.rs @@ -23,8 +23,7 @@ pub fn count_elems_generic<'a, T: 'a>( output.send(v).unwrap(); })); - flow.with_default_optimize() - .compile_no_network::() + flow.compile_no_network::() } #[stageleft::entry] diff --git a/hydroflow_plus_test_local/src/local/first_ten.rs b/hydroflow_plus_test_local/src/local/first_ten.rs index 20c674a691e4..867df566ce38 100644 --- a/hydroflow_plus_test_local/src/local/first_ten.rs +++ b/hydroflow_plus_test_local/src/local/first_ten.rs @@ -10,8 +10,7 @@ pub fn first_ten(flow: &FlowBuilder) { #[stageleft::entry] pub fn first_ten_runtime<'a>(flow: FlowBuilder<'a>) -> impl Quoted<'a, Hydroflow<'a>> { first_ten(&flow); - flow.with_default_optimize() - .compile_no_network::() + flow.compile_no_network::() } #[stageleft::runtime] diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index 928a9157c2d1..b03aefb230e2 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -29,8 +29,7 @@ pub fn graph_reachability<'a>( reached_out.send(v).unwrap(); })); - flow.with_default_optimize() - .compile_no_network::() + flow.compile_no_network::() } #[stageleft::runtime] diff --git a/hydroflow_plus_test_local/src/local/negation.rs b/hydroflow_plus_test_local/src/local/negation.rs index 667bec86c572..b771dd760353 100644 --- a/hydroflow_plus_test_local/src/local/negation.rs +++ b/hydroflow_plus_test_local/src/local/negation.rs @@ -27,8 +27,7 @@ pub fn test_difference<'a>( output.send(v).unwrap(); })); - flow.with_default_optimize() - .compile_no_network::() + flow.compile_no_network::() } #[stageleft::entry] @@ -58,8 +57,7 @@ pub fn test_anti_join<'a>( output.send(v.0).unwrap(); })); - flow.with_default_optimize() - .compile_no_network::() + flow.compile_no_network::() } #[stageleft::runtime] diff --git a/hydroflow_plus_test_local/src/local/teed_join.rs b/hydroflow_plus_test_local/src/local/teed_join.rs index 3d1283528af9..3771e481a7e7 100644 --- a/hydroflow_plus_test_local/src/local/teed_join.rs +++ b/hydroflow_plus_test_local/src/local/teed_join.rs @@ -41,8 +41,7 @@ pub fn teed_join<'a, S: Stream + Unpin + 'a>( output.send(v).unwrap(); })); - flow.with_default_optimize() - .compile_no_network::() + flow.compile_no_network::() .with_dynamic_id(subgraph_id) } diff --git a/template/hydroflow_plus/examples/first_ten.rs b/template/hydroflow_plus/examples/first_ten.rs index ada07db93fbf..51d614431fed 100644 --- a/template/hydroflow_plus/examples/first_ten.rs +++ b/template/hydroflow_plus/examples/first_ten.rs @@ -9,7 +9,6 @@ async fn main() { hydroflow_plus_template::first_ten::first_ten(&process); let _nodes = flow - .with_default_optimize() .with_process(&process, deployment.Localhost()) .deploy(&mut deployment); diff --git a/template/hydroflow_plus/examples/first_ten_distributed.rs b/template/hydroflow_plus/examples/first_ten_distributed.rs index e75bdbb1ecd6..472a6c7774d6 100644 --- a/template/hydroflow_plus/examples/first_ten_distributed.rs +++ b/template/hydroflow_plus/examples/first_ten_distributed.rs @@ -8,7 +8,6 @@ async fn main() { let (p1, p2) = hydroflow_plus_template::first_ten_distributed::first_ten_distributed(&flow); let _nodes = flow - .with_default_optimize() .with_process(&p1, deployment.Localhost()) .with_process(&p2, deployment.Localhost()) .deploy(&mut deployment); diff --git a/template/hydroflow_plus/examples/first_ten_distributed_gcp.rs b/template/hydroflow_plus/examples/first_ten_distributed_gcp.rs index 3caf775ce50d..4193dc98ef95 100644 --- a/template/hydroflow_plus/examples/first_ten_distributed_gcp.rs +++ b/template/hydroflow_plus/examples/first_ten_distributed_gcp.rs @@ -21,7 +21,6 @@ async fn main() { let (p1, p2) = hydroflow_plus_template::first_ten_distributed::first_ten_distributed(&flow); let _nodes = flow - .with_default_optimize() .with_process( &p1, TrybuildHost::new( diff --git a/template/hydroflow_plus/src/first_ten_distributed.rs b/template/hydroflow_plus/src/first_ten_distributed.rs index c4d8d1cbcccc..5385d002c726 100644 --- a/template/hydroflow_plus/src/first_ten_distributed.rs +++ b/template/hydroflow_plus/src/first_ten_distributed.rs @@ -31,7 +31,6 @@ mod tests { let (p1, p2) = super::first_ten_distributed(&flow); let nodes = flow - .with_default_optimize() .with_process(&p1, localhost.clone()) .with_process(&p2, localhost.clone()) .deploy(&mut deployment); From dcd48fc7ee805898d9b5ef0d082870e30615e95b Mon Sep 17 00:00:00 2001 From: "hydro-project-bot[bot]" <132423234+hydro-project-bot[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 19:24:04 +0000 Subject: [PATCH 47/74] Release hydroflow_lang v0.10.0, hydroflow_datalog_core v0.10.0, hydroflow_datalog v0.10.0, hydroflow_deploy_integration v0.10.0, hydroflow_macro v0.10.0, lattices_macro v0.5.7, variadics v0.0.7, variadics_macro v0.5.5, lattices v0.5.8, multiplatform_test v0.3.0, pusherator v0.0.9, hydroflow v0.10.0, hydro_deploy v0.10.0, stageleft_macro v0.4.0, stageleft v0.5.0, stageleft_tool v0.4.0, hydroflow_plus v0.10.0, hydro_cli v0.10.0, safety bump 8 crates SAFETY BUMP: hydroflow_datalog_core v0.10.0, hydroflow_datalog v0.10.0, hydroflow_macro v0.10.0, hydroflow v0.10.0, hydro_deploy v0.10.0, stageleft v0.5.0, hydroflow_plus v0.10.0, hydro_cli v0.10.0 --- Cargo.lock | 34 +- datastores/gossip_kv/Cargo.toml | 2 +- hydro_deploy/core/CHANGELOG.md | 113 ++++++- hydro_deploy/core/Cargo.toml | 4 +- hydro_deploy/hydro_cli/CHANGELOG.md | 40 ++- hydro_deploy/hydro_cli/Cargo.toml | 6 +- .../hydroflow_deploy_integration/CHANGELOG.md | 33 +- .../hydroflow_deploy_integration/Cargo.toml | 2 +- hydroflow/CHANGELOG.md | 122 ++++++- hydroflow/Cargo.toml | 20 +- hydroflow_datalog/CHANGELOG.md | 42 ++- hydroflow_datalog/Cargo.toml | 4 +- hydroflow_datalog_core/CHANGELOG.md | 67 +++- hydroflow_datalog_core/Cargo.toml | 4 +- hydroflow_lang/CHANGELOG.md | 111 ++++++- hydroflow_lang/Cargo.toml | 2 +- hydroflow_macro/CHANGELOG.md | 46 ++- hydroflow_macro/Cargo.toml | 6 +- hydroflow_plus/CHANGELOG.md | 310 +++++++++++++++++- hydroflow_plus/Cargo.toml | 16 +- hydroflow_plus_test/Cargo.toml | 10 +- hydroflow_plus_test_local/Cargo.toml | 8 +- hydroflow_plus_test_local_macro/Cargo.toml | 6 +- lattices/CHANGELOG.md | 71 +++- lattices/Cargo.toml | 6 +- lattices_macro/CHANGELOG.md | 32 +- lattices_macro/Cargo.toml | 2 +- multiplatform_test/CHANGELOG.md | 32 +- multiplatform_test/Cargo.toml | 2 +- pusherator/CHANGELOG.md | 39 ++- pusherator/Cargo.toml | 4 +- stageleft/CHANGELOG.md | 55 +++- stageleft/Cargo.toml | 4 +- stageleft_macro/CHANGELOG.md | 72 +++- stageleft_macro/Cargo.toml | 2 +- stageleft_test/Cargo.toml | 4 +- stageleft_test_macro/Cargo.toml | 4 +- stageleft_tool/CHANGELOG.md | 74 ++++- stageleft_tool/Cargo.toml | 2 +- variadics/CHANGELOG.md | 84 ++++- variadics/Cargo.toml | 2 +- variadics_macro/CHANGELOG.md | 34 ++ variadics_macro/Cargo.toml | 2 +- 43 files changed, 1405 insertions(+), 130 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4d39f542cae..f194522cfb0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1404,7 +1404,7 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hydro_cli" -version = "0.9.0" +version = "0.10.0" dependencies = [ "anyhow", "async-ssh2-lite", @@ -1435,7 +1435,7 @@ dependencies = [ [[package]] name = "hydro_deploy" -version = "0.9.0" +version = "0.10.0" dependencies = [ "anyhow", "async-process", @@ -1467,7 +1467,7 @@ dependencies = [ [[package]] name = "hydroflow" -version = "0.9.0" +version = "0.10.0" dependencies = [ "bincode", "byteorder", @@ -1514,7 +1514,7 @@ dependencies = [ [[package]] name = "hydroflow_datalog" -version = "0.9.0" +version = "0.10.0" dependencies = [ "hydroflow_datalog_core", "proc-macro-crate", @@ -1525,7 +1525,7 @@ dependencies = [ [[package]] name = "hydroflow_datalog_core" -version = "0.9.0" +version = "0.10.0" dependencies = [ "hydroflow_lang", "insta", @@ -1541,7 +1541,7 @@ dependencies = [ [[package]] name = "hydroflow_deploy_integration" -version = "0.9.0" +version = "0.10.0" dependencies = [ "async-recursion", "async-trait", @@ -1557,7 +1557,7 @@ dependencies = [ [[package]] name = "hydroflow_lang" -version = "0.9.0" +version = "0.10.0" dependencies = [ "auto_impl", "clap", @@ -1575,7 +1575,7 @@ dependencies = [ [[package]] name = "hydroflow_macro" -version = "0.9.0" +version = "0.10.0" dependencies = [ "hydroflow_lang", "itertools", @@ -1587,7 +1587,7 @@ dependencies = [ [[package]] name = "hydroflow_plus" -version = "0.9.0" +version = "0.10.0" dependencies = [ "async-ssh2-lite", "bincode", @@ -1897,7 +1897,7 @@ dependencies = [ [[package]] name = "lattices" -version = "0.5.7" +version = "0.5.8" dependencies = [ "cc-traits", "lattices_macro", @@ -1911,7 +1911,7 @@ dependencies = [ [[package]] name = "lattices_macro" -version = "0.5.6" +version = "0.5.7" dependencies = [ "insta", "prettyplease", @@ -2119,7 +2119,7 @@ dependencies = [ [[package]] name = "multiplatform_test" -version = "0.2.0" +version = "0.3.0" dependencies = [ "env_logger", "log", @@ -2637,7 +2637,7 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "pusherator" -version = "0.0.8" +version = "0.0.9" dependencies = [ "either", "variadics", @@ -3333,7 +3333,7 @@ dependencies = [ [[package]] name = "stageleft" -version = "0.4.0" +version = "0.5.0" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3344,7 +3344,7 @@ dependencies = [ [[package]] name = "stageleft_macro" -version = "0.3.0" +version = "0.4.0" dependencies = [ "insta", "prettyplease", @@ -3374,7 +3374,7 @@ dependencies = [ [[package]] name = "stageleft_tool" -version = "0.3.0" +version = "0.4.0" dependencies = [ "proc-macro2", "quote", @@ -4090,7 +4090,7 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "variadics" -version = "0.0.6" +version = "0.0.7" dependencies = [ "hashbrown 0.14.5", "sealed", diff --git a/datastores/gossip_kv/Cargo.toml b/datastores/gossip_kv/Cargo.toml index 37943072a7d1..c2b7e8dc243b 100644 --- a/datastores/gossip_kv/Cargo.toml +++ b/datastores/gossip_kv/Cargo.toml @@ -42,4 +42,4 @@ path = "cli/main.rs" [lib] name = "gossip_kv" -path = "kv/lib.rs" \ No newline at end of file +path = "kv/lib.rs" diff --git a/hydro_deploy/core/CHANGELOG.md b/hydro_deploy/core/CHANGELOG.md index 26f8b6922b0e..f526879cdf38 100644 --- a/hydro_deploy/core/CHANGELOG.md +++ b/hydro_deploy/core/CHANGELOG.md @@ -5,8 +5,82 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.10.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - add ability to have staged flows inside unit tests + Whenever a Hydroflow+ program is compiled, it depends on a generated + `__staged` module, which contains the entire contents of the crate but + with every type / function made `pub` and exported, so that the compiled + UDFs can resolve local references appropriately. + + Previously, we would not do this for `#[cfg(test)]` modules, since they + may use `dev-dependencies` and therefore the generated module may fail + to compile when not in test mode. To solve this, when running a unit + test (marked with `hydroflow_plus::deploy::init_test()`) that uses + trybuild, we emit a version of the `__staged` module with `#[cfg(test)]` + modules included _into the generated trybuild sources_ because we can + guarantee via trybuild that the appropriate `dev-dependencies` are + available. + + This by itself allows crates depending on `hydroflow_plus` to have local + unit tests with Hydroflow+ logic inside them. But we also want to use + this support for unit tests inside `hydroflow_plus` itself. To enable + that, we eliminate the `hydroflow_plus_deploy` crate and move its + contents directly to `hydroflow_plus` itself so that we can access the + trybuild machinery without incurring a circular dependency. + + Also fixes #1408 + - add API for external network inputs + This is a key step towards being able to unit-test HF+ graphs, by being + able to have controlled inputs. Outputs next. + +### Style + + - fixes for latest nightly clippy + +### Commit Statistics + + + + - 4 commits contributed to the release. + - 69 days passed between releases. + - 4 commits were understood as [conventional](https://www.conventionalcommits.org). + - 4 unique issues were worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1449](https://github.com/hydro-project/hydroflow/issues/1449), [#1450](https://github.com/hydro-project/hydroflow/issues/1450), [#1537](https://github.com/hydro-project/hydroflow/issues/1537) + +### Commit Details + + + +

view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1449](https://github.com/hydro-project/hydroflow/issues/1449)** + - Add API for external network inputs ([`8a80931`](https://github.com/hydro-project/hydroflow/commit/8a809315cd37929687fcabc34a12042db25d5767)) + * **[#1450](https://github.com/hydro-project/hydroflow/issues/1450)** + - Add ability to have staged flows inside unit tests ([`afe78c3`](https://github.com/hydro-project/hydroflow/commit/afe78c343658472513b34d28658634b253148aee)) + * **[#1537](https://github.com/hydro-project/hydroflow/issues/1537)** + - Fixes for latest nightly clippy ([`8442d1b`](https://github.com/hydro-project/hydroflow/commit/8442d1b524621a9f8b43372a9c25991efb33c25e)) +
+ ## v0.9.0 (2024-08-30) + + + + + + + + + + ### Chore - manually set versions for crates renamed in #1413 @@ -15,6 +89,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --------- +### Refactor (BREAKING) + + - simplify process/cluster specs + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1394). + * #1395 + * __->__ #1394 + ### Documentation - cleanup doc comments for clippy latest @@ -79,11 +164,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - use `buildstructor` to handle excessive `Deployment` method arguments, fix #1364 Adds new method `Deployment::AzureHost` + simplify process/cluster specs + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1394). + * #1395 + * __->__ #1394 + - end-to-end flamegraph generation, fix #1365 + Depends on #1370 + - `Deployment.stop()` for graceful shutdown including updated `perf` profile downloading + * `perf` profile downloading moved from the `drop()` impl to `async fn + stop()` + * download perf data via stdout + * update async-ssh2-lite to 0.5 to cleanup tokio compat issues + + WIP for #1365 + - use `buildstructor` to handle excessive `Deployment` method arguments, fix #1364 + Adds new method `Deployment::AzureHost` + ### Commit Statistics - - 19 commits contributed to the release. + - 20 commits contributed to the release. + - 38 days passed between releases. - 18 commits were understood as [conventional](https://www.conventionalcommits.org). - 17 unique issues were worked on: [#1313](https://github.com/hydro-project/hydroflow/issues/1313), [#1360](https://github.com/hydro-project/hydroflow/issues/1360), [#1366](https://github.com/hydro-project/hydroflow/issues/1366), [#1369](https://github.com/hydro-project/hydroflow/issues/1369), [#1370](https://github.com/hydro-project/hydroflow/issues/1370), [#1372](https://github.com/hydro-project/hydroflow/issues/1372), [#1378](https://github.com/hydro-project/hydroflow/issues/1378), [#1394](https://github.com/hydro-project/hydroflow/issues/1394), [#1396](https://github.com/hydro-project/hydroflow/issues/1396), [#1398](https://github.com/hydro-project/hydroflow/issues/1398), [#1403](https://github.com/hydro-project/hydroflow/issues/1403), [#1411](https://github.com/hydro-project/hydroflow/issues/1411), [#1413](https://github.com/hydro-project/hydroflow/issues/1413), [#1423](https://github.com/hydro-project/hydroflow/issues/1423), [#1428](https://github.com/hydro-project/hydroflow/issues/1428), [#1429](https://github.com/hydro-project/hydroflow/issues/1429), [#1431](https://github.com/hydro-project/hydroflow/issues/1431) @@ -129,6 +235,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **[#1431](https://github.com/hydro-project/hydroflow/issues/1431)** - Only record usermode events in perf ([`c4683ca`](https://github.com/hydro-project/hydroflow/commit/c4683caca43f2927694c920b43ef35a6d1629eaa)) * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) - Manually set versions for crates renamed in #1413 ([`a2ec110`](https://github.com/hydro-project/hydroflow/commit/a2ec110ccadb97e293b19d83a155d98d94224bba)) @@ -196,6 +303,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 11 commits contributed to the release. + - 59 days passed between releases. - 10 commits were understood as [conventional](https://www.conventionalcommits.org). - 10 unique issues were worked on: [#1334](https://github.com/hydro-project/hydroflow/issues/1334), [#1338](https://github.com/hydro-project/hydroflow/issues/1338), [#1339](https://github.com/hydro-project/hydroflow/issues/1339), [#1340](https://github.com/hydro-project/hydroflow/issues/1340), [#1343](https://github.com/hydro-project/hydroflow/issues/1343), [#1345](https://github.com/hydro-project/hydroflow/issues/1345), [#1346](https://github.com/hydro-project/hydroflow/issues/1346), [#1347](https://github.com/hydro-project/hydroflow/issues/1347), [#1348](https://github.com/hydro-project/hydroflow/issues/1348), [#1356](https://github.com/hydro-project/hydroflow/issues/1356) @@ -245,6 +353,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 3 commits contributed to the release. + - 44 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1129](https://github.com/hydro-project/hydroflow/issues/1129), [#1157](https://github.com/hydro-project/hydroflow/issues/1157) @@ -275,6 +384,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 4 commits contributed to the release. + - 38 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1090](https://github.com/hydro-project/hydroflow/issues/1090) @@ -316,6 +426,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 4 commits contributed to the release. + - 32 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1015](https://github.com/hydro-project/hydroflow/issues/1015), [#1043](https://github.com/hydro-project/hydroflow/issues/1043), [#1084](https://github.com/hydro-project/hydroflow/issues/1084) diff --git a/hydro_deploy/core/Cargo.toml b/hydro_deploy/core/Cargo.toml index f74fc73fa066..a93ee31e7b20 100644 --- a/hydro_deploy/core/Cargo.toml +++ b/hydro_deploy/core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydro_deploy" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydro_deploy/" @@ -22,7 +22,7 @@ cargo_metadata = "0.18.0" dunce = "1.0.0" dyn-clone = "1.0.0" futures = "0.3.0" -hydroflow_deploy_integration = { path = "../hydroflow_deploy_integration", version = "^0.9.0" } +hydroflow_deploy_integration = { path = "../hydroflow_deploy_integration", version = "^0.10.0" } indicatif = "0.17.0" inferno = "0.11.0" itertools = "0.10.0" # TODO(mingwei): remove when `iter_intersperse` is stabilized. diff --git a/hydro_deploy/hydro_cli/CHANGELOG.md b/hydro_deploy/hydro_cli/CHANGELOG.md index 6e2875acbf5d..e87f947e1ddd 100644 --- a/hydro_deploy/hydro_cli/CHANGELOG.md +++ b/hydro_deploy/hydro_cli/CHANGELOG.md @@ -5,8 +5,39 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 69 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) +
+ ## 0.9.0 (2024-08-30) + + + + + + ### Chore - manually set versions for crates renamed in #1413 @@ -46,7 +77,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 8 commits contributed to the release. + - 9 commits contributed to the release. + - 38 days passed between releases. - 8 commits were understood as [conventional](https://www.conventionalcommits.org). - 7 unique issues were worked on: [#1313](https://github.com/hydro-project/hydroflow/issues/1313), [#1366](https://github.com/hydro-project/hydroflow/issues/1366), [#1370](https://github.com/hydro-project/hydroflow/issues/1370), [#1398](https://github.com/hydro-project/hydroflow/issues/1398), [#1403](https://github.com/hydro-project/hydroflow/issues/1403), [#1413](https://github.com/hydro-project/hydroflow/issues/1413), [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -71,6 +103,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) - Manually set versions for crates renamed in #1413 ([`a2ec110`](https://github.com/hydro-project/hydroflow/commit/a2ec110ccadb97e293b19d83a155d98d94224bba)) @@ -130,6 +163,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 9 commits contributed to the release. + - 59 days passed between releases. - 8 commits were understood as [conventional](https://www.conventionalcommits.org). - 8 unique issues were worked on: [#1309](https://github.com/hydro-project/hydroflow/issues/1309), [#1334](https://github.com/hydro-project/hydroflow/issues/1334), [#1339](https://github.com/hydro-project/hydroflow/issues/1339), [#1340](https://github.com/hydro-project/hydroflow/issues/1340), [#1345](https://github.com/hydro-project/hydroflow/issues/1345), [#1346](https://github.com/hydro-project/hydroflow/issues/1346), [#1347](https://github.com/hydro-project/hydroflow/issues/1347), [#1356](https://github.com/hydro-project/hydroflow/issues/1356) @@ -176,6 +210,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 3 commits contributed to the release. + - 44 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1152](https://github.com/hydro-project/hydroflow/issues/1152), [#1157](https://github.com/hydro-project/hydroflow/issues/1157) @@ -206,6 +241,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 4 commits contributed to the release. + - 38 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1090](https://github.com/hydro-project/hydroflow/issues/1090) @@ -242,6 +278,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 3 commits contributed to the release. + - 28 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1015](https://github.com/hydro-project/hydroflow/issues/1015), [#1043](https://github.com/hydro-project/hydroflow/issues/1043) @@ -326,6 +363,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 7 commits contributed to the release. + - 114 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#1046](https://github.com/hydro-project/hydroflow/issues/1046), [#986](https://github.com/hydro-project/hydroflow/issues/986), [#987](https://github.com/hydro-project/hydroflow/issues/987), [#994](https://github.com/hydro-project/hydroflow/issues/994) diff --git a/hydro_deploy/hydro_cli/Cargo.toml b/hydro_deploy/hydro_cli/Cargo.toml index 815fdb9f5190..5dcae2c98634 100644 --- a/hydro_deploy/hydro_cli/Cargo.toml +++ b/hydro_deploy/hydro_cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydro_cli" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydro_cli/" @@ -16,7 +16,7 @@ name = "hydro_cli" crate-type = ["cdylib"] [dependencies] -hydro_deploy = { path = "../core", version = "^0.9.0" } +hydro_deploy = { path = "../core", version = "^0.10.0" } tokio = { version = "1.29.0", features = [ "full" ] } anyhow = { version = "1.0.82", features = [ "backtrace" ] } clap = { version = "4.5.4", features = ["derive"] } @@ -25,7 +25,7 @@ pyo3-asyncio = { version = "0.20.0", features = ["attributes", "tokio-runtime"] pythonize = "0.20.0" futures = "0.3.0" bytes = "1.1.0" -hydroflow_deploy_integration = { path = "../hydroflow_deploy_integration", version = "^0.9.0" } +hydroflow_deploy_integration = { path = "../hydroflow_deploy_integration", version = "^0.10.0" } # request vendored openssl async-ssh2-lite = { version = "0.5.0", features = [ "vendored-openssl" ] } diff --git a/hydro_deploy/hydroflow_deploy_integration/CHANGELOG.md b/hydro_deploy/hydroflow_deploy_integration/CHANGELOG.md index 187072076cc4..d7f61787aaa7 100644 --- a/hydro_deploy/hydroflow_deploy_integration/CHANGELOG.md +++ b/hydro_deploy/hydroflow_deploy_integration/CHANGELOG.md @@ -5,8 +5,38 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 69 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) +
+ ## 0.9.0 (2024-08-30) + + + + + ### Chore - manually set versions for crates renamed in #1413 @@ -27,7 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 4 commits contributed to the release. + - 5 commits contributed to the release. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1413](https://github.com/hydro-project/hydroflow/issues/1413), [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -42,6 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) - Manually set versions for crates renamed in #1413 ([`a2ec110`](https://github.com/hydro-project/hydroflow/commit/a2ec110ccadb97e293b19d83a155d98d94224bba)) - Update `RELEASING.md` notes, prep for release, wip ([`c41787f`](https://github.com/hydro-project/hydroflow/commit/c41787f527859cb9d704736ecdea5ca7bc641460)) diff --git a/hydro_deploy/hydroflow_deploy_integration/Cargo.toml b/hydro_deploy/hydroflow_deploy_integration/Cargo.toml index 37c954e85232..f87f512c81a6 100644 --- a/hydro_deploy/hydroflow_deploy_integration/Cargo.toml +++ b/hydro_deploy/hydroflow_deploy_integration/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydroflow_deploy_integration" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydroflow_deploy_integration/" diff --git a/hydroflow/CHANGELOG.md b/hydroflow/CHANGELOG.md index 1a5930a2bceb..714c405bee85 100644 --- a/hydroflow/CHANGELOG.md +++ b/hydroflow/CHANGELOG.md @@ -5,8 +5,100 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2024-11-08) + +### Chore + + - update `proc-macro2`, fixes span info, fix #729 + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - generalized hash trie indexes for relational tuples + Generalized Hash Tries are part of the SIGMOD '23 FreeJoin + [paper](https://dl.acm.org/doi/abs/10.1145/3589295) by + Wang/Willsey/Suciu. They provide a compressed ("factorized") + representation of relations. By operating in the factorized domain, join + algorithms can defer cross-products and achieve asymptotically optimal + performance. + + --------- + +### Bug Fixes + + - `cross_singleton()` forgot value if multiple runs in a tick, fix #1518 + Adds the minimal reproducer test from @shadaj + + Note this may have negative performance implications, as the singleton value now is stored in the state API (heap) instead of locally. If we use singleton syntax this duplicate allocation could probably be avoided. + + > Confirmed that this fixed the bugs in our Paxos implementation, no noticeable performance impact. @shadj + - cleanup temp tcp networking code, fix race condition fix #1458 + consolidate into one task to prevent races + +### Style + + - fixes for latest nightly clippy + - fixes for nightly clippy + a couple few spurious `too_many_arguments` and a spurious + `zombie_processes` still on current nightly (`clippy 0.1.84 (4392847410 + 2024-10-21)`) + +### Test + + - ignore trybuild tests inconsistent on latest nightly + +### Bug Fixes (BREAKING) + + - fix #1401 `lattice_bimorphism()` double-emit, add docs + Fixes the issue by combining the all values generated per subgraph + execution into one, which effectively de-duplicates the values. + + Adds basic docs. + +### Commit Statistics + + + + - 9 commits contributed to the release. + - 69 days passed between releases. + - 9 commits were understood as [conventional](https://www.conventionalcommits.org). + - 8 unique issues were worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1446](https://github.com/hydro-project/hydroflow/issues/1446), [#1497](https://github.com/hydro-project/hydroflow/issues/1497), [#1503](https://github.com/hydro-project/hydroflow/issues/1503), [#1505](https://github.com/hydro-project/hydroflow/issues/1505), [#1520](https://github.com/hydro-project/hydroflow/issues/1520), [#1522](https://github.com/hydro-project/hydroflow/issues/1522), [#1537](https://github.com/hydro-project/hydroflow/issues/1537) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1446](https://github.com/hydro-project/hydroflow/issues/1446)** + - Cleanup temp tcp networking code, fix race condition fix #1458 ([`b961233`](https://github.com/hydro-project/hydroflow/commit/b96123369cec3d6d407af973f1896b2734fd92ef)) + * **[#1497](https://github.com/hydro-project/hydroflow/issues/1497)** + - Update `proc-macro2`, fixes span info, fix #729 ([`e564b13`](https://github.com/hydro-project/hydroflow/commit/e564b133a4db192e0331d427aa80ef52cd97608c)) + * **[#1503](https://github.com/hydro-project/hydroflow/issues/1503)** + - Generalized hash trie indexes for relational tuples ([`f7e740f`](https://github.com/hydro-project/hydroflow/commit/f7e740fb2ba36d0fcf3fd196d60333552911e3a4)) + * **[#1505](https://github.com/hydro-project/hydroflow/issues/1505)** + - Fixes for nightly clippy ([`47cb703`](https://github.com/hydro-project/hydroflow/commit/47cb703e771f7d1c451ceb9d185ada96410949da)) + * **[#1520](https://github.com/hydro-project/hydroflow/issues/1520)** + - `cross_singleton()` forgot value if multiple runs in a tick, fix #1518 ([`16b730c`](https://github.com/hydro-project/hydroflow/commit/16b730c75cfca79ea5f869308b1e1e14b3e9c155)) + * **[#1522](https://github.com/hydro-project/hydroflow/issues/1522)** + - Fix #1401 `lattice_bimorphism()` double-emit, add docs ([`e796200`](https://github.com/hydro-project/hydroflow/commit/e796200743f2cc2da5a0e91c492f016ca98008e8)) + * **[#1537](https://github.com/hydro-project/hydroflow/issues/1537)** + - Fixes for latest nightly clippy ([`8442d1b`](https://github.com/hydro-project/hydroflow/commit/8442d1b524621a9f8b43372a9c25991efb33c25e)) + * **Uncategorized** + - Ignore trybuild tests inconsistent on latest nightly ([`656ee32`](https://github.com/hydro-project/hydroflow/commit/656ee328c8710bce7370c851437a80ca3db46a5a)) +
+ ## 0.9.0 (2024-08-30) + + + + + + + ### Chore - manually set versions for crates renamed in #1413 @@ -55,7 +147,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 12 commits contributed to the release. + - 13 commits contributed to the release. + - 38 days passed between releases. - 12 commits were understood as [conventional](https://www.conventionalcommits.org). - 11 unique issues were worked on: [#1370](https://github.com/hydro-project/hydroflow/issues/1370), [#1373](https://github.com/hydro-project/hydroflow/issues/1373), [#1399](https://github.com/hydro-project/hydroflow/issues/1399), [#1407](https://github.com/hydro-project/hydroflow/issues/1407), [#1409](https://github.com/hydro-project/hydroflow/issues/1409), [#1413](https://github.com/hydro-project/hydroflow/issues/1413), [#1416](https://github.com/hydro-project/hydroflow/issues/1416), [#1417](https://github.com/hydro-project/hydroflow/issues/1417), [#1420](https://github.com/hydro-project/hydroflow/issues/1420), [#1423](https://github.com/hydro-project/hydroflow/issues/1423), [#1428](https://github.com/hydro-project/hydroflow/issues/1428) @@ -88,6 +181,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **[#1428](https://github.com/hydro-project/hydroflow/issues/1428)** - Cleanup doc comments for clippy latest ([`f5f1eb0`](https://github.com/hydro-project/hydroflow/commit/f5f1eb0c612f5c0c1752360d972ef6853c5e12f0)) * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) - Manually set versions for crates renamed in #1413 ([`a2ec110`](https://github.com/hydro-project/hydroflow/commit/a2ec110ccadb97e293b19d83a155d98d94224bba)) @@ -204,6 +298,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 17 commits contributed to the release. + - 59 days passed between releases. - 16 commits were understood as [conventional](https://www.conventionalcommits.org). - 17 unique issues were worked on: [#1143](https://github.com/hydro-project/hydroflow/issues/1143), [#1216](https://github.com/hydro-project/hydroflow/issues/1216), [#1244](https://github.com/hydro-project/hydroflow/issues/1244), [#1260](https://github.com/hydro-project/hydroflow/issues/1260), [#1271](https://github.com/hydro-project/hydroflow/issues/1271), [#1273](https://github.com/hydro-project/hydroflow/issues/1273), [#1274](https://github.com/hydro-project/hydroflow/issues/1274), [#1280](https://github.com/hydro-project/hydroflow/issues/1280), [#1283](https://github.com/hydro-project/hydroflow/issues/1283), [#1295](https://github.com/hydro-project/hydroflow/issues/1295), [#1296](https://github.com/hydro-project/hydroflow/issues/1296), [#1297](https://github.com/hydro-project/hydroflow/issues/1297), [#1300](https://github.com/hydro-project/hydroflow/issues/1300), [#1309](https://github.com/hydro-project/hydroflow/issues/1309), [#1312](https://github.com/hydro-project/hydroflow/issues/1312), [#1332](https://github.com/hydro-project/hydroflow/issues/1332), [#1345](https://github.com/hydro-project/hydroflow/issues/1345) @@ -251,15 +346,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Release hydroflow_lang v0.8.0, hydroflow_datalog_core v0.8.0, hydroflow_datalog v0.8.0, hydroflow_macro v0.8.0, lattices_macro v0.5.5, lattices v0.5.6, variadics v0.0.5, pusherator v0.0.7, hydroflow v0.8.0, hydroflow_plus v0.8.0, hydro_deploy v0.8.0, hydro_cli v0.8.0, hydroflow_plus_cli_integration v0.8.0, safety bump 7 crates ([`ca6c16b`](https://github.com/hydro-project/hydroflow/commit/ca6c16b4a7ce35e155fe7fc6c7d1676c37c9e4de)) - -Other 'tick state will need to be cleared, but existing implementation does that when the iterator runs, which is good enough. There is only a problem if a singleton can reference the state before the iterator runs, in that case. allow use of generics in demux_enum::<...>() opensures turbofish syntax for the match clauses, which is needed when there are generic parameters in the typeadds a test as well yield before collect_ready_async to ensure background async tasks can send to the stream make sure tasks are spawned!!!!fix bug introduced in #978 Make inner for WithTop & WithBot privateOption is not a lattice, so it is unsafe to expose as public.I also updated documentation to lead with intention beforeimplementation (minor cleanup).To emulate this unintuitive behavior, we currently ensure that apersist::<'static>() exists before operator that references thesingleton (filter, in this case). (Note that this is equivalent tocross_join::<'static>() and not cross_join::<'tick>())However singletons also have had a different mechanism that affectsthis- currently singleton references create a next-stratum constraint,that ensures a singleton referencer must be in a later stratum than thesingleton it is referencing.Note that this actually prevents the example situation above fromhappening– the updates to y will be received all at once at the startof the next stratum.This means that actually, currently singletons are equivalent tosomething like:ruststream -> cj[0]; -y -> next_stratum() -> last() -> cj[1]; -cj = cross_join() -> filter(|(item, y)| ...) -> ... -last() is a hypothetical operator that only keeps the most recent itemoutput by y. next_stratum() -> last() is equivalent to reduce(|acc, item| *acc = item) (since that comes with a stratum barrier). Sotechnically this is a slightly different behavior than just cross_join,but it is more intuitive.ruststream -> cj[0]; -y -> reduce(|acc, item| { *acc = item; }) -> cj[1]; -cj = cross_join() -> filter(|(item, y)| ...) -> ... -Also fixes #1293 - ## 0.7.0 (2024-05-24) @@ -342,6 +428,7 @@ Also fixes #1293 - 31 commits contributed to the release. + - 44 days passed between releases. - 27 commits were understood as [conventional](https://www.conventionalcommits.org). - 23 unique issues were worked on: [#1120](https://github.com/hydro-project/hydroflow/issues/1120), [#1143](https://github.com/hydro-project/hydroflow/issues/1143), [#1152](https://github.com/hydro-project/hydroflow/issues/1152), [#1159](https://github.com/hydro-project/hydroflow/issues/1159), [#1164](https://github.com/hydro-project/hydroflow/issues/1164), [#1166](https://github.com/hydro-project/hydroflow/issues/1166), [#1167](https://github.com/hydro-project/hydroflow/issues/1167), [#1171](https://github.com/hydro-project/hydroflow/issues/1171), [#1176](https://github.com/hydro-project/hydroflow/issues/1176), [#1178](https://github.com/hydro-project/hydroflow/issues/1178), [#1182](https://github.com/hydro-project/hydroflow/issues/1182), [#1190](https://github.com/hydro-project/hydroflow/issues/1190), [#1191](https://github.com/hydro-project/hydroflow/issues/1191), [#1192](https://github.com/hydro-project/hydroflow/issues/1192), [#1193](https://github.com/hydro-project/hydroflow/issues/1193), [#1196](https://github.com/hydro-project/hydroflow/issues/1196), [#1197](https://github.com/hydro-project/hydroflow/issues/1197), [#1198](https://github.com/hydro-project/hydroflow/issues/1198), [#1199](https://github.com/hydro-project/hydroflow/issues/1199), [#1204](https://github.com/hydro-project/hydroflow/issues/1204), [#1232](https://github.com/hydro-project/hydroflow/issues/1232), [#1236](https://github.com/hydro-project/hydroflow/issues/1236), [#1238](https://github.com/hydro-project/hydroflow/issues/1238) @@ -435,6 +522,7 @@ Also fixes #1293 - 6 commits contributed to the release. + - 3 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1134](https://github.com/hydro-project/hydroflow/issues/1134), [#1148](https://github.com/hydro-project/hydroflow/issues/1148), [#1150](https://github.com/hydro-project/hydroflow/issues/1150) @@ -533,6 +621,7 @@ Also fixes #1293 - 28 commits contributed to the release. + - 34 days passed between releases. - 27 commits were understood as [conventional](https://www.conventionalcommits.org). - 18 unique issues were worked on: [#1068](https://github.com/hydro-project/hydroflow/issues/1068), [#1086](https://github.com/hydro-project/hydroflow/issues/1086), [#1087](https://github.com/hydro-project/hydroflow/issues/1087), [#1089](https://github.com/hydro-project/hydroflow/issues/1089), [#1090](https://github.com/hydro-project/hydroflow/issues/1090), [#1091](https://github.com/hydro-project/hydroflow/issues/1091), [#1093](https://github.com/hydro-project/hydroflow/issues/1093), [#1094](https://github.com/hydro-project/hydroflow/issues/1094), [#1102](https://github.com/hydro-project/hydroflow/issues/1102), [#1113](https://github.com/hydro-project/hydroflow/issues/1113), [#1125](https://github.com/hydro-project/hydroflow/issues/1125), [#1128](https://github.com/hydro-project/hydroflow/issues/1128), [#1132](https://github.com/hydro-project/hydroflow/issues/1132), [#1133](https://github.com/hydro-project/hydroflow/issues/1133), [#1137](https://github.com/hydro-project/hydroflow/issues/1137), [#1140](https://github.com/hydro-project/hydroflow/issues/1140), [#1145](https://github.com/hydro-project/hydroflow/issues/1145), [#1146](https://github.com/hydro-project/hydroflow/issues/1146) @@ -630,6 +719,7 @@ Also fixes #1293 - 10 commits contributed to the release. + - 28 days passed between releases. - 9 commits were understood as [conventional](https://www.conventionalcommits.org). - 6 unique issues were worked on: [#1015](https://github.com/hydro-project/hydroflow/issues/1015), [#1057](https://github.com/hydro-project/hydroflow/issues/1057), [#1060](https://github.com/hydro-project/hydroflow/issues/1060), [#1061](https://github.com/hydro-project/hydroflow/issues/1061), [#1084](https://github.com/hydro-project/hydroflow/issues/1084), [#1085](https://github.com/hydro-project/hydroflow/issues/1085) @@ -679,6 +769,7 @@ Also fixes #1293 - 5 commits contributed to the release. + - 4 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#1041](https://github.com/hydro-project/hydroflow/issues/1041), [#1051](https://github.com/hydro-project/hydroflow/issues/1051), [#1054](https://github.com/hydro-project/hydroflow/issues/1054), [#1055](https://github.com/hydro-project/hydroflow/issues/1055) @@ -750,6 +841,8 @@ Also fixes #1293 -- - new implementation and Hydro Deploy setup -- + - new implementation and Hydro Deploy setup + -- ### Bug Fixes @@ -788,6 +881,7 @@ Also fixes #1293 - 27 commits contributed to the release. + - 110 days passed between releases. - 26 commits were understood as [conventional](https://www.conventionalcommits.org). - 23 unique issues were worked on: [#1003](https://github.com/hydro-project/hydroflow/issues/1003), [#1005](https://github.com/hydro-project/hydroflow/issues/1005), [#1024](https://github.com/hydro-project/hydroflow/issues/1024), [#1025](https://github.com/hydro-project/hydroflow/issues/1025), [#1026](https://github.com/hydro-project/hydroflow/issues/1026), [#1032](https://github.com/hydro-project/hydroflow/issues/1032), [#1036](https://github.com/hydro-project/hydroflow/issues/1036), [#899](https://github.com/hydro-project/hydroflow/issues/899), [#909](https://github.com/hydro-project/hydroflow/issues/909), [#942](https://github.com/hydro-project/hydroflow/issues/942), [#945](https://github.com/hydro-project/hydroflow/issues/945), [#948](https://github.com/hydro-project/hydroflow/issues/948), [#950](https://github.com/hydro-project/hydroflow/issues/950), [#959](https://github.com/hydro-project/hydroflow/issues/959), [#960](https://github.com/hydro-project/hydroflow/issues/960), [#967](https://github.com/hydro-project/hydroflow/issues/967), [#971](https://github.com/hydro-project/hydroflow/issues/971), [#974](https://github.com/hydro-project/hydroflow/issues/974), [#978](https://github.com/hydro-project/hydroflow/issues/978), [#979](https://github.com/hydro-project/hydroflow/issues/979), [#984](https://github.com/hydro-project/hydroflow/issues/984), [#986](https://github.com/hydro-project/hydroflow/issues/986), [#996](https://github.com/hydro-project/hydroflow/issues/996) @@ -943,6 +1037,7 @@ Also fixes #1293 - 46 commits contributed to the release. + - 56 days passed between releases. - 43 commits were understood as [conventional](https://www.conventionalcommits.org). - 19 unique issues were worked on: [#882](https://github.com/hydro-project/hydroflow/issues/882), [#884](https://github.com/hydro-project/hydroflow/issues/884), [#885](https://github.com/hydro-project/hydroflow/issues/885), [#886](https://github.com/hydro-project/hydroflow/issues/886), [#887](https://github.com/hydro-project/hydroflow/issues/887), [#892](https://github.com/hydro-project/hydroflow/issues/892), [#893](https://github.com/hydro-project/hydroflow/issues/893), [#896](https://github.com/hydro-project/hydroflow/issues/896), [#897](https://github.com/hydro-project/hydroflow/issues/897), [#898](https://github.com/hydro-project/hydroflow/issues/898), [#902](https://github.com/hydro-project/hydroflow/issues/902), [#906](https://github.com/hydro-project/hydroflow/issues/906), [#918](https://github.com/hydro-project/hydroflow/issues/918), [#919](https://github.com/hydro-project/hydroflow/issues/919), [#923](https://github.com/hydro-project/hydroflow/issues/923), [#924](https://github.com/hydro-project/hydroflow/issues/924), [#926](https://github.com/hydro-project/hydroflow/issues/926), [#932](https://github.com/hydro-project/hydroflow/issues/932), [#935](https://github.com/hydro-project/hydroflow/issues/935) @@ -1098,6 +1193,7 @@ Also fixes #1293 - 25 commits contributed to the release. + - 42 days passed between releases. - 22 commits were understood as [conventional](https://www.conventionalcommits.org). - 23 unique issues were worked on: [#820](https://github.com/hydro-project/hydroflow/issues/820), [#821](https://github.com/hydro-project/hydroflow/issues/821), [#822](https://github.com/hydro-project/hydroflow/issues/822), [#823](https://github.com/hydro-project/hydroflow/issues/823), [#833](https://github.com/hydro-project/hydroflow/issues/833), [#835](https://github.com/hydro-project/hydroflow/issues/835), [#837](https://github.com/hydro-project/hydroflow/issues/837), [#840](https://github.com/hydro-project/hydroflow/issues/840), [#842](https://github.com/hydro-project/hydroflow/issues/842), [#843](https://github.com/hydro-project/hydroflow/issues/843), [#844](https://github.com/hydro-project/hydroflow/issues/844), [#845](https://github.com/hydro-project/hydroflow/issues/845), [#846](https://github.com/hydro-project/hydroflow/issues/846), [#848](https://github.com/hydro-project/hydroflow/issues/848), [#851](https://github.com/hydro-project/hydroflow/issues/851), [#853](https://github.com/hydro-project/hydroflow/issues/853), [#857](https://github.com/hydro-project/hydroflow/issues/857), [#861](https://github.com/hydro-project/hydroflow/issues/861), [#870](https://github.com/hydro-project/hydroflow/issues/870), [#872](https://github.com/hydro-project/hydroflow/issues/872), [#874](https://github.com/hydro-project/hydroflow/issues/874), [#878](https://github.com/hydro-project/hydroflow/issues/878), [#880](https://github.com/hydro-project/hydroflow/issues/880) @@ -1257,6 +1353,7 @@ Also fixes #1293 - 34 commits contributed to the release. + - 33 days passed between releases. - 31 commits were understood as [conventional](https://www.conventionalcommits.org). - 25 unique issues were worked on: [#739](https://github.com/hydro-project/hydroflow/issues/739), [#743](https://github.com/hydro-project/hydroflow/issues/743), [#745](https://github.com/hydro-project/hydroflow/issues/745), [#748](https://github.com/hydro-project/hydroflow/issues/748), [#749](https://github.com/hydro-project/hydroflow/issues/749), [#755](https://github.com/hydro-project/hydroflow/issues/755), [#761](https://github.com/hydro-project/hydroflow/issues/761), [#763](https://github.com/hydro-project/hydroflow/issues/763), [#765](https://github.com/hydro-project/hydroflow/issues/765), [#772](https://github.com/hydro-project/hydroflow/issues/772), [#773](https://github.com/hydro-project/hydroflow/issues/773), [#774](https://github.com/hydro-project/hydroflow/issues/774), [#775](https://github.com/hydro-project/hydroflow/issues/775), [#778](https://github.com/hydro-project/hydroflow/issues/778), [#780](https://github.com/hydro-project/hydroflow/issues/780), [#784](https://github.com/hydro-project/hydroflow/issues/784), [#788](https://github.com/hydro-project/hydroflow/issues/788), [#789](https://github.com/hydro-project/hydroflow/issues/789), [#791](https://github.com/hydro-project/hydroflow/issues/791), [#792](https://github.com/hydro-project/hydroflow/issues/792), [#799](https://github.com/hydro-project/hydroflow/issues/799), [#801](https://github.com/hydro-project/hydroflow/issues/801), [#803](https://github.com/hydro-project/hydroflow/issues/803), [#804](https://github.com/hydro-project/hydroflow/issues/804), [#809](https://github.com/hydro-project/hydroflow/issues/809) @@ -1350,6 +1447,7 @@ Also fixes #1293 - 4 commits contributed to the release. + - 1 day passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -1426,6 +1524,7 @@ Also fixes #1293 - 18 commits contributed to the release. + - 6 days passed between releases. - 17 commits were understood as [conventional](https://www.conventionalcommits.org). - 12 unique issues were worked on: [#686](https://github.com/hydro-project/hydroflow/issues/686), [#690](https://github.com/hydro-project/hydroflow/issues/690), [#692](https://github.com/hydro-project/hydroflow/issues/692), [#696](https://github.com/hydro-project/hydroflow/issues/696), [#697](https://github.com/hydro-project/hydroflow/issues/697), [#702](https://github.com/hydro-project/hydroflow/issues/702), [#706](https://github.com/hydro-project/hydroflow/issues/706), [#708](https://github.com/hydro-project/hydroflow/issues/708), [#714](https://github.com/hydro-project/hydroflow/issues/714), [#716](https://github.com/hydro-project/hydroflow/issues/716), [#719](https://github.com/hydro-project/hydroflow/issues/719), [#721](https://github.com/hydro-project/hydroflow/issues/721) @@ -1494,6 +1593,7 @@ Also fixes #1293 - 8 commits contributed to the release. + - 2 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#661](https://github.com/hydro-project/hydroflow/issues/661), [#671](https://github.com/hydro-project/hydroflow/issues/671), [#677](https://github.com/hydro-project/hydroflow/issues/677), [#684](https://github.com/hydro-project/hydroflow/issues/684) @@ -1542,6 +1642,7 @@ Also fixes #1293 - 19 commits contributed to the release. + - 18 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 14 unique issues were worked on: [#625](https://github.com/hydro-project/hydroflow/issues/625), [#638](https://github.com/hydro-project/hydroflow/issues/638), [#640](https://github.com/hydro-project/hydroflow/issues/640), [#641](https://github.com/hydro-project/hydroflow/issues/641), [#642](https://github.com/hydro-project/hydroflow/issues/642), [#644](https://github.com/hydro-project/hydroflow/issues/644), [#649](https://github.com/hydro-project/hydroflow/issues/649), [#650](https://github.com/hydro-project/hydroflow/issues/650), [#651](https://github.com/hydro-project/hydroflow/issues/651), [#654](https://github.com/hydro-project/hydroflow/issues/654), [#656](https://github.com/hydro-project/hydroflow/issues/656), [#657](https://github.com/hydro-project/hydroflow/issues/657), [#660](https://github.com/hydro-project/hydroflow/issues/660), [#667](https://github.com/hydro-project/hydroflow/issues/667) @@ -1607,6 +1708,7 @@ Also fixes #1293 - 7 commits contributed to the release. + - 7 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 6 unique issues were worked on: [#622](https://github.com/hydro-project/hydroflow/issues/622), [#629](https://github.com/hydro-project/hydroflow/issues/629), [#632](https://github.com/hydro-project/hydroflow/issues/632), [#633](https://github.com/hydro-project/hydroflow/issues/633), [#634](https://github.com/hydro-project/hydroflow/issues/634), [#635](https://github.com/hydro-project/hydroflow/issues/635) diff --git a/hydroflow/Cargo.toml b/hydroflow/Cargo.toml index 3d4400e9deb3..96d2616a05ab 100644 --- a/hydroflow/Cargo.toml +++ b/hydroflow/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydroflow" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydroflow/" @@ -42,13 +42,13 @@ bincode = "1.3.1" byteorder = "1.3.2" bytes = "1.1.0" futures = "0.3.0" -hydroflow_deploy_integration = { optional = true, path = "../hydro_deploy/hydroflow_deploy_integration", version = "^0.9.0" } -hydroflow_datalog = { optional = true, path = "../hydroflow_datalog", version = "^0.9.0" } -hydroflow_lang = { path = "../hydroflow_lang", version = "^0.9.0", features = [ "clap-derive" ] } -hydroflow_macro = { optional = true, path = "../hydroflow_macro", version = "^0.9.0" } +hydroflow_deploy_integration = { optional = true, path = "../hydro_deploy/hydroflow_deploy_integration", version = "^0.10.0" } +hydroflow_datalog = { optional = true, path = "../hydroflow_datalog", version = "^0.10.0" } +hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0", features = [ "clap-derive" ] } +hydroflow_macro = { optional = true, path = "../hydroflow_macro", version = "^0.10.0" } itertools = "0.10.0" -lattices = { path = "../lattices", version = "^0.5.7", features = [ "serde" ] } -pusherator = { path = "../pusherator", version = "^0.0.8" } +lattices = { path = "../lattices", version = "^0.5.8", features = [ "serde" ] } +pusherator = { path = "../pusherator", version = "^0.0.9" } pyo3 = { optional = true, version = "0.20" } ref-cast = "1.0.0" regex = "1.10.4" @@ -60,11 +60,11 @@ slotmap = "1.0.0" smallvec = "1.6.1" tokio-stream = { version = "0.1.3", default-features = false, features = [ "time", "io-util", "sync" ] } tracing = "0.1.37" -variadics = { path = "../variadics", version = "^0.0.6" } +variadics = { path = "../variadics", version = "^0.0.7" } web-time = "1.0.0" # added to workaround `cargo smart-release` https://github.com/Byron/cargo-smart-release/issues/16 -multiplatform_test = { path = "../multiplatform_test", version = "^0.2.0", optional = true } +multiplatform_test = { path = "../multiplatform_test", version = "^0.3.0", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { version = "1.29.0", features = [ "full" ] } @@ -85,7 +85,7 @@ chrono = { version = "0.4.20", features = [ "serde", "clock" ], default-features clap = { version = "4.5.4", features = [ "derive" ] } colored = "2.0" insta = "1.39" -multiplatform_test = { path = "../multiplatform_test", version = "^0.2.0" } +multiplatform_test = { path = "../multiplatform_test", version = "^0.3.0" } wasm-bindgen-test = "0.3.33" rand = { version = "0.8.0", features = [ "small_rng" ] } rand_distr = "0.4.3" diff --git a/hydroflow_datalog/CHANGELOG.md b/hydroflow_datalog/CHANGELOG.md index 2e9897912924..c8dd77928a2f 100644 --- a/hydroflow_datalog/CHANGELOG.md +++ b/hydroflow_datalog/CHANGELOG.md @@ -5,8 +5,35 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 69 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) +
+ ## 0.9.0 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -18,7 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 1 commit contributed to the release. + - 2 commits contributed to the release. + - 38 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -30,6 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## 0.8.0 (2024-07-23) @@ -47,6 +77,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 59 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -76,6 +107,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 83 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -105,6 +137,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 32 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -135,6 +168,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 110 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -162,6 +196,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 56 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -191,6 +226,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 42 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -223,6 +259,7 @@ Unchanged from previous release. - 3 commits contributed to the release. + - 33 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#780](https://github.com/hydro-project/hydroflow/issues/780), [#801](https://github.com/hydro-project/hydroflow/issues/801) @@ -253,6 +290,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 7 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -285,6 +323,7 @@ Unchanged from previous release. - 3 commits contributed to the release. + - 2 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#677](https://github.com/hydro-project/hydroflow/issues/677), [#684](https://github.com/hydro-project/hydroflow/issues/684) @@ -315,6 +354,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 25 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#660](https://github.com/hydro-project/hydroflow/issues/660) diff --git a/hydroflow_datalog/Cargo.toml b/hydroflow_datalog/Cargo.toml index 93633dfe7d1f..58a62cab9d3b 100644 --- a/hydroflow_datalog/Cargo.toml +++ b/hydroflow_datalog/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydroflow_datalog" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydroflow_datalog/" @@ -25,4 +25,4 @@ proc-macro-crate = "1.0.0" # Note: If we ever compile this proc macro crate to WASM (e.g., if we are # building on a WASM host), we may need to turn diagnostics off for WASM if # proc_macro2 does not support WASM at that time. -hydroflow_datalog_core = { path = "../hydroflow_datalog_core", version = "^0.9.0" } +hydroflow_datalog_core = { path = "../hydroflow_datalog_core", version = "^0.10.0" } diff --git a/hydroflow_datalog_core/CHANGELOG.md b/hydroflow_datalog_core/CHANGELOG.md index 09fb9147413e..64d92a4d7c41 100644 --- a/hydroflow_datalog_core/CHANGELOG.md +++ b/hydroflow_datalog_core/CHANGELOG.md @@ -5,8 +5,48 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2024-11-08) + +### Chore + + - update rust-sitter + The latest Rust Sitter drops the dependency on `tree-sitter-cli`, which + eliminates many transitive dependencies. + - update pinned rust version, clippy lints, remove some dead code + +### Style + + - fixes for latest nightly clippy + +### Commit Statistics + + + + - 3 commits contributed to the release. + - 69 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 3 unique issues were worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1495](https://github.com/hydro-project/hydroflow/issues/1495), [#1537](https://github.com/hydro-project/hydroflow/issues/1537) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1495](https://github.com/hydro-project/hydroflow/issues/1495)** + - Update rust-sitter ([`4f6d400`](https://github.com/hydro-project/hydroflow/commit/4f6d400d0992594f8f12992fb6939378b12fadd6)) + * **[#1537](https://github.com/hydro-project/hydroflow/issues/1537)** + - Fixes for latest nightly clippy ([`8442d1b`](https://github.com/hydro-project/hydroflow/commit/8442d1b524621a9f8b43372a9c25991efb33c25e)) +
+ ## 0.9.0 (2024-08-30) + + + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -27,7 +67,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 4 commits contributed to the release. + - 5 commits contributed to the release. + - 38 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#1417](https://github.com/hydro-project/hydroflow/issues/1417), [#1420](https://github.com/hydro-project/hydroflow/issues/1420), [#1423](https://github.com/hydro-project/hydroflow/issues/1423), [#1432](https://github.com/hydro-project/hydroflow/issues/1432) @@ -45,6 +86,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) * **[#1432](https://github.com/hydro-project/hydroflow/issues/1432)** - Cleanup handling of span locations #1268, workaround fix #729 ([`9c352f5`](https://github.com/hydro-project/hydroflow/commit/9c352f50a5a8d1b2187b34d5847a23b7397fe6ec)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## 0.8.0 (2024-07-23) @@ -118,6 +161,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 5 commits contributed to the release. + - 59 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#1250](https://github.com/hydro-project/hydroflow/issues/1250), [#1295](https://github.com/hydro-project/hydroflow/issues/1295), [#1300](https://github.com/hydro-project/hydroflow/issues/1300), [#1332](https://github.com/hydro-project/hydroflow/issues/1332) @@ -139,15 +183,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Release hydroflow_lang v0.8.0, hydroflow_datalog_core v0.8.0, hydroflow_datalog v0.8.0, hydroflow_macro v0.8.0, lattices_macro v0.5.5, lattices v0.5.6, variadics v0.0.5, pusherator v0.0.7, hydroflow v0.8.0, hydroflow_plus v0.8.0, hydro_deploy v0.8.0, hydro_cli v0.8.0, hydroflow_plus_cli_integration v0.8.0, safety bump 7 crates ([`ca6c16b`](https://github.com/hydro-project/hydroflow/commit/ca6c16b4a7ce35e155fe7fc6c7d1676c37c9e4de)) - -Other 'tick state will need to be cleared, but existing implementation does that when the iterator runs, which is good enough. There is only a problem if a singleton can reference the state before the iterator runs, in that case.To emulate this unintuitive behavior, we currently ensure that apersist::<'static>() exists before operator that references thesingleton (filter, in this case). (Note that this is equivalent tocross_join::<'static>() and not cross_join::<'tick>())However singletons also have had a different mechanism that affectsthis- currently singleton references create a next-stratum constraint,that ensures a singleton referencer must be in a later stratum than thesingleton it is referencing.Note that this actually prevents the example situation above fromhappening– the updates to y will be received all at once at the startof the next stratum.This means that actually, currently singletons are equivalent tosomething like:ruststream -> cj[0]; -y -> next_stratum() -> last() -> cj[1]; -cj = cross_join() -> filter(|(item, y)| ...) -> ... -last() is a hypothetical operator that only keeps the most recent itemoutput by y. next_stratum() -> last() is equivalent to reduce(|acc, item| *acc = item) (since that comes with a stratum barrier). Sotechnically this is a slightly different behavior than just cross_join,but it is more intuitive.ruststream -> cj[0]; -y -> reduce(|acc, item| { *acc = item; }) -> cj[1]; -cj = cross_join() -> filter(|(item, y)| ...) -> ... -Also fixes #1293 - ## 0.7.0 (2024-05-24) @@ -179,6 +214,7 @@ Also fixes #1293 - 6 commits contributed to the release. + - 48 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 5 unique issues were worked on: [#1160](https://github.com/hydro-project/hydroflow/issues/1160), [#1166](https://github.com/hydro-project/hydroflow/issues/1166), [#1168](https://github.com/hydro-project/hydroflow/issues/1168), [#1176](https://github.com/hydro-project/hydroflow/issues/1176), [#1192](https://github.com/hydro-project/hydroflow/issues/1192) @@ -241,6 +277,7 @@ Also fixes #1293 - 9 commits contributed to the release. + - 34 days passed between releases. - 8 commits were understood as [conventional](https://www.conventionalcommits.org). - 7 unique issues were worked on: [#1086](https://github.com/hydro-project/hydroflow/issues/1086), [#1091](https://github.com/hydro-project/hydroflow/issues/1091), [#1094](https://github.com/hydro-project/hydroflow/issues/1094), [#1132](https://github.com/hydro-project/hydroflow/issues/1132), [#1135](https://github.com/hydro-project/hydroflow/issues/1135), [#1136](https://github.com/hydro-project/hydroflow/issues/1136), [#1137](https://github.com/hydro-project/hydroflow/issues/1137) @@ -282,6 +319,7 @@ Also fixes #1293 - 2 commits contributed to the release. + - 28 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -315,6 +353,7 @@ Also fixes #1293 - 3 commits contributed to the release. + - 4 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1041](https://github.com/hydro-project/hydroflow/issues/1041) @@ -364,6 +403,7 @@ Also fixes #1293 - 7 commits contributed to the release. + - 110 days passed between releases. - 6 commits were understood as [conventional](https://www.conventionalcommits.org). - 5 unique issues were worked on: [#1016](https://github.com/hydro-project/hydroflow/issues/1016), [#1023](https://github.com/hydro-project/hydroflow/issues/1023), [#1033](https://github.com/hydro-project/hydroflow/issues/1033), [#945](https://github.com/hydro-project/hydroflow/issues/945), [#989](https://github.com/hydro-project/hydroflow/issues/989) @@ -420,6 +460,7 @@ Also fixes #1293 - 11 commits contributed to the release. + - 56 days passed between releases. - 10 commits were understood as [conventional](https://www.conventionalcommits.org). - 7 unique issues were worked on: [#882](https://github.com/hydro-project/hydroflow/issues/882), [#893](https://github.com/hydro-project/hydroflow/issues/893), [#896](https://github.com/hydro-project/hydroflow/issues/896), [#898](https://github.com/hydro-project/hydroflow/issues/898), [#906](https://github.com/hydro-project/hydroflow/issues/906), [#924](https://github.com/hydro-project/hydroflow/issues/924), [#926](https://github.com/hydro-project/hydroflow/issues/926) @@ -487,6 +528,7 @@ Also fixes #1293 - 6 commits contributed to the release. + - 42 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#833](https://github.com/hydro-project/hydroflow/issues/833), [#845](https://github.com/hydro-project/hydroflow/issues/845), [#870](https://github.com/hydro-project/hydroflow/issues/870), [#872](https://github.com/hydro-project/hydroflow/issues/872) @@ -527,6 +569,7 @@ Also fixes #1293 - 3 commits contributed to the release. + - 33 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#780](https://github.com/hydro-project/hydroflow/issues/780), [#801](https://github.com/hydro-project/hydroflow/issues/801) @@ -557,6 +600,7 @@ Also fixes #1293 - 2 commits contributed to the release. + - 1 day passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -602,6 +646,7 @@ Also fixes #1293 - 5 commits contributed to the release. + - 6 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#697](https://github.com/hydro-project/hydroflow/issues/697), [#702](https://github.com/hydro-project/hydroflow/issues/702), [#714](https://github.com/hydro-project/hydroflow/issues/714), [#716](https://github.com/hydro-project/hydroflow/issues/716) @@ -642,6 +687,7 @@ Also fixes #1293 - 4 commits contributed to the release. + - 2 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#673](https://github.com/hydro-project/hydroflow/issues/673), [#677](https://github.com/hydro-project/hydroflow/issues/677), [#684](https://github.com/hydro-project/hydroflow/issues/684) @@ -678,6 +724,7 @@ Also fixes #1293 - 6 commits contributed to the release. + - 25 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#639](https://github.com/hydro-project/hydroflow/issues/639), [#642](https://github.com/hydro-project/hydroflow/issues/642), [#660](https://github.com/hydro-project/hydroflow/issues/660) diff --git a/hydroflow_datalog_core/Cargo.toml b/hydroflow_datalog_core/Cargo.toml index f669063d3107..79b541b1bf30 100644 --- a/hydroflow_datalog_core/Cargo.toml +++ b/hydroflow_datalog_core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydroflow_datalog_core" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydroflow_datalog_core/" @@ -23,7 +23,7 @@ slotmap = "1.0.0" syn = { version = "2.0.46", features = [ "parsing", "extra-traits" ] } proc-macro2 = "1.0.74" rust-sitter = "0.4.3" -hydroflow_lang = { path = "../hydroflow_lang", version = "^0.9.0" } +hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0" } [build-dependencies] rust-sitter-tool = "0.4.3" diff --git a/hydroflow_lang/CHANGELOG.md b/hydroflow_lang/CHANGELOG.md index c6d3b14035a3..5bf10ccccd08 100644 --- a/hydroflow_lang/CHANGELOG.md +++ b/hydroflow_lang/CHANGELOG.md @@ -5,8 +5,91 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - generalized hash trie indexes for relational tuples + Generalized Hash Tries are part of the SIGMOD '23 FreeJoin + [paper](https://dl.acm.org/doi/abs/10.1145/3589295) by + Wang/Willsey/Suciu. They provide a compressed ("factorized") + representation of relations. By operating in the factorized domain, join + algorithms can defer cross-products and achieve asymptotically optimal + performance. + + --------- + - Added state_by operator. + For https://github.com/hydro-project/hydroflow/issues/1467 + +### Bug Fixes + + - `cross_singleton()` forgot value if multiple runs in a tick, fix #1518 + Adds the minimal reproducer test from @shadaj + + Note this may have negative performance implications, as the singleton value now is stored in the state API (heap) instead of locally. If we use singleton syntax this duplicate allocation could probably be avoided. + + > Confirmed that this fixed the bugs in our Paxos implementation, no noticeable performance impact. @shadj + +### Refactor + + - update topo-sort to detect cycles + +### Style + + - fixes for nightly clippy + a couple few spurious `too_many_arguments` and a spurious + `zombie_processes` still on current nightly (`clippy 0.1.84 (4392847410 + 2024-10-21)`) + +### Bug Fixes (BREAKING) + + - fix #1401 `lattice_bimorphism()` double-emit, add docs + Fixes the issue by combining the all values generated per subgraph + execution into one, which effectively de-duplicates the values. + + Adds basic docs. + +### Commit Statistics + + + + - 7 commits contributed to the release. + - 69 days passed between releases. + - 7 commits were understood as [conventional](https://www.conventionalcommits.org). + - 7 unique issues were worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1469](https://github.com/hydro-project/hydroflow/issues/1469), [#1503](https://github.com/hydro-project/hydroflow/issues/1503), [#1505](https://github.com/hydro-project/hydroflow/issues/1505), [#1512](https://github.com/hydro-project/hydroflow/issues/1512), [#1520](https://github.com/hydro-project/hydroflow/issues/1520), [#1522](https://github.com/hydro-project/hydroflow/issues/1522) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1469](https://github.com/hydro-project/hydroflow/issues/1469)** + - Added state_by operator. ([`d83cb83`](https://github.com/hydro-project/hydroflow/commit/d83cb83df59e647ba99bd896e7605ee18b9a84f6)) + * **[#1503](https://github.com/hydro-project/hydroflow/issues/1503)** + - Generalized hash trie indexes for relational tuples ([`f7e740f`](https://github.com/hydro-project/hydroflow/commit/f7e740fb2ba36d0fcf3fd196d60333552911e3a4)) + * **[#1505](https://github.com/hydro-project/hydroflow/issues/1505)** + - Fixes for nightly clippy ([`47cb703`](https://github.com/hydro-project/hydroflow/commit/47cb703e771f7d1c451ceb9d185ada96410949da)) + * **[#1512](https://github.com/hydro-project/hydroflow/issues/1512)** + - Update topo-sort to detect cycles ([`32e2970`](https://github.com/hydro-project/hydroflow/commit/32e297094fed9908ca7bf77e7068bc0a6ea52eae)) + * **[#1520](https://github.com/hydro-project/hydroflow/issues/1520)** + - `cross_singleton()` forgot value if multiple runs in a tick, fix #1518 ([`16b730c`](https://github.com/hydro-project/hydroflow/commit/16b730c75cfca79ea5f869308b1e1e14b3e9c155)) + * **[#1522](https://github.com/hydro-project/hydroflow/issues/1522)** + - Fix #1401 `lattice_bimorphism()` double-emit, add docs ([`e796200`](https://github.com/hydro-project/hydroflow/commit/e796200743f2cc2da5a0e91c492f016ca98008e8)) +
+ ## 0.9.0 (2024-08-30) + + + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -46,7 +129,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 10 commits contributed to the release. + - 11 commits contributed to the release. + - 38 days passed between releases. - 10 commits were understood as [conventional](https://www.conventionalcommits.org). - 10 unique issues were worked on: [#1362](https://github.com/hydro-project/hydroflow/issues/1362), [#1373](https://github.com/hydro-project/hydroflow/issues/1373), [#1407](https://github.com/hydro-project/hydroflow/issues/1407), [#1409](https://github.com/hydro-project/hydroflow/issues/1409), [#1412](https://github.com/hydro-project/hydroflow/issues/1412), [#1417](https://github.com/hydro-project/hydroflow/issues/1417), [#1420](https://github.com/hydro-project/hydroflow/issues/1420), [#1423](https://github.com/hydro-project/hydroflow/issues/1423), [#1428](https://github.com/hydro-project/hydroflow/issues/1428), [#1432](https://github.com/hydro-project/hydroflow/issues/1432) @@ -76,6 +160,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Cleanup doc comments for clippy latest ([`f5f1eb0`](https://github.com/hydro-project/hydroflow/commit/f5f1eb0c612f5c0c1752360d972ef6853c5e12f0)) * **[#1432](https://github.com/hydro-project/hydroflow/issues/1432)** - Cleanup handling of span locations #1268, workaround fix #729 ([`9c352f5`](https://github.com/hydro-project/hydroflow/commit/9c352f50a5a8d1b2187b34d5847a23b7397fe6ec)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## 0.8.0 (2024-07-23) @@ -177,6 +263,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 14 commits contributed to the release. + - 59 days passed between releases. - 13 commits were understood as [conventional](https://www.conventionalcommits.org). - 13 unique issues were worked on: [#1143](https://github.com/hydro-project/hydroflow/issues/1143), [#1216](https://github.com/hydro-project/hydroflow/issues/1216), [#1260](https://github.com/hydro-project/hydroflow/issues/1260), [#1266](https://github.com/hydro-project/hydroflow/issues/1266), [#1271](https://github.com/hydro-project/hydroflow/issues/1271), [#1280](https://github.com/hydro-project/hydroflow/issues/1280), [#1285](https://github.com/hydro-project/hydroflow/issues/1285), [#1295](https://github.com/hydro-project/hydroflow/issues/1295), [#1296](https://github.com/hydro-project/hydroflow/issues/1296), [#1297](https://github.com/hydro-project/hydroflow/issues/1297), [#1300](https://github.com/hydro-project/hydroflow/issues/1300), [#1312](https://github.com/hydro-project/hydroflow/issues/1312), [#1332](https://github.com/hydro-project/hydroflow/issues/1332) @@ -217,15 +304,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix unnecessary `&` for clippy ([`b7275c2`](https://github.com/hydro-project/hydroflow/commit/b7275c2a770283cf8012a107ec422931e1f7338a)) - -Other 'tick state will need to be cleared, but existing implementation does that when the iterator runs, which is good enough. There is only a problem if a singleton can reference the state before the iterator runs, in that case. allow use of generics in demux_enum::<...>() opensures turbofish syntax for the match clauses, which is needed when there are generic parameters in the typeadds a test as wellTo emulate this unintuitive behavior, we currently ensure that apersist::<'static>() exists before operator that references thesingleton (filter, in this case). (Note that this is equivalent tocross_join::<'static>() and not cross_join::<'tick>())However singletons also have had a different mechanism that affectsthis- currently singleton references create a next-stratum constraint,that ensures a singleton referencer must be in a later stratum than thesingleton it is referencing.Note that this actually prevents the example situation above fromhappening– the updates to y will be received all at once at the startof the next stratum.This means that actually, currently singletons are equivalent tosomething like:ruststream -> cj[0]; -y -> next_stratum() -> last() -> cj[1]; -cj = cross_join() -> filter(|(item, y)| ...) -> ... -last() is a hypothetical operator that only keeps the most recent itemoutput by y. next_stratum() -> last() is equivalent to reduce(|acc, item| *acc = item) (since that comes with a stratum barrier). Sotechnically this is a slightly different behavior than just cross_join,but it is more intuitive.ruststream -> cj[0]; -y -> reduce(|acc, item| { *acc = item; }) -> cj[1]; -cj = cross_join() -> filter(|(item, y)| ...) -> ... -Also fixes #1293 - ## 0.7.0 (2024-05-24) @@ -293,6 +371,7 @@ Also fixes #1293 - 23 commits contributed to the release. + - 44 days passed between releases. - 21 commits were understood as [conventional](https://www.conventionalcommits.org). - 15 unique issues were worked on: [#1143](https://github.com/hydro-project/hydroflow/issues/1143), [#1152](https://github.com/hydro-project/hydroflow/issues/1152), [#1157](https://github.com/hydro-project/hydroflow/issues/1157), [#1159](https://github.com/hydro-project/hydroflow/issues/1159), [#1160](https://github.com/hydro-project/hydroflow/issues/1160), [#1167](https://github.com/hydro-project/hydroflow/issues/1167), [#1171](https://github.com/hydro-project/hydroflow/issues/1171), [#1176](https://github.com/hydro-project/hydroflow/issues/1176), [#1182](https://github.com/hydro-project/hydroflow/issues/1182), [#1190](https://github.com/hydro-project/hydroflow/issues/1190), [#1192](https://github.com/hydro-project/hydroflow/issues/1192), [#1193](https://github.com/hydro-project/hydroflow/issues/1193), [#1198](https://github.com/hydro-project/hydroflow/issues/1198), [#1204](https://github.com/hydro-project/hydroflow/issues/1204), [#1232](https://github.com/hydro-project/hydroflow/issues/1232) @@ -364,6 +443,7 @@ Also fixes #1293 - 6 commits contributed to the release. + - 3 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1134](https://github.com/hydro-project/hydroflow/issues/1134), [#1148](https://github.com/hydro-project/hydroflow/issues/1148), [#1150](https://github.com/hydro-project/hydroflow/issues/1150) @@ -432,6 +512,7 @@ Also fixes #1293 - 18 commits contributed to the release. + - 34 days passed between releases. - 17 commits were understood as [conventional](https://www.conventionalcommits.org). - 13 unique issues were worked on: [#1086](https://github.com/hydro-project/hydroflow/issues/1086), [#1087](https://github.com/hydro-project/hydroflow/issues/1087), [#1089](https://github.com/hydro-project/hydroflow/issues/1089), [#1090](https://github.com/hydro-project/hydroflow/issues/1090), [#1091](https://github.com/hydro-project/hydroflow/issues/1091), [#1109](https://github.com/hydro-project/hydroflow/issues/1109), [#1128](https://github.com/hydro-project/hydroflow/issues/1128), [#1130](https://github.com/hydro-project/hydroflow/issues/1130), [#1133](https://github.com/hydro-project/hydroflow/issues/1133), [#1136](https://github.com/hydro-project/hydroflow/issues/1136), [#1137](https://github.com/hydro-project/hydroflow/issues/1137), [#1145](https://github.com/hydro-project/hydroflow/issues/1145), [#1146](https://github.com/hydro-project/hydroflow/issues/1146) @@ -488,6 +569,7 @@ Also fixes #1293 - 3 commits contributed to the release. + - 28 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1061](https://github.com/hydro-project/hydroflow/issues/1061), [#1070](https://github.com/hydro-project/hydroflow/issues/1070) @@ -537,6 +619,7 @@ Also fixes #1293 - 9 commits contributed to the release. + - 4 days passed between releases. - 8 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1041](https://github.com/hydro-project/hydroflow/issues/1041), [#1053](https://github.com/hydro-project/hydroflow/issues/1053), [#1055](https://github.com/hydro-project/hydroflow/issues/1055) @@ -631,6 +714,7 @@ Also fixes #1293 - 28 commits contributed to the release. + - 110 days passed between releases. - 26 commits were understood as [conventional](https://www.conventionalcommits.org). - 20 unique issues were worked on: [#1005](https://github.com/hydro-project/hydroflow/issues/1005), [#1009](https://github.com/hydro-project/hydroflow/issues/1009), [#1016](https://github.com/hydro-project/hydroflow/issues/1016), [#1017](https://github.com/hydro-project/hydroflow/issues/1017), [#1021](https://github.com/hydro-project/hydroflow/issues/1021), [#1023](https://github.com/hydro-project/hydroflow/issues/1023), [#1026](https://github.com/hydro-project/hydroflow/issues/1026), [#1033](https://github.com/hydro-project/hydroflow/issues/1033), [#1036](https://github.com/hydro-project/hydroflow/issues/1036), [#1040](https://github.com/hydro-project/hydroflow/issues/1040), [#899](https://github.com/hydro-project/hydroflow/issues/899), [#945](https://github.com/hydro-project/hydroflow/issues/945), [#947](https://github.com/hydro-project/hydroflow/issues/947), [#948](https://github.com/hydro-project/hydroflow/issues/948), [#949](https://github.com/hydro-project/hydroflow/issues/949), [#950](https://github.com/hydro-project/hydroflow/issues/950), [#959](https://github.com/hydro-project/hydroflow/issues/959), [#960](https://github.com/hydro-project/hydroflow/issues/960), [#978](https://github.com/hydro-project/hydroflow/issues/978), [#989](https://github.com/hydro-project/hydroflow/issues/989) @@ -779,6 +863,7 @@ Also fixes #1293 - 42 commits contributed to the release. + - 56 days passed between releases. - 41 commits were understood as [conventional](https://www.conventionalcommits.org). - 14 unique issues were worked on: [#882](https://github.com/hydro-project/hydroflow/issues/882), [#883](https://github.com/hydro-project/hydroflow/issues/883), [#884](https://github.com/hydro-project/hydroflow/issues/884), [#892](https://github.com/hydro-project/hydroflow/issues/892), [#896](https://github.com/hydro-project/hydroflow/issues/896), [#898](https://github.com/hydro-project/hydroflow/issues/898), [#902](https://github.com/hydro-project/hydroflow/issues/902), [#906](https://github.com/hydro-project/hydroflow/issues/906), [#923](https://github.com/hydro-project/hydroflow/issues/923), [#924](https://github.com/hydro-project/hydroflow/issues/924), [#926](https://github.com/hydro-project/hydroflow/issues/926), [#932](https://github.com/hydro-project/hydroflow/issues/932), [#933](https://github.com/hydro-project/hydroflow/issues/933), [#935](https://github.com/hydro-project/hydroflow/issues/935) @@ -904,6 +989,7 @@ Also fixes #1293 - 17 commits contributed to the release. + - 42 days passed between releases. - 14 commits were understood as [conventional](https://www.conventionalcommits.org). - 16 unique issues were worked on: [#820](https://github.com/hydro-project/hydroflow/issues/820), [#821](https://github.com/hydro-project/hydroflow/issues/821), [#822](https://github.com/hydro-project/hydroflow/issues/822), [#823](https://github.com/hydro-project/hydroflow/issues/823), [#833](https://github.com/hydro-project/hydroflow/issues/833), [#835](https://github.com/hydro-project/hydroflow/issues/835), [#840](https://github.com/hydro-project/hydroflow/issues/840), [#843](https://github.com/hydro-project/hydroflow/issues/843), [#844](https://github.com/hydro-project/hydroflow/issues/844), [#845](https://github.com/hydro-project/hydroflow/issues/845), [#851](https://github.com/hydro-project/hydroflow/issues/851), [#853](https://github.com/hydro-project/hydroflow/issues/853), [#861](https://github.com/hydro-project/hydroflow/issues/861), [#870](https://github.com/hydro-project/hydroflow/issues/870), [#872](https://github.com/hydro-project/hydroflow/issues/872), [#873](https://github.com/hydro-project/hydroflow/issues/873) @@ -1014,6 +1100,7 @@ Also fixes #1293 - 17 commits contributed to the release. + - 33 days passed between releases. - 14 commits were understood as [conventional](https://www.conventionalcommits.org). - 15 unique issues were worked on: [#741](https://github.com/hydro-project/hydroflow/issues/741), [#765](https://github.com/hydro-project/hydroflow/issues/765), [#773](https://github.com/hydro-project/hydroflow/issues/773), [#774](https://github.com/hydro-project/hydroflow/issues/774), [#775](https://github.com/hydro-project/hydroflow/issues/775), [#778](https://github.com/hydro-project/hydroflow/issues/778), [#780](https://github.com/hydro-project/hydroflow/issues/780), [#784](https://github.com/hydro-project/hydroflow/issues/784), [#789](https://github.com/hydro-project/hydroflow/issues/789), [#792](https://github.com/hydro-project/hydroflow/issues/792), [#799](https://github.com/hydro-project/hydroflow/issues/799), [#801](https://github.com/hydro-project/hydroflow/issues/801), [#803](https://github.com/hydro-project/hydroflow/issues/803), [#804](https://github.com/hydro-project/hydroflow/issues/804), [#809](https://github.com/hydro-project/hydroflow/issues/809) @@ -1084,6 +1171,7 @@ Also fixes #1293 - 5 commits contributed to the release. + - 1 day passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#728](https://github.com/hydro-project/hydroflow/issues/728), [#730](https://github.com/hydro-project/hydroflow/issues/730) @@ -1153,6 +1241,7 @@ Also fixes #1293 - 13 commits contributed to the release. + - 6 days passed between releases. - 12 commits were understood as [conventional](https://www.conventionalcommits.org). - 8 unique issues were worked on: [#696](https://github.com/hydro-project/hydroflow/issues/696), [#697](https://github.com/hydro-project/hydroflow/issues/697), [#702](https://github.com/hydro-project/hydroflow/issues/702), [#704](https://github.com/hydro-project/hydroflow/issues/704), [#706](https://github.com/hydro-project/hydroflow/issues/706), [#714](https://github.com/hydro-project/hydroflow/issues/714), [#716](https://github.com/hydro-project/hydroflow/issues/716), [#719](https://github.com/hydro-project/hydroflow/issues/719) @@ -1205,6 +1294,7 @@ Also fixes #1293 - 6 commits contributed to the release. + - 2 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 5 unique issues were worked on: [#661](https://github.com/hydro-project/hydroflow/issues/661), [#673](https://github.com/hydro-project/hydroflow/issues/673), [#676](https://github.com/hydro-project/hydroflow/issues/676), [#677](https://github.com/hydro-project/hydroflow/issues/677), [#684](https://github.com/hydro-project/hydroflow/issues/684) @@ -1252,6 +1342,7 @@ Also fixes #1293 - 11 commits contributed to the release. + - 25 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 7 unique issues were worked on: [#638](https://github.com/hydro-project/hydroflow/issues/638), [#639](https://github.com/hydro-project/hydroflow/issues/639), [#642](https://github.com/hydro-project/hydroflow/issues/642), [#649](https://github.com/hydro-project/hydroflow/issues/649), [#654](https://github.com/hydro-project/hydroflow/issues/654), [#660](https://github.com/hydro-project/hydroflow/issues/660), [#667](https://github.com/hydro-project/hydroflow/issues/667) diff --git a/hydroflow_lang/Cargo.toml b/hydroflow_lang/Cargo.toml index 40fc75f5d0fc..b1321b5ebf72 100644 --- a/hydroflow_lang/Cargo.toml +++ b/hydroflow_lang/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydroflow_lang" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydroflow_lang/" diff --git a/hydroflow_macro/CHANGELOG.md b/hydroflow_macro/CHANGELOG.md index 8d47bde8ee0b..574b4e2cd328 100644 --- a/hydroflow_macro/CHANGELOG.md +++ b/hydroflow_macro/CHANGELOG.md @@ -5,8 +5,35 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 69 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) +
+ ## 0.9.0 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -22,7 +49,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 2 commits contributed to the release. + - 3 commits contributed to the release. + - 38 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1409](https://github.com/hydro-project/hydroflow/issues/1409), [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -36,6 +64,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow `demux_enum` to have any number of outputs, fix #1329 ([`9e5f58e`](https://github.com/hydro-project/hydroflow/commit/9e5f58ef773f0aee39a9705d9845361a2488649b)) * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## 0.8.0 (2024-07-23) @@ -53,6 +83,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 59 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -80,6 +111,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 83 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1204](https://github.com/hydro-project/hydroflow/issues/1204) @@ -110,6 +142,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 28 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -136,6 +169,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 4 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1041](https://github.com/hydro-project/hydroflow/issues/1041) @@ -176,12 +210,15 @@ Unchanged from previous release. -- - new implementation and Hydro Deploy setup -- + - new implementation and Hydro Deploy setup + -- ### Commit Statistics - 3 commits contributed to the release. + - 110 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#909](https://github.com/hydro-project/hydroflow/issues/909) @@ -235,6 +272,7 @@ Unchanged from previous release. - 11 commits contributed to the release. + - 56 days passed between releases. - 9 commits were understood as [conventional](https://www.conventionalcommits.org). - 6 unique issues were worked on: [#881](https://github.com/hydro-project/hydroflow/issues/881), [#882](https://github.com/hydro-project/hydroflow/issues/882), [#884](https://github.com/hydro-project/hydroflow/issues/884), [#898](https://github.com/hydro-project/hydroflow/issues/898), [#932](https://github.com/hydro-project/hydroflow/issues/932), [#938](https://github.com/hydro-project/hydroflow/issues/938) @@ -276,6 +314,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 42 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#845](https://github.com/hydro-project/hydroflow/issues/845) @@ -314,6 +353,7 @@ Unchanged from previous release. - 5 commits contributed to the release. + - 33 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#758](https://github.com/hydro-project/hydroflow/issues/758), [#780](https://github.com/hydro-project/hydroflow/issues/780), [#801](https://github.com/hydro-project/hydroflow/issues/801) @@ -360,6 +400,7 @@ Unchanged from previous release. - 5 commits contributed to the release. + - 1 day passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#728](https://github.com/hydro-project/hydroflow/issues/728), [#730](https://github.com/hydro-project/hydroflow/issues/730) @@ -391,6 +432,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 6 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#686](https://github.com/hydro-project/hydroflow/issues/686) @@ -424,6 +466,7 @@ Unchanged from previous release. - 5 commits contributed to the release. + - 2 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#661](https://github.com/hydro-project/hydroflow/issues/661), [#671](https://github.com/hydro-project/hydroflow/issues/671), [#677](https://github.com/hydro-project/hydroflow/issues/677), [#684](https://github.com/hydro-project/hydroflow/issues/684) @@ -458,6 +501,7 @@ Unchanged from previous release. - 3 commits contributed to the release. + - 25 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#643](https://github.com/hydro-project/hydroflow/issues/643), [#660](https://github.com/hydro-project/hydroflow/issues/660) diff --git a/hydroflow_macro/Cargo.toml b/hydroflow_macro/Cargo.toml index bf7f97434a8b..947cd24a997e 100644 --- a/hydroflow_macro/Cargo.toml +++ b/hydroflow_macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydroflow_macro" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydroflow_macro/" @@ -20,13 +20,13 @@ diagnostics = [ "hydroflow_lang/diagnostics" ] # Note: If we ever compile this proc macro crate to WASM (e.g., if we are # building on a WASM host), we may need to turn diagnostics off for WASM if # proc_macro2 still does not support WASM. -hydroflow_lang = { path = "../hydroflow_lang", version = "^0.9.0" } +hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0" } proc-macro2 = "1.0.74" proc-macro-crate = "1.0.0" quote = "1.0.35" syn = { version = "2.0.46", features = [ "parsing", "extra-traits" ] } [build-dependencies] -hydroflow_lang = { path = "../hydroflow_lang", version = "^0.9.0" } +hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0" } itertools = "0.10.0" quote = "1.0.35" diff --git a/hydroflow_plus/CHANGELOG.md b/hydroflow_plus/CHANGELOG.md index 5b211d6675fc..0556857807cb 100644 --- a/hydroflow_plus/CHANGELOG.md +++ b/hydroflow_plus/CHANGELOG.md @@ -5,8 +5,236 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.10.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - improve quickstart ergonomics + - add utility to dedup tees when debugging IR + - add decouple and simple test and two_pc + - implement support for external network outputs + - add ability to have staged flows inside unit tests + Whenever a Hydroflow+ program is compiled, it depends on a generated + `__staged` module, which contains the entire contents of the crate but + with every type / function made `pub` and exported, so that the compiled + UDFs can resolve local references appropriately. + + Previously, we would not do this for `#[cfg(test)]` modules, since they + may use `dev-dependencies` and therefore the generated module may fail + to compile when not in test mode. To solve this, when running a unit + test (marked with `hydroflow_plus::deploy::init_test()`) that uses + trybuild, we emit a version of the `__staged` module with `#[cfg(test)]` + modules included _into the generated trybuild sources_ because we can + guarantee via trybuild that the appropriate `dev-dependencies` are + available. + + This by itself allows crates depending on `hydroflow_plus` to have local + unit tests with Hydroflow+ logic inside them. But we also want to use + this support for unit tests inside `hydroflow_plus` itself. To enable + that, we eliminate the `hydroflow_plus_deploy` crate and move its + contents directly to `hydroflow_plus` itself so that we can access the + trybuild machinery without incurring a circular dependency. + + Also fixes #1408 + - add API for external network inputs + This is a key step towards being able to unit-test HF+ graphs, by being + able to have controlled inputs. Outputs next. + - splice UDFs with type hints to avoid inference failures + +### Bug Fixes + + - properly handle `crate::` imports + - be more careful about which parts of proposer and acceptor have to be maintained atomically + - adjust default features to allow compilation to musl targets + Previously, the default `deploy` feature would pull in Hydro Deploy and + its transitive native dependencies. + + Also sets up `examples/paxos.rs` with CLI flags to deploy to GCP. + - add missing `sample_every` for singletons + Discovered during a live-coding demo, we only had it for optionals + before. + +### Refactor + + - move rewrites to a submodule + - move `HfCompiled` and friends to a module + - use `location.flow_state()` to avoid clone + - deduplicate some error messages and drop unused `Interval` IR node + - dedup signatures for `Stream` operators + - clean up traits for cycles and forward references + - split up location module and store locations directly in streams + - use `usize` for slot numbers + - make Paxos-KV generic + - simplify latency calculations + - complete split into leader election and sequencing phases + - start splitting out leader election into a separate module + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1485). + * #1493 + * #1492 + * #1489 + * #1488 + * #1487 + * #1486 + * __->__ #1485 + - simplify `persist_pullup` code + Instead of matching on `&mut` and juggling ownership, instead match on + the owned node and always replaced `*node = new_node` (sometimes itself) + - use max and min in Paxos and make client generic over ballots + +### Style + + - fixes for nightly clippy + a couple few spurious `too_many_arguments` and a spurious + `zombie_processes` still on current nightly (`clippy 0.1.84 (4392847410 + 2024-10-21)`) + +### New Features (BREAKING) + + - implicitly apply default optimizations + This also changes the behavior of `with_default_optimize` to be + terminal, if users want to apply optimizations after these they should + explicitly invoke the optimizations. + - add an explicit API for creating tick contexts + Previously, each location had a (semantic) global clock that drives + ticks, and so all streams in a tick domain were all in the same atomic + block. For future optimizations, we'd like developers to be able to + place streams on the same location into different clocks to eliminate + synchronization between them, which in turn would allow the computations + in those separate clocks to be potentially decoupled across machines. + - strongly-typed runtime cluster IDs + Instead of `u32`s everywhere, we now have a `ClusterId` type that + ensures that cluster IDs are not misused. + - provide an API for creating cycles across tick iterations + Towards making it more clear which parts of a program depend on ticks + versus don't. + +### Refactor (BREAKING) + + - eliminate remaining `Hf` name prefixes + - location type parameter before boundedness + When looking at a prefix in an IDE, the location type argument is + generally more useful. + - dedup signatures for `Singleton` and `Optional` + Also renames `cross_singleton` to `zip` when both sides are + singleton-like. + - fold `Tick` vs `NoTick` into the location type parameter + Now, when the location is a top-level `Process` or `Cluster` that + corresponds to a `NoTick`, and for streams inside a tick we wrap the + location type (e.g. `Tick>`). This simplifies type + signatures for a lot of our example code. + - simplify intervals and split Paxos-KV into separate module + - move input APIs back to being on locations + - move `self_id` and `members` to be APIs on cluster instead of builder + +### Commit Statistics + + + + - 38 commits contributed to the release. + - 69 days passed between releases. + - 38 commits were understood as [conventional](https://www.conventionalcommits.org). + - 38 unique issues were worked on: [#1434](https://github.com/hydro-project/hydroflow/issues/1434), [#1441](https://github.com/hydro-project/hydroflow/issues/1441), [#1443](https://github.com/hydro-project/hydroflow/issues/1443), [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1449](https://github.com/hydro-project/hydroflow/issues/1449), [#1450](https://github.com/hydro-project/hydroflow/issues/1450), [#1451](https://github.com/hydro-project/hydroflow/issues/1451), [#1453](https://github.com/hydro-project/hydroflow/issues/1453), [#1455](https://github.com/hydro-project/hydroflow/issues/1455), [#1461](https://github.com/hydro-project/hydroflow/issues/1461), [#1464](https://github.com/hydro-project/hydroflow/issues/1464), [#1468](https://github.com/hydro-project/hydroflow/issues/1468), [#1471](https://github.com/hydro-project/hydroflow/issues/1471), [#1477](https://github.com/hydro-project/hydroflow/issues/1477), [#1485](https://github.com/hydro-project/hydroflow/issues/1485), [#1486](https://github.com/hydro-project/hydroflow/issues/1486), [#1488](https://github.com/hydro-project/hydroflow/issues/1488), [#1491](https://github.com/hydro-project/hydroflow/issues/1491), [#1505](https://github.com/hydro-project/hydroflow/issues/1505), [#1515](https://github.com/hydro-project/hydroflow/issues/1515), [#1516](https://github.com/hydro-project/hydroflow/issues/1516), [#1517](https://github.com/hydro-project/hydroflow/issues/1517), [#1519](https://github.com/hydro-project/hydroflow/issues/1519), [#1521](https://github.com/hydro-project/hydroflow/issues/1521), [#1523](https://github.com/hydro-project/hydroflow/issues/1523), [#1524](https://github.com/hydro-project/hydroflow/issues/1524), [#1525](https://github.com/hydro-project/hydroflow/issues/1525), [#1526](https://github.com/hydro-project/hydroflow/issues/1526), [#1527](https://github.com/hydro-project/hydroflow/issues/1527), [#1540](https://github.com/hydro-project/hydroflow/issues/1540), [#1541](https://github.com/hydro-project/hydroflow/issues/1541), [#1542](https://github.com/hydro-project/hydroflow/issues/1542), [#1543](https://github.com/hydro-project/hydroflow/issues/1543), [#1550](https://github.com/hydro-project/hydroflow/issues/1550), [#1551](https://github.com/hydro-project/hydroflow/issues/1551), [#1553](https://github.com/hydro-project/hydroflow/issues/1553), [#1554](https://github.com/hydro-project/hydroflow/issues/1554), [#1557](https://github.com/hydro-project/hydroflow/issues/1557) + +### Commit Details + + + +
view details + + * **[#1434](https://github.com/hydro-project/hydroflow/issues/1434)** + - Splice UDFs with type hints to avoid inference failures ([`60d9bec`](https://github.com/hydro-project/hydroflow/commit/60d9becaf0b67f9819316ce6d76bd867f7d46505)) + * **[#1441](https://github.com/hydro-project/hydroflow/issues/1441)** + - Provide an API for creating cycles across tick iterations ([`4f3b51b`](https://github.com/hydro-project/hydroflow/commit/4f3b51b4b9187f1187be23e6f04034778fe76388)) + * **[#1443](https://github.com/hydro-project/hydroflow/issues/1443)** + - Use max and min in Paxos and make client generic over ballots ([`c752aff`](https://github.com/hydro-project/hydroflow/commit/c752affc2ee2c5d82d19dd992f6a89b7070b8773)) + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1449](https://github.com/hydro-project/hydroflow/issues/1449)** + - Add API for external network inputs ([`8a80931`](https://github.com/hydro-project/hydroflow/commit/8a809315cd37929687fcabc34a12042db25d5767)) + * **[#1450](https://github.com/hydro-project/hydroflow/issues/1450)** + - Add ability to have staged flows inside unit tests ([`afe78c3`](https://github.com/hydro-project/hydroflow/commit/afe78c343658472513b34d28658634b253148aee)) + * **[#1451](https://github.com/hydro-project/hydroflow/issues/1451)** + - Implement support for external network outputs ([`074f2cf`](https://github.com/hydro-project/hydroflow/commit/074f2cf76158a126370a7e6b184bc6b928eb6fe2)) + * **[#1453](https://github.com/hydro-project/hydroflow/issues/1453)** + - Add decouple and simple test and two_pc ([`2141c5f`](https://github.com/hydro-project/hydroflow/commit/2141c5f04cb7e9cb7cd2f50f849f6c4b3d745377)) + * **[#1455](https://github.com/hydro-project/hydroflow/issues/1455)** + - Simplify `persist_pullup` code ([`1b18b35`](https://github.com/hydro-project/hydroflow/commit/1b18b358c87caa37a6519612131c8674653a2407)) + * **[#1461](https://github.com/hydro-project/hydroflow/issues/1461)** + - Add missing `sample_every` for singletons ([`d4320e3`](https://github.com/hydro-project/hydroflow/commit/d4320e311562a004c01342a2b0f03ab6e2520562)) + * **[#1464](https://github.com/hydro-project/hydroflow/issues/1464)** + - Adjust default features to allow compilation to musl targets ([`87a6834`](https://github.com/hydro-project/hydroflow/commit/87a68346aa10051d9d205d791407ce85546802da)) + * **[#1468](https://github.com/hydro-project/hydroflow/issues/1468)** + - Move `self_id` and `members` to be APIs on cluster instead of builder ([`8ad997b`](https://github.com/hydro-project/hydroflow/commit/8ad997b2dfd23bb09f7d361d763d6b5e78f406d6)) + * **[#1471](https://github.com/hydro-project/hydroflow/issues/1471)** + - Move input APIs back to being on locations ([`30c4f70`](https://github.com/hydro-project/hydroflow/commit/30c4f708faff7875ab42e551dd4bccbe231dfdad)) + * **[#1477](https://github.com/hydro-project/hydroflow/issues/1477)** + - Strongly-typed runtime cluster IDs ([`edd8649`](https://github.com/hydro-project/hydroflow/commit/edd86496240e4ebb39e0cf3bc153d8f282ff2870)) + * **[#1485](https://github.com/hydro-project/hydroflow/issues/1485)** + - Start splitting out leader election into a separate module ([`dff2a40`](https://github.com/hydro-project/hydroflow/commit/dff2a40669736014349cf12744d6a057a7992e11)) + * **[#1486](https://github.com/hydro-project/hydroflow/issues/1486)** + - Complete split into leader election and sequencing phases ([`8b7b1c6`](https://github.com/hydro-project/hydroflow/commit/8b7b1c60fd33b78f9a4b0873bbbd150260ae2ad5)) + * **[#1488](https://github.com/hydro-project/hydroflow/issues/1488)** + - Be more careful about which parts of proposer and acceptor have to be maintained atomically ([`275a0ed`](https://github.com/hydro-project/hydroflow/commit/275a0edf1fb8eba467728c24edf3a984c8eaca75)) + * **[#1491](https://github.com/hydro-project/hydroflow/issues/1491)** + - Add utility to dedup tees when debugging IR ([`98a21e3`](https://github.com/hydro-project/hydroflow/commit/98a21e36bd50d312402e46357fea6330816d0139)) + * **[#1505](https://github.com/hydro-project/hydroflow/issues/1505)** + - Fixes for nightly clippy ([`47cb703`](https://github.com/hydro-project/hydroflow/commit/47cb703e771f7d1c451ceb9d185ada96410949da)) + * **[#1515](https://github.com/hydro-project/hydroflow/issues/1515)** + - Simplify latency calculations ([`38b17cd`](https://github.com/hydro-project/hydroflow/commit/38b17cd977fb6c00ddc37e7a5b30e45dba17329e)) + * **[#1516](https://github.com/hydro-project/hydroflow/issues/1516)** + - Simplify intervals and split Paxos-KV into separate module ([`e5b456b`](https://github.com/hydro-project/hydroflow/commit/e5b456bdafcb80aae6039e4c90a2e60098e499bf)) + * **[#1517](https://github.com/hydro-project/hydroflow/issues/1517)** + - Make Paxos-KV generic ([`0a5abab`](https://github.com/hydro-project/hydroflow/commit/0a5abab3dac224c9591bcdd837d07c6e5c2773c6)) + * **[#1519](https://github.com/hydro-project/hydroflow/issues/1519)** + - Fold `Tick` vs `NoTick` into the location type parameter ([`5657563`](https://github.com/hydro-project/hydroflow/commit/5657563c989566e7c7b69dcb395e40b024c83c6c)) + * **[#1521](https://github.com/hydro-project/hydroflow/issues/1521)** + - Use `usize` for slot numbers ([`534fe97`](https://github.com/hydro-project/hydroflow/commit/534fe974101e38ecb847cd759dbaf503ff97f822)) + * **[#1523](https://github.com/hydro-project/hydroflow/issues/1523)** + - Split up location module and store locations directly in streams ([`d9634f2`](https://github.com/hydro-project/hydroflow/commit/d9634f242a97c06bdb53011bf3d75256425a1598)) + * **[#1524](https://github.com/hydro-project/hydroflow/issues/1524)** + - Clean up traits for cycles and forward references ([`bf9dcd5`](https://github.com/hydro-project/hydroflow/commit/bf9dcd5a923dd4b5efa337a9127086e5609a1722)) + * **[#1525](https://github.com/hydro-project/hydroflow/issues/1525)** + - Dedup signatures for `Stream` operators ([`244207c`](https://github.com/hydro-project/hydroflow/commit/244207c2acd2243ece6e787d54eadacf06e9e8bb)) + * **[#1526](https://github.com/hydro-project/hydroflow/issues/1526)** + - Dedup signatures for `Singleton` and `Optional` ([`919099e`](https://github.com/hydro-project/hydroflow/commit/919099ea3a414560b473ec89b993eeb26dfa2579)) + * **[#1527](https://github.com/hydro-project/hydroflow/issues/1527)** + - Properly handle `crate::` imports ([`2faffdb`](https://github.com/hydro-project/hydroflow/commit/2faffdbf2cc886da22e496df64f46aefa380766c)) + * **[#1540](https://github.com/hydro-project/hydroflow/issues/1540)** + - Deduplicate some error messages and drop unused `Interval` IR node ([`5b819a2`](https://github.com/hydro-project/hydroflow/commit/5b819a2dc6c507222a3e22d71efcde8b43cebad5)) + * **[#1541](https://github.com/hydro-project/hydroflow/issues/1541)** + - Use `location.flow_state()` to avoid clone ([`9f74405`](https://github.com/hydro-project/hydroflow/commit/9f744052dd4ac744f5a1baa4e0cb9253adaeba1b)) + * **[#1542](https://github.com/hydro-project/hydroflow/issues/1542)** + - Move `HfCompiled` and friends to a module ([`e9d05bf`](https://github.com/hydro-project/hydroflow/commit/e9d05bf11a0e85da8ed1a0fe00be7769298308c2)) + * **[#1543](https://github.com/hydro-project/hydroflow/issues/1543)** + - Move rewrites to a submodule ([`a1b4520`](https://github.com/hydro-project/hydroflow/commit/a1b45203178165683cb4b5ae611c598cc9c14853)) + * **[#1550](https://github.com/hydro-project/hydroflow/issues/1550)** + - Add an explicit API for creating tick contexts ([`5d5209b`](https://github.com/hydro-project/hydroflow/commit/5d5209b4a5556618d8a8c8219e1e2a4e837256ef)) + * **[#1551](https://github.com/hydro-project/hydroflow/issues/1551)** + - Location type parameter before boundedness ([`9107841`](https://github.com/hydro-project/hydroflow/commit/9107841700db0ae72de6269ab6f132be0ae51cd9)) + * **[#1553](https://github.com/hydro-project/hydroflow/issues/1553)** + - Improve quickstart ergonomics ([`baedf23`](https://github.com/hydro-project/hydroflow/commit/baedf23eaa056bc0dad8331d116bb71176764206)) + * **[#1554](https://github.com/hydro-project/hydroflow/issues/1554)** + - Eliminate remaining `Hf` name prefixes ([`0bd3a2d`](https://github.com/hydro-project/hydroflow/commit/0bd3a2d2230cbef24210f71a3ea83d82d1cc7244)) + * **[#1557](https://github.com/hydro-project/hydroflow/issues/1557)** + - Implicitly apply default optimizations ([`8d8b4b2`](https://github.com/hydro-project/hydroflow/commit/8d8b4b2288746e0aa2a95329d91297820aee7586)) +
+ ## v0.9.0 (2024-08-30) + + + + + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -14,6 +242,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --------- +### Refactor (BREAKING) + + - simplify process/cluster specs + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1394). + * #1395 + * __->__ #1394 + - defer network instantiation until after finalizing IR + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1377). + * #1395 + * #1394 + * __->__ #1377 + ### Documentation - cleanup doc comments for clippy latest @@ -28,6 +276,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Bug Fixes + + + + - remove `FlowProps` - rewrite IR in place to avoid stack overflow and disable cloning Cloning was unsafe because values behind a `Rc>` in the @@ -40,10 +292,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * #1405 * #1398 * __->__ #1404 - - wrong stream type for `source_interval` - - add `Clone` bounds to `cross_join` and simplify broadcast logic - - overly restrictive input types for `send_bincode_interleaved` - The original types prevented usage in cluster-to-cluster communication. +* #1398 +* __->__ #1404 + - rewrite IR in place to avoid stack overflow and disable cloning + Cloning was unsafe because values behind a `Rc>` in the + case of tee would be entangled with the old IR. + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1404). + * #1405 + * #1398 + * __->__ #1404 ### New Features (BREAKING) @@ -77,6 +338,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * __->__ #1377 - start rearranging stages of flow compilation to prepare for trybuild approach + simplify process/cluster specs + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1394). + * #1395 + * __->__ #1394 + - defer network instantiation until after finalizing IR + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1377). + * #1395 + * #1394 + * __->__ #1377 + + defer network instantiation until after finalizing IR + --- + [//]: # (BEGIN SAPLING FOOTER) + Stack created with [Sapling](https://sapling-scm.com). Best reviewed + with + [ReviewStack](https://reviewstack.dev/hydro-project/hydroflow/pull/1377). + * #1395 + * #1394 + * __->__ #1377 + - start rearranging stages of flow compilation to prepare for trybuild approach + ### Style (BREAKING) - rename some `CLI`->`Deploy`, decapitalize acronym names @@ -85,7 +375,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 20 commits contributed to the release. + - 21 commits contributed to the release. + - 38 days passed between releases. - 20 commits were understood as [conventional](https://www.conventionalcommits.org). - 20 unique issues were worked on: [#1358](https://github.com/hydro-project/hydroflow/issues/1358), [#1368](https://github.com/hydro-project/hydroflow/issues/1368), [#1375](https://github.com/hydro-project/hydroflow/issues/1375), [#1376](https://github.com/hydro-project/hydroflow/issues/1376), [#1377](https://github.com/hydro-project/hydroflow/issues/1377), [#1394](https://github.com/hydro-project/hydroflow/issues/1394), [#1395](https://github.com/hydro-project/hydroflow/issues/1395), [#1398](https://github.com/hydro-project/hydroflow/issues/1398), [#1399](https://github.com/hydro-project/hydroflow/issues/1399), [#1404](https://github.com/hydro-project/hydroflow/issues/1404), [#1405](https://github.com/hydro-project/hydroflow/issues/1405), [#1410](https://github.com/hydro-project/hydroflow/issues/1410), [#1413](https://github.com/hydro-project/hydroflow/issues/1413), [#1420](https://github.com/hydro-project/hydroflow/issues/1420), [#1421](https://github.com/hydro-project/hydroflow/issues/1421), [#1423](https://github.com/hydro-project/hydroflow/issues/1423), [#1425](https://github.com/hydro-project/hydroflow/issues/1425), [#1427](https://github.com/hydro-project/hydroflow/issues/1427), [#1428](https://github.com/hydro-project/hydroflow/issues/1428), [#1430](https://github.com/hydro-project/hydroflow/issues/1430) @@ -135,8 +426,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Cleanup doc comments for clippy latest ([`f5f1eb0`](https://github.com/hydro-project/hydroflow/commit/f5f1eb0c612f5c0c1752360d972ef6853c5e12f0)) * **[#1430](https://github.com/hydro-project/hydroflow/issues/1430)** - Add API for cycle with initial value ([`71f69aa`](https://github.com/hydro-project/hydroflow/commit/71f69aa5e9f2ba187f07c44c0a9f2becfe72aab1)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) + + wrong stream type for source_interval add Clone bounds to cross_join and simplify broadcast logic overly restrictive input types for send_bincode_interleavedThe original types prevented usage in cluster-to-cluster communication. + ## v0.8.0 (2024-07-23) @@ -160,6 +456,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 3 commits contributed to the release. + - 59 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1143](https://github.com/hydro-project/hydroflow/issues/1143), [#1216](https://github.com/hydro-project/hydroflow/issues/1216), [#1295](https://github.com/hydro-project/hydroflow/issues/1295) @@ -205,6 +502,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 7 commits contributed to the release. + - 44 days passed between releases. - 6 commits were understood as [conventional](https://www.conventionalcommits.org). - 6 unique issues were worked on: [#1143](https://github.com/hydro-project/hydroflow/issues/1143), [#1151](https://github.com/hydro-project/hydroflow/issues/1151), [#1156](https://github.com/hydro-project/hydroflow/issues/1156), [#1157](https://github.com/hydro-project/hydroflow/issues/1157), [#1194](https://github.com/hydro-project/hydroflow/issues/1194), [#1238](https://github.com/hydro-project/hydroflow/issues/1238) @@ -264,6 +562,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 9 commits contributed to the release. + - 38 days passed between releases. - 6 commits were understood as [conventional](https://www.conventionalcommits.org). - 6 unique issues were worked on: [#1083](https://github.com/hydro-project/hydroflow/issues/1083), [#1098](https://github.com/hydro-project/hydroflow/issues/1098), [#1100](https://github.com/hydro-project/hydroflow/issues/1100), [#1101](https://github.com/hydro-project/hydroflow/issues/1101), [#1107](https://github.com/hydro-project/hydroflow/issues/1107), [#1140](https://github.com/hydro-project/hydroflow/issues/1140) @@ -311,6 +610,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 4 commits contributed to the release. + - 32 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1070](https://github.com/hydro-project/hydroflow/issues/1070), [#1080](https://github.com/hydro-project/hydroflow/issues/1080), [#1084](https://github.com/hydro-project/hydroflow/issues/1084) diff --git a/hydroflow_plus/Cargo.toml b/hydroflow_plus/Cargo.toml index 847213de8fc8..f06788155f7d 100644 --- a/hydroflow_plus/Cargo.toml +++ b/hydroflow_plus/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hydroflow_plus" publish = true -version = "0.9.0" +version = "0.10.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/hydroflow_plus/" @@ -25,26 +25,26 @@ quote = "1.0.35" syn = { version = "2.0.46", features = [ "parsing", "extra-traits", "visit-mut" ] } proc-macro2 = "1.0.74" proc-macro-crate = "1.0.0" -hydroflow = { path = "../hydroflow", version = "^0.9.0", default-features = false } -hydroflow_lang = { path = "../hydroflow_lang", version = "^0.9.0" } +hydroflow = { path = "../hydroflow", version = "^0.10.0", default-features = false } +hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0" } serde = { version = "1.0.197", features = [ "derive" ] } bincode = "1.3.1" tokio = { version = "1.29.0", features = [ "full" ] } -stageleft = { path = "../stageleft", version = "^0.4.0" } +stageleft = { path = "../stageleft", version = "^0.5.0" } nameof = "1.0.0" sha2 = "0.10.0" -stageleft_tool = { path = "../stageleft_tool", version = "^0.3.0" } -hydro_deploy = { path = "../hydro_deploy/core", version = "^0.9.0", optional = true } +stageleft_tool = { path = "../stageleft_tool", version = "^0.4.0" } +hydro_deploy = { path = "../hydro_deploy/core", version = "^0.10.0", optional = true } prettyplease = { version = "0.2.0", features = [ "verbatim" ], optional = true } toml = { version = "0.8.0", optional = true } trybuild-internals-api = { version = "1.0.99", optional = true } [build-dependencies] -stageleft_tool = { path = "../stageleft_tool", version = "^0.3.0" } +stageleft_tool = { path = "../stageleft_tool", version = "^0.4.0" } [dev-dependencies] insta = "1.39" -hydro_deploy = { path = "../hydro_deploy/core", version = "^0.9.0" } +hydro_deploy = { path = "../hydro_deploy/core", version = "^0.10.0" } async-ssh2-lite = { version = "0.5.0", features = ["vendored-openssl"] } ctor = "0.2.8" diff --git a/hydroflow_plus_test/Cargo.toml b/hydroflow_plus_test/Cargo.toml index 471cbd781d04..6d57e17ebbe1 100644 --- a/hydroflow_plus_test/Cargo.toml +++ b/hydroflow_plus_test/Cargo.toml @@ -12,18 +12,18 @@ default = ["stageleft_devel"] stageleft_devel = [] [dependencies] -hydroflow_plus = { path = "../hydroflow_plus", version = "^0.9.0" } +hydroflow_plus = { path = "../hydroflow_plus", version = "^0.10.0" } tokio = { version = "1.29.0", features = [ "full" ] } -stageleft = { path = "../stageleft", version = "^0.4.0" } +stageleft = { path = "../stageleft", version = "^0.5.0" } rand = "0.8.0" serde = { version = "1.0.197", features = [ "derive" ] } [build-dependencies] -stageleft_tool = { path = "../stageleft_tool", version = "^0.3.0" } +stageleft_tool = { path = "../stageleft_tool", version = "^0.4.0" } [dev-dependencies] insta = "1.39" -hydro_deploy = { path = "../hydro_deploy/core", version = "^0.9.0" } -hydroflow_plus = { path = "../hydroflow_plus", version = "^0.9.0", features = [ "deploy" ] } +hydro_deploy = { path = "../hydro_deploy/core", version = "^0.10.0" } +hydroflow_plus = { path = "../hydroflow_plus", version = "^0.10.0", features = [ "deploy" ] } futures = "0.3.0" async-ssh2-lite = { version = "0.5.0", features = ["vendored-openssl"] } diff --git a/hydroflow_plus_test_local/Cargo.toml b/hydroflow_plus_test_local/Cargo.toml index b38ed516dfa9..b8f301a0ce31 100644 --- a/hydroflow_plus_test_local/Cargo.toml +++ b/hydroflow_plus_test_local/Cargo.toml @@ -11,15 +11,15 @@ workspace = true stageleft_devel = [] [dependencies] -hydroflow = { path = "../hydroflow", version = "^0.9.0", default-features = false } # , features = ["debugging"] } -hydroflow_plus = { path = "../hydroflow_plus", version = "^0.9.0" } -stageleft = { path = "../stageleft", version = "^0.4.0" } +hydroflow = { path = "../hydroflow", version = "^0.10.0", default-features = false } # , features = ["debugging"] } +hydroflow_plus = { path = "../hydroflow_plus", version = "^0.10.0" } +stageleft = { path = "../stageleft", version = "^0.5.0" } rand = "0.8.0" hydroflow_plus_test_local_macro = { path = "../hydroflow_plus_test_local_macro" } [build-dependencies] -stageleft_tool = { path = "../stageleft_tool", version = "^0.3.0" } +stageleft_tool = { path = "../stageleft_tool", version = "^0.4.0" } [dev-dependencies] insta = "1.39" diff --git a/hydroflow_plus_test_local_macro/Cargo.toml b/hydroflow_plus_test_local_macro/Cargo.toml index 67427cf11a03..57da63e32396 100644 --- a/hydroflow_plus_test_local_macro/Cargo.toml +++ b/hydroflow_plus_test_local_macro/Cargo.toml @@ -12,9 +12,9 @@ proc-macro = true path = "../hydroflow_plus_test_local/src/lib.rs" [dependencies] -hydroflow_plus = { path = "../hydroflow_plus", version = "^0.9.0" } -stageleft = { path = "../stageleft", version = "^0.4.0" } +hydroflow_plus = { path = "../hydroflow_plus", version = "^0.10.0" } +stageleft = { path = "../stageleft", version = "^0.5.0" } rand = "0.8.0" [build-dependencies] -stageleft_tool = { path = "../stageleft_tool", version = "^0.3.0" } +stageleft_tool = { path = "../stageleft_tool", version = "^0.4.0" } diff --git a/lattices/CHANGELOG.md b/lattices/CHANGELOG.md index ceb4043f103a..24698a4b3b12 100644 --- a/lattices/CHANGELOG.md +++ b/lattices/CHANGELOG.md @@ -5,8 +5,58 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.5.8 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - generalized hash trie indexes for relational tuples + Generalized Hash Tries are part of the SIGMOD '23 FreeJoin + [paper](https://dl.acm.org/doi/abs/10.1145/3589295) by + Wang/Willsey/Suciu. They provide a compressed ("factorized") + representation of relations. By operating in the factorized domain, join + algorithms can defer cross-products and achieve asymptotically optimal + performance. + + --------- + +### Style + + - fixes for nightly clippy + a couple few spurious `too_many_arguments` and a spurious + `zombie_processes` still on current nightly (`clippy 0.1.84 (4392847410 + 2024-10-21)`) + +### Commit Statistics + + + + - 3 commits contributed to the release. + - 69 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 3 unique issues were worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1503](https://github.com/hydro-project/hydroflow/issues/1503), [#1505](https://github.com/hydro-project/hydroflow/issues/1505) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1503](https://github.com/hydro-project/hydroflow/issues/1503)** + - Generalized hash trie indexes for relational tuples ([`f7e740f`](https://github.com/hydro-project/hydroflow/commit/f7e740fb2ba36d0fcf3fd196d60333552911e3a4)) + * **[#1505](https://github.com/hydro-project/hydroflow/issues/1505)** + - Fixes for nightly clippy ([`47cb703`](https://github.com/hydro-project/hydroflow/commit/47cb703e771f7d1c451ceb9d185ada96410949da)) +
+ ## 0.5.7 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -22,7 +72,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 2 commits contributed to the release. + - 3 commits contributed to the release. + - 38 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1423](https://github.com/hydro-project/hydroflow/issues/1423), [#1428](https://github.com/hydro-project/hydroflow/issues/1428) @@ -36,6 +87,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) * **[#1428](https://github.com/hydro-project/hydroflow/issues/1428)** - Cleanup doc comments for clippy latest ([`f5f1eb0`](https://github.com/hydro-project/hydroflow/commit/f5f1eb0c612f5c0c1752360d972ef6853c5e12f0)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## 0.5.6 (2024-07-23) @@ -81,6 +134,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 5 commits contributed to the release. + - 59 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#1244](https://github.com/hydro-project/hydroflow/issues/1244), [#1250](https://github.com/hydro-project/hydroflow/issues/1250), [#1309](https://github.com/hydro-project/hydroflow/issues/1309), [#1326](https://github.com/hydro-project/hydroflow/issues/1326) @@ -129,6 +183,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 9 commits contributed to the release. + - 48 days passed between releases. - 6 commits were understood as [conventional](https://www.conventionalcommits.org). - 7 unique issues were worked on: [#1155](https://github.com/hydro-project/hydroflow/issues/1155), [#1156](https://github.com/hydro-project/hydroflow/issues/1156), [#1174](https://github.com/hydro-project/hydroflow/issues/1174), [#1181](https://github.com/hydro-project/hydroflow/issues/1181), [#1230](https://github.com/hydro-project/hydroflow/issues/1230), [#1233](https://github.com/hydro-project/hydroflow/issues/1233), [#1236](https://github.com/hydro-project/hydroflow/issues/1236) @@ -157,9 +212,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Definitions of linearity and bilinearity in algebra lib ([`d8e4d9d`](https://github.com/hydro-project/hydroflow/commit/d8e4d9dc784ae28fcefe5f32a0561698c1196d31)) - -set_union: is not a latticemap_union - not safe to expose mapunion_find - K is not a latticeVecUnion - not safe to expose vecWithTop/WithBot - already pubPair - Changed in this commitDomPair - Already correctly done with left pub andright private.Conflict / Point - T is not a lattice type.() - No nested types here. - ## 0.5.4 (2024-04-05) @@ -175,6 +227,7 @@ Unchanged from previous release. - 3 commits contributed to the release. + - 34 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1127](https://github.com/hydro-project/hydroflow/issues/1127) @@ -214,6 +267,7 @@ Unchanged from previous release. - 5 commits contributed to the release. + - 28 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1061](https://github.com/hydro-project/hydroflow/issues/1061), [#1062](https://github.com/hydro-project/hydroflow/issues/1062), [#1084](https://github.com/hydro-project/hydroflow/issues/1084) @@ -245,6 +299,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 4 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1052](https://github.com/hydro-project/hydroflow/issues/1052) @@ -289,6 +344,7 @@ Unchanged from previous release. - 8 commits contributed to the release. + - 110 days passed between releases. - 6 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#1032](https://github.com/hydro-project/hydroflow/issues/1032), [#942](https://github.com/hydro-project/hydroflow/issues/942), [#960](https://github.com/hydro-project/hydroflow/issues/960), [#967](https://github.com/hydro-project/hydroflow/issues/967) @@ -339,6 +395,7 @@ Unchanged from previous release. - 7 commits contributed to the release. + - 56 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#915](https://github.com/hydro-project/hydroflow/issues/915), [#922](https://github.com/hydro-project/hydroflow/issues/922) @@ -407,6 +464,7 @@ Unchanged from previous release. - 10 commits contributed to the release. + - 42 days passed between releases. - 9 commits were understood as [conventional](https://www.conventionalcommits.org). - 8 unique issues were worked on: [#822](https://github.com/hydro-project/hydroflow/issues/822), [#849](https://github.com/hydro-project/hydroflow/issues/849), [#854](https://github.com/hydro-project/hydroflow/issues/854), [#860](https://github.com/hydro-project/hydroflow/issues/860), [#865](https://github.com/hydro-project/hydroflow/issues/865), [#866](https://github.com/hydro-project/hydroflow/issues/866), [#867](https://github.com/hydro-project/hydroflow/issues/867), [#879](https://github.com/hydro-project/hydroflow/issues/879) @@ -496,6 +554,7 @@ Unchanged from previous release. - 18 commits contributed to the release. + - 33 days passed between releases. - 17 commits were understood as [conventional](https://www.conventionalcommits.org). - 12 unique issues were worked on: [#742](https://github.com/hydro-project/hydroflow/issues/742), [#744](https://github.com/hydro-project/hydroflow/issues/744), [#761](https://github.com/hydro-project/hydroflow/issues/761), [#763](https://github.com/hydro-project/hydroflow/issues/763), [#765](https://github.com/hydro-project/hydroflow/issues/765), [#766](https://github.com/hydro-project/hydroflow/issues/766), [#767](https://github.com/hydro-project/hydroflow/issues/767), [#772](https://github.com/hydro-project/hydroflow/issues/772), [#773](https://github.com/hydro-project/hydroflow/issues/773), [#780](https://github.com/hydro-project/hydroflow/issues/780), [#789](https://github.com/hydro-project/hydroflow/issues/789), [#793](https://github.com/hydro-project/hydroflow/issues/793) @@ -556,6 +615,7 @@ Unchanged from previous release. - 3 commits contributed to the release. + - 1 day passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -582,6 +642,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 6 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#691](https://github.com/hydro-project/hydroflow/issues/691) @@ -615,6 +676,7 @@ Unchanged from previous release. - 5 commits contributed to the release. + - 2 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#671](https://github.com/hydro-project/hydroflow/issues/671), [#674](https://github.com/hydro-project/hydroflow/issues/674), [#687](https://github.com/hydro-project/hydroflow/issues/687) @@ -662,6 +724,7 @@ Unchanged from previous release. - 14 commits contributed to the release. + - 18 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 10 unique issues were worked on: [#625](https://github.com/hydro-project/hydroflow/issues/625), [#637](https://github.com/hydro-project/hydroflow/issues/637), [#638](https://github.com/hydro-project/hydroflow/issues/638), [#642](https://github.com/hydro-project/hydroflow/issues/642), [#644](https://github.com/hydro-project/hydroflow/issues/644), [#645](https://github.com/hydro-project/hydroflow/issues/645), [#658](https://github.com/hydro-project/hydroflow/issues/658), [#660](https://github.com/hydro-project/hydroflow/issues/660), [#664](https://github.com/hydro-project/hydroflow/issues/664), [#667](https://github.com/hydro-project/hydroflow/issues/667) diff --git a/lattices/Cargo.toml b/lattices/Cargo.toml index 7684d2ca3cfb..844d53d311c3 100644 --- a/lattices/Cargo.toml +++ b/lattices/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lattices" publish = true -version = "0.5.7" +version = "0.5.8" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/lattices/" @@ -18,9 +18,9 @@ serde = ["dep:serde"] cc-traits = "2.0.0" sealed = "0.5.0" serde = { version = "1.0.197", features = ["derive"], optional = true } -lattices_macro = { path = "../lattices_macro", version = "^0.5.6" } +lattices_macro = { path = "../lattices_macro", version = "^0.5.7" } ref-cast = "1.0.23" -variadics = { path = "../variadics", version = "^0.0.6" } +variadics = { path = "../variadics", version = "^0.0.7" } variadics_macro = { path = "../variadics_macro", version = "^0.5.5" } [dev-dependencies] diff --git a/lattices_macro/CHANGELOG.md b/lattices_macro/CHANGELOG.md index 6234c31f39b4..4f06af34a7f8 100644 --- a/lattices_macro/CHANGELOG.md +++ b/lattices_macro/CHANGELOG.md @@ -1,7 +1,34 @@ +## v0.5.7 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 69 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) +
+ ## v0.5.6 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -13,7 +40,8 @@ - - 1 commit contributed to the release. + - 2 commits contributed to the release. + - 38 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -25,6 +53,8 @@ * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## v0.5.5 (2024-07-23) diff --git a/lattices_macro/Cargo.toml b/lattices_macro/Cargo.toml index 7daa07140178..6a7dd5e8636b 100644 --- a/lattices_macro/Cargo.toml +++ b/lattices_macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lattices_macro" publish = true -version = "0.5.6" +version = "0.5.7" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/lattices/" diff --git a/multiplatform_test/CHANGELOG.md b/multiplatform_test/CHANGELOG.md index ab716140ec61..f02c6cbb9faf 100644 --- a/multiplatform_test/CHANGELOG.md +++ b/multiplatform_test/CHANGELOG.md @@ -5,8 +5,35 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.3.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 69 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) +
+ ## 0.2.0 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -18,7 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 1 commit contributed to the release. + - 2 commits contributed to the release. + - 97 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -30,6 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## 0.1.0 (2024-05-24) diff --git a/multiplatform_test/Cargo.toml b/multiplatform_test/Cargo.toml index cccc85ec4c15..926f07804ef7 100644 --- a/multiplatform_test/Cargo.toml +++ b/multiplatform_test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "multiplatform_test" publish = true -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/multiplatform_test/" diff --git a/pusherator/CHANGELOG.md b/pusherator/CHANGELOG.md index 72128d0236fc..14c0fa4e081f 100644 --- a/pusherator/CHANGELOG.md +++ b/pusherator/CHANGELOG.md @@ -5,8 +5,35 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.0.9 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 69 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) +
+ ## 0.0.8 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -18,7 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 1 commit contributed to the release. + - 2 commits contributed to the release. + - 38 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -30,6 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## 0.0.7 (2024-07-23) @@ -47,6 +77,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 59 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -74,6 +105,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 83 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1204](https://github.com/hydro-project/hydroflow/issues/1204) @@ -102,6 +134,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 32 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -132,6 +165,7 @@ Unchanged from previous release. - 2 commits contributed to the release. + - 166 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -164,6 +198,7 @@ Unchanged from previous release. - 3 commits contributed to the release. + - 42 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#822](https://github.com/hydro-project/hydroflow/issues/822), [#835](https://github.com/hydro-project/hydroflow/issues/835) @@ -193,6 +228,7 @@ Unchanged from previous release. - 3 commits contributed to the release. + - 44 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#780](https://github.com/hydro-project/hydroflow/issues/780) @@ -224,6 +260,7 @@ Unchanged from previous release. - 3 commits contributed to the release. + - 25 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#660](https://github.com/hydro-project/hydroflow/issues/660) diff --git a/pusherator/Cargo.toml b/pusherator/Cargo.toml index 4c454c1f0611..e02eb53f0d75 100644 --- a/pusherator/Cargo.toml +++ b/pusherator/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pusherator" publish = true -version = "0.0.8" +version = "0.0.9" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/pusherator/" @@ -16,4 +16,4 @@ demux = [ "dep:variadics" ] [dependencies] either = "1.0.0" -variadics = { optional = true, path = "../variadics", version = "^0.0.6" } +variadics = { optional = true, path = "../variadics", version = "^0.0.7" } diff --git a/stageleft/CHANGELOG.md b/stageleft/CHANGELOG.md index 2d6df3147511..466d60124526 100644 --- a/stageleft/CHANGELOG.md +++ b/stageleft/CHANGELOG.md @@ -5,8 +5,55 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.5.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - add API for external network inputs + This is a key step towards being able to unit-test HF+ graphs, by being + able to have controlled inputs. Outputs next. + - splice UDFs with type hints to avoid inference failures + +### Style + + - fixes for nightly clippy + a couple few spurious `too_many_arguments` and a spurious + `zombie_processes` still on current nightly (`clippy 0.1.84 (4392847410 + 2024-10-21)`) + +### Commit Statistics + + + + - 4 commits contributed to the release. + - 69 days passed between releases. + - 4 commits were understood as [conventional](https://www.conventionalcommits.org). + - 4 unique issues were worked on: [#1434](https://github.com/hydro-project/hydroflow/issues/1434), [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1449](https://github.com/hydro-project/hydroflow/issues/1449), [#1505](https://github.com/hydro-project/hydroflow/issues/1505) + +### Commit Details + + + +
view details + + * **[#1434](https://github.com/hydro-project/hydroflow/issues/1434)** + - Splice UDFs with type hints to avoid inference failures ([`60d9bec`](https://github.com/hydro-project/hydroflow/commit/60d9becaf0b67f9819316ce6d76bd867f7d46505)) + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1449](https://github.com/hydro-project/hydroflow/issues/1449)** + - Add API for external network inputs ([`8a80931`](https://github.com/hydro-project/hydroflow/commit/8a809315cd37929687fcabc34a12042db25d5767)) + * **[#1505](https://github.com/hydro-project/hydroflow/issues/1505)** + - Fixes for nightly clippy ([`47cb703`](https://github.com/hydro-project/hydroflow/commit/47cb703e771f7d1c451ceb9d185ada96410949da)) +
+ ## v0.4.0 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -31,7 +78,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 3 commits contributed to the release. + - 4 commits contributed to the release. + - 97 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1397](https://github.com/hydro-project/hydroflow/issues/1397), [#1398](https://github.com/hydro-project/hydroflow/issues/1398), [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -47,6 +95,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use trybuild to compile subgraph binaries ([`46a8a2c`](https://github.com/hydro-project/hydroflow/commit/46a8a2cb08732bb21096e824bc4542d208c68fb2)) * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## v0.3.0 (2024-05-24) @@ -66,6 +116,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 4 commits contributed to the release. + - 48 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1104](https://github.com/hydro-project/hydroflow/issues/1104), [#1151](https://github.com/hydro-project/hydroflow/issues/1151), [#1225](https://github.com/hydro-project/hydroflow/issues/1225) @@ -108,6 +159,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 3 commits contributed to the release. + - 34 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1090](https://github.com/hydro-project/hydroflow/issues/1090), [#1100](https://github.com/hydro-project/hydroflow/issues/1100) @@ -137,6 +189,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 2 commits contributed to the release. + - 32 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1070](https://github.com/hydro-project/hydroflow/issues/1070) diff --git a/stageleft/Cargo.toml b/stageleft/Cargo.toml index d58e1bfa18f0..49a0ee4c9a4b 100644 --- a/stageleft/Cargo.toml +++ b/stageleft/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "stageleft" publish = true -version = "0.4.0" +version = "0.5.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/stageleft/" @@ -18,4 +18,4 @@ quote = "1.0.35" syn = { version = "2.0.46", features = [ "parsing", "extra-traits", "visit-mut" ] } proc-macro2 = "1.0.74" proc-macro-crate = "1.0.0" -stageleft_macro = { path = "../stageleft_macro", version = "^0.3.0" } +stageleft_macro = { path = "../stageleft_macro", version = "^0.4.0" } diff --git a/stageleft_macro/CHANGELOG.md b/stageleft_macro/CHANGELOG.md index 8ca48945af54..ba0e9b7679f2 100644 --- a/stageleft_macro/CHANGELOG.md +++ b/stageleft_macro/CHANGELOG.md @@ -5,8 +5,73 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.4.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - add ability to have staged flows inside unit tests + Whenever a Hydroflow+ program is compiled, it depends on a generated + `__staged` module, which contains the entire contents of the crate but + with every type / function made `pub` and exported, so that the compiled + UDFs can resolve local references appropriately. + + Previously, we would not do this for `#[cfg(test)]` modules, since they + may use `dev-dependencies` and therefore the generated module may fail + to compile when not in test mode. To solve this, when running a unit + test (marked with `hydroflow_plus::deploy::init_test()`) that uses + trybuild, we emit a version of the `__staged` module with `#[cfg(test)]` + modules included _into the generated trybuild sources_ because we can + guarantee via trybuild that the appropriate `dev-dependencies` are + available. + + This by itself allows crates depending on `hydroflow_plus` to have local + unit tests with Hydroflow+ logic inside them. But we also want to use + this support for unit tests inside `hydroflow_plus` itself. To enable + that, we eliminate the `hydroflow_plus_deploy` crate and move its + contents directly to `hydroflow_plus` itself so that we can access the + trybuild machinery without incurring a circular dependency. + + Also fixes #1408 + - splice UDFs with type hints to avoid inference failures + +### Bug Fixes + + - support tuple patterns + +### Commit Statistics + + + + - 4 commits contributed to the release. + - 69 days passed between releases. + - 4 commits were understood as [conventional](https://www.conventionalcommits.org). + - 4 unique issues were worked on: [#1434](https://github.com/hydro-project/hydroflow/issues/1434), [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1445](https://github.com/hydro-project/hydroflow/issues/1445), [#1450](https://github.com/hydro-project/hydroflow/issues/1450) + +### Commit Details + + + +
view details + + * **[#1434](https://github.com/hydro-project/hydroflow/issues/1434)** + - Splice UDFs with type hints to avoid inference failures ([`60d9bec`](https://github.com/hydro-project/hydroflow/commit/60d9becaf0b67f9819316ce6d76bd867f7d46505)) + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1445](https://github.com/hydro-project/hydroflow/issues/1445)** + - Support tuple patterns ([`486dfbe`](https://github.com/hydro-project/hydroflow/commit/486dfbe1fab51f1b7c7aa03a51e6e9e4e427b912)) + * **[#1450](https://github.com/hydro-project/hydroflow/issues/1450)** + - Add ability to have staged flows inside unit tests ([`afe78c3`](https://github.com/hydro-project/hydroflow/commit/afe78c343658472513b34d28658634b253148aee)) +
+ ## v0.3.0 (2024-08-30) + + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -26,7 +91,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 3 commits contributed to the release. + - 4 commits contributed to the release. + - 97 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 3 unique issues were worked on: [#1423](https://github.com/hydro-project/hydroflow/issues/1423), [#1426](https://github.com/hydro-project/hydroflow/issues/1426), [#1428](https://github.com/hydro-project/hydroflow/issues/1428) @@ -42,6 +108,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove `lazy_static` dependency ([`461ae84`](https://github.com/hydro-project/hydroflow/commit/461ae845c6c6506c733be6287eeefe6e3beca52c)) * **[#1428](https://github.com/hydro-project/hydroflow/issues/1428)** - Cleanup doc comments for clippy latest ([`f5f1eb0`](https://github.com/hydro-project/hydroflow/commit/f5f1eb0c612f5c0c1752360d972ef6853c5e12f0)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## v0.2.0 (2024-05-24) @@ -60,6 +128,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 3 commits contributed to the release. + - 48 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1104](https://github.com/hydro-project/hydroflow/issues/1104), [#1151](https://github.com/hydro-project/hydroflow/issues/1151) @@ -105,6 +174,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 5 commits contributed to the release. + - 67 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#1090](https://github.com/hydro-project/hydroflow/issues/1090), [#1100](https://github.com/hydro-project/hydroflow/issues/1100), [#1117](https://github.com/hydro-project/hydroflow/issues/1117), [#1124](https://github.com/hydro-project/hydroflow/issues/1124) diff --git a/stageleft_macro/Cargo.toml b/stageleft_macro/Cargo.toml index 6912ac8471f1..8ef2802afba9 100644 --- a/stageleft_macro/Cargo.toml +++ b/stageleft_macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "stageleft_macro" publish = true -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/stageleft_macro/" diff --git a/stageleft_test/Cargo.toml b/stageleft_test/Cargo.toml index ef0f6b0113b1..30dbf948227a 100644 --- a/stageleft_test/Cargo.toml +++ b/stageleft_test/Cargo.toml @@ -11,8 +11,8 @@ workspace = true stageleft_devel = [] [dependencies] -stageleft = { path = "../stageleft", version = "^0.4.0" } +stageleft = { path = "../stageleft", version = "^0.5.0" } stageleft_test_macro = { path = "../stageleft_test_macro" } [build-dependencies] -stageleft_tool = { path = "../stageleft_tool", version = "^0.3.0" } +stageleft_tool = { path = "../stageleft_tool", version = "^0.4.0" } diff --git a/stageleft_test_macro/Cargo.toml b/stageleft_test_macro/Cargo.toml index f576ae78d027..57368231137a 100644 --- a/stageleft_test_macro/Cargo.toml +++ b/stageleft_test_macro/Cargo.toml @@ -12,7 +12,7 @@ proc-macro = true path = "../stageleft_test/src/lib.rs" [dependencies] -stageleft = { path = "../stageleft", version = "^0.4.0" } +stageleft = { path = "../stageleft", version = "^0.5.0" } [build-dependencies] -stageleft_tool = { path = "../stageleft_tool", version = "^0.3.0" } +stageleft_tool = { path = "../stageleft_tool", version = "^0.4.0" } diff --git a/stageleft_tool/CHANGELOG.md b/stageleft_tool/CHANGELOG.md index 4a3c7616eb14..8614dcf47336 100644 --- a/stageleft_tool/CHANGELOG.md +++ b/stageleft_tool/CHANGELOG.md @@ -1,7 +1,74 @@ +## v0.4.0 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - add ability to have staged flows inside unit tests + Whenever a Hydroflow+ program is compiled, it depends on a generated + `__staged` module, which contains the entire contents of the crate but + with every type / function made `pub` and exported, so that the compiled + UDFs can resolve local references appropriately. + + Previously, we would not do this for `#[cfg(test)]` modules, since they + may use `dev-dependencies` and therefore the generated module may fail + to compile when not in test mode. To solve this, when running a unit + test (marked with `hydroflow_plus::deploy::init_test()`) that uses + trybuild, we emit a version of the `__staged` module with `#[cfg(test)]` + modules included _into the generated trybuild sources_ because we can + guarantee via trybuild that the appropriate `dev-dependencies` are + available. + + This by itself allows crates depending on `hydroflow_plus` to have local + unit tests with Hydroflow+ logic inside them. But we also want to use + this support for unit tests inside `hydroflow_plus` itself. To enable + that, we eliminate the `hydroflow_plus_deploy` crate and move its + contents directly to `hydroflow_plus` itself so that we can access the + trybuild machinery without incurring a circular dependency. + + Also fixes #1408 + +### Bug Fixes + + - properly handle `crate::` imports + +### Refactor + + - complete split into leader election and sequencing phases + +### Commit Statistics + + + + - 4 commits contributed to the release. + - 69 days passed between releases. + - 4 commits were understood as [conventional](https://www.conventionalcommits.org). + - 4 unique issues were worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1450](https://github.com/hydro-project/hydroflow/issues/1450), [#1486](https://github.com/hydro-project/hydroflow/issues/1486), [#1527](https://github.com/hydro-project/hydroflow/issues/1527) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1450](https://github.com/hydro-project/hydroflow/issues/1450)** + - Add ability to have staged flows inside unit tests ([`afe78c3`](https://github.com/hydro-project/hydroflow/commit/afe78c343658472513b34d28658634b253148aee)) + * **[#1486](https://github.com/hydro-project/hydroflow/issues/1486)** + - Complete split into leader election and sequencing phases ([`8b7b1c6`](https://github.com/hydro-project/hydroflow/commit/8b7b1c60fd33b78f9a4b0873bbbd150260ae2ad5)) + * **[#1527](https://github.com/hydro-project/hydroflow/issues/1527)** + - Properly handle `crate::` imports ([`2faffdb`](https://github.com/hydro-project/hydroflow/commit/2faffdbf2cc886da22e496df64f46aefa380766c)) +
+ ## v0.3.0 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -17,7 +84,8 @@ - - 2 commits contributed to the release. + - 3 commits contributed to the release. + - 97 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1398](https://github.com/hydro-project/hydroflow/issues/1398), [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -31,6 +99,8 @@ - Use trybuild to compile subgraph binaries ([`46a8a2c`](https://github.com/hydro-project/hydroflow/commit/46a8a2cb08732bb21096e824bc4542d208c68fb2)) * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## v0.2.0 (2024-05-24) @@ -52,6 +122,7 @@ - 3 commits contributed to the release. + - 44 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1104](https://github.com/hydro-project/hydroflow/issues/1104), [#1192](https://github.com/hydro-project/hydroflow/issues/1192) @@ -96,6 +167,7 @@ - 6 commits contributed to the release. + - 71 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 4 unique issues were worked on: [#1083](https://github.com/hydro-project/hydroflow/issues/1083), [#1090](https://github.com/hydro-project/hydroflow/issues/1090), [#1098](https://github.com/hydro-project/hydroflow/issues/1098), [#1140](https://github.com/hydro-project/hydroflow/issues/1140) diff --git a/stageleft_tool/Cargo.toml b/stageleft_tool/Cargo.toml index bcaae60d8f7a..d8250cb5c3f3 100644 --- a/stageleft_tool/Cargo.toml +++ b/stageleft_tool/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "stageleft_tool" publish = true -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/stageleft_macro/" diff --git a/variadics/CHANGELOG.md b/variadics/CHANGELOG.md index 404186fd5224..6dafc30d06b7 100644 --- a/variadics/CHANGELOG.md +++ b/variadics/CHANGELOG.md @@ -5,8 +5,83 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.0.7 (2024-11-08) + +### Chore + + - update pinned rust version, clippy lints, remove some dead code + +### New Features + + - generalized hash trie indexes for relational tuples + Generalized Hash Tries are part of the SIGMOD '23 FreeJoin + [paper](https://dl.acm.org/doi/abs/10.1145/3589295) by + Wang/Willsey/Suciu. They provide a compressed ("factorized") + representation of relations. By operating in the factorized domain, join + algorithms can defer cross-products and achieve asymptotically optimal + performance. + + --------- + - additions to variadics including collection types + adds a number of features: + + collection types for variadics (sets, multisets) that allow search via + RefVars (variadic of refs) + into_option (convert a variadic to a variadic of options) + into_vec (convert a variadic to a variadic of vecs) + - additions to variadics including collection types + adds a number of features: + - collection types for variadics (sets, multisets) that allow search via + RefVars (variadic of refs) + - into_option (convert a variadic to a variadic of options) + - into_vec (convert a variadic to a variadic of vecs) + +### Style + + - fixes for nightly clippy + a couple few spurious `too_many_arguments` and a spurious + `zombie_processes` still on current nightly (`clippy 0.1.84 (4392847410 + 2024-10-21)`) + +### Test + + - ignore trybuild tests inconsistent on latest nightly + +### Commit Statistics + + + + - 7 commits contributed to the release. + - 69 days passed between releases. + - 6 commits were understood as [conventional](https://www.conventionalcommits.org). + - 6 unique issues were worked on: [#1444](https://github.com/hydro-project/hydroflow/issues/1444), [#1473](https://github.com/hydro-project/hydroflow/issues/1473), [#1474](https://github.com/hydro-project/hydroflow/issues/1474), [#1475](https://github.com/hydro-project/hydroflow/issues/1475), [#1503](https://github.com/hydro-project/hydroflow/issues/1503), [#1505](https://github.com/hydro-project/hydroflow/issues/1505) + +### Commit Details + + + +
view details + + * **[#1444](https://github.com/hydro-project/hydroflow/issues/1444)** + - Update pinned rust version, clippy lints, remove some dead code ([`d567760`](https://github.com/hydro-project/hydroflow/commit/d5677604e93c07a5392f4229af94a0b736eca382)) + * **[#1473](https://github.com/hydro-project/hydroflow/issues/1473)** + - Additions to variadics including collection types ([`8afd326`](https://github.com/hydro-project/hydroflow/commit/8afd3266dac43c04c3fc29065a13c9c9a6a55afe)) + * **[#1474](https://github.com/hydro-project/hydroflow/issues/1474)** + - Revert "feat: additions to variadics including collection types" ([`08c2af5`](https://github.com/hydro-project/hydroflow/commit/08c2af538821bbf460d2a52b4f0474082b5de7da)) + * **[#1475](https://github.com/hydro-project/hydroflow/issues/1475)** + - Additions to variadics including collection types ([`1c28259`](https://github.com/hydro-project/hydroflow/commit/1c2825942f8a326699a7fb68b5372b49918851b5)) + * **[#1503](https://github.com/hydro-project/hydroflow/issues/1503)** + - Generalized hash trie indexes for relational tuples ([`f7e740f`](https://github.com/hydro-project/hydroflow/commit/f7e740fb2ba36d0fcf3fd196d60333552911e3a4)) + * **[#1505](https://github.com/hydro-project/hydroflow/issues/1505)** + - Fixes for nightly clippy ([`47cb703`](https://github.com/hydro-project/hydroflow/commit/47cb703e771f7d1c451ceb9d185ada96410949da)) + * **Uncategorized** + - Ignore trybuild tests inconsistent on latest nightly ([`656ee32`](https://github.com/hydro-project/hydroflow/commit/656ee328c8710bce7370c851437a80ca3db46a5a)) +
+ ## 0.0.6 (2024-08-30) + + ### Chore - lower min dependency versions where possible, update `Cargo.lock` @@ -23,7 +98,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 2 commits contributed to the release. + - 3 commits contributed to the release. + - 38 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 2 unique issues were worked on: [#1367](https://github.com/hydro-project/hydroflow/issues/1367), [#1423](https://github.com/hydro-project/hydroflow/issues/1423) @@ -37,6 +113,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow `PartialEqVariadic::eq_ref` to take `AsRefVar`s with different lifetimes ([`43ff49d`](https://github.com/hydro-project/hydroflow/commit/43ff49d72789d78535717d2db04cf595cc511274)) * **[#1423](https://github.com/hydro-project/hydroflow/issues/1423)** - Lower min dependency versions where possible, update `Cargo.lock` ([`11af328`](https://github.com/hydro-project/hydroflow/commit/11af32828bab6e4a4264d2635ff71a12bb0bb778)) + * **Uncategorized** + - Release hydroflow_lang v0.9.0, hydroflow_datalog_core v0.9.0, hydroflow_datalog v0.9.0, hydroflow_deploy_integration v0.9.0, hydroflow_macro v0.9.0, lattices_macro v0.5.6, lattices v0.5.7, multiplatform_test v0.2.0, variadics v0.0.6, pusherator v0.0.8, hydroflow v0.9.0, stageleft_macro v0.3.0, stageleft v0.4.0, stageleft_tool v0.3.0, hydroflow_plus v0.9.0, hydro_deploy v0.9.0, hydro_cli v0.9.0, hydroflow_plus_deploy v0.9.0, safety bump 8 crates ([`0750117`](https://github.com/hydro-project/hydroflow/commit/0750117de7088c01a439b102adeb4c832889f171)) ## 0.0.5 (2024-07-23) @@ -65,6 +143,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 6 commits contributed to the release. + - 143 days passed between releases. - 5 commits were understood as [conventional](https://www.conventionalcommits.org). - 5 unique issues were worked on: [#1241](https://github.com/hydro-project/hydroflow/issues/1241), [#1245](https://github.com/hydro-project/hydroflow/issues/1245), [#1324](https://github.com/hydro-project/hydroflow/issues/1324), [#1325](https://github.com/hydro-project/hydroflow/issues/1325), [#1352](https://github.com/hydro-project/hydroflow/issues/1352) @@ -108,6 +187,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 4 commits contributed to the release. + - 32 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -149,6 +229,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 3 commits contributed to the release. + - 253 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#974](https://github.com/hydro-project/hydroflow/issues/974) @@ -178,6 +259,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 2 commits contributed to the release. + - 25 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#660](https://github.com/hydro-project/hydroflow/issues/660) diff --git a/variadics/Cargo.toml b/variadics/Cargo.toml index 182161686148..0198c897ba6f 100644 --- a/variadics/Cargo.toml +++ b/variadics/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "variadics" publish = true -version = "0.0.6" +version = "0.0.7" edition = "2021" license = "Apache-2.0" documentation = "https://docs.rs/variadics/" diff --git a/variadics_macro/CHANGELOG.md b/variadics_macro/CHANGELOG.md index e69de29bb2d1..737a1cf26547 100644 --- a/variadics_macro/CHANGELOG.md +++ b/variadics_macro/CHANGELOG.md @@ -0,0 +1,34 @@ + + +## v0.5.5 (2024-11-08) + +### New Features + + - generalized hash trie indexes for relational tuples + Generalized Hash Tries are part of the SIGMOD '23 FreeJoin + [paper](https://dl.acm.org/doi/abs/10.1145/3589295) by + Wang/Willsey/Suciu. They provide a compressed ("factorized") + representation of relations. By operating in the factorized domain, join + algorithms can defer cross-products and achieve asymptotically optimal + performance. + + --------- + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1503](https://github.com/hydro-project/hydroflow/issues/1503) + +### Commit Details + + + +
view details + + * **[#1503](https://github.com/hydro-project/hydroflow/issues/1503)** + - Generalized hash trie indexes for relational tuples ([`f7e740f`](https://github.com/hydro-project/hydroflow/commit/f7e740fb2ba36d0fcf3fd196d60333552911e3a4)) +
+ diff --git a/variadics_macro/Cargo.toml b/variadics_macro/Cargo.toml index 2c8ef8b0944c..df2ad67be93e 100644 --- a/variadics_macro/Cargo.toml +++ b/variadics_macro/Cargo.toml @@ -15,7 +15,7 @@ proc-macro2 = "1.0.63" proc-macro-crate = "1.1.0" quote = "1.0.0" syn = { version = "2.0.0", features = [ "full", "parsing", "visit-mut" ] } -variadics = { path = "../variadics", version = "^0.0.6" } +variadics = { path = "../variadics", version = "^0.0.7" } [dev-dependencies] insta = "1.7.1" From 5c53e12d6845f60c95248164eea3e2c48a8ff270 Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Fri, 8 Nov 2024 13:20:24 -0800 Subject: [PATCH 48/74] docs: `getLines` support named section markers, fix #1320 (#1555) Fix #1320 suggested in https://github.com/hydro-project/hydroflow/pull/1318#pullrequestreview-2137476550 --- .../quickstart/example_1_simplest.mdx | 8 ++-- docs/src/util.ts | 41 +++++++++++++++++-- hydroflow/examples/example_1_simplest.rs | 6 +++ 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/docs/docs/hydroflow/quickstart/example_1_simplest.mdx b/docs/docs/hydroflow/quickstart/example_1_simplest.mdx index 4a3f72c2bb34..394367386a0d 100644 --- a/docs/docs/hydroflow/quickstart/example_1_simplest.mdx +++ b/docs/docs/hydroflow/quickstart/example_1_simplest.mdx @@ -52,15 +52,15 @@ And then run the program: ## Understanding the Code Although this is a trivial program, it's useful to go through it line by line. -{getLines(exampleCode, 1)} +{getLines(exampleCode, 'use')} -This import gives you everything you need from Hydroflow to write code with Hydroflow's +This import gives you the macro you need from Hydroflow to write code in Hydroflow's [_surface syntax_](../syntax). Next, inside the main method we specify a flow by calling the `hydroflow_syntax!` macro. We assign the resulting `Hydroflow` instance to a mutable variable `flow`––mutable because we will be changing its status when we run it. -{getLines(exampleCode, 3, 6)} +{getLines(exampleCode, 'macro_call')} Hydroflow surface syntax defines a "flow" consisting of *operators* connected via `->` arrows. This simplest example uses a simple two-step linear flow. @@ -74,7 +74,7 @@ The Hydroflow surface syntax is merely a *specification*; it does not actually d until we run it. We can run this flow from within Rust via the [`run_available()` method](https://hydro-project.github.io/hydroflow/doc/hydroflow/scheduled/graph/struct.Hydroflow.html#method.run_available). -{getLines(exampleCode, 8)} +{getLines(exampleCode, 'run')} Note that `run_available()` runs the Hydroflow graph until no more work is immediately available. In this example flow, running the graph drains the iterator completely, so no diff --git a/docs/src/util.ts b/docs/src/util.ts index 4cd8eec79eb9..a1417166dcae 100644 --- a/docs/src/util.ts +++ b/docs/src/util.ts @@ -1,8 +1,41 @@ -/// Grabs the specified lines `[lineStart, lineEnd]` from the string. +/// Grabs the specified lines from the string. +/// +/// Can specify a line number range `lineStart, lineEnd`, a specific line number `lineNumber` (no +/// second arg), or a section name. /// /// Lines are one-indexed (start with `1`). Both `lineStart` and `lineEnd` are inclusive. -export function getLines(str: string, lineStart: number, lineEnd?: number): string { - let lines = str.split('\n').slice(lineStart - 1, lineEnd || lineStart); +/// +/// Sections are marked in the code with a start tag `//[mysection]//` and and end tag +/// `//[/mysection]//`. The rest of these tag lines must be whitespace, and these tag lines are not +/// included in the output. However they are included for line number _counting_. +export function getLines(str: string, sectionName: string): string; +export function getLines(str: string, lineStart: number, lineEnd?: number): string; +export function getLines(str: string, lineStartOrSectionName: number | string, lineEnd?: number): string { + // `//[section]//` or `//[/section]//` (rest of line must be whitespace). + const SECTION_REGEX = /^\s*\/\/\[(\/?)(\S+)\]\/\/\s*$/; + let lines; + if ('string' === typeof lineStartOrSectionName) { + let inSection = false; + lines = str + .split('\n') + .filter(line => { + const match = SECTION_REGEX.exec(line); + if (null == match) { + return inSection; + } + const [_, end, name] = match; + if (name == lineStartOrSectionName) { + inSection = 0 === end.length; + } + return false; + }) + } + else { + lines = str + .split('\n') + .slice(lineStartOrSectionName - 1, lineEnd || lineStartOrSectionName) // Select lines before removing section lines. + .filter(line => !SECTION_REGEX.test(line)); + } const leadingWhitespace = Math.min(...lines.filter(line => 0 !== line.length).map(line => line.search(/\S/)).map(Number)); if (0 < leadingWhitespace) { lines = lines.map(line => line.slice(leadingWhitespace)); @@ -40,7 +73,7 @@ ${stdOut}`; /// Extract the mermaid graph logged to stdout from the snapshots created by `surface_examples.rs`. export function extractMermaid(output: string): string { const outputLines = output.split('\n'); - // Delete the first four lines, which are the snapshot front matter. + // Delete the first four lines, which are the snapshot front matter. outputLines.splice(0, 4); // Mermaid graph starts with double-percent signs. if (!outputLines[0].startsWith('%%')) { diff --git a/hydroflow/examples/example_1_simplest.rs b/hydroflow/examples/example_1_simplest.rs index f93182cd7dd6..8f7b84b46140 100644 --- a/hydroflow/examples/example_1_simplest.rs +++ b/hydroflow/examples/example_1_simplest.rs @@ -1,9 +1,15 @@ +//[use]// use hydroflow::hydroflow_syntax; +//[/use]// +//[macro_call]// pub fn main() { let mut flow = hydroflow_syntax! { source_iter(0..10) -> for_each(|n| println!("Hello {}", n)); }; + //[/macro_call]// + //[run]// flow.run_available(); + //[/run]// } From c58f13cff39b838fa283fae2711501c8b7894ff4 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 8 Nov 2024 16:09:54 -0800 Subject: [PATCH 49/74] chore(docs): upgrade to Docusaurus v3 (#1558) Main breaking change is MDX parsing, which trips up on unescaped `<` in the generated docs, so we have to adjust the generator logic. --- docs/docusaurus.config.js | 6 +- docs/package-lock.json | 12420 ++++++++++++-------- docs/package.json | 26 +- docs/src/theme/prism-include-languages.js | 22 +- hydroflow_macro/build.rs | 16 +- 5 files changed, 7572 insertions(+), 4918 deletions(-) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 6ce7eea1c1be..3fe6ab3c9fd3 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -1,8 +1,8 @@ // @ts-check // Note: type annotations allow type checking and IDEs autocompletion -const lightCodeTheme = require('prism-react-renderer/themes/github'); -const darkCodeTheme = require('prism-react-renderer/themes/dracula'); +const lightCodeTheme = require('prism-react-renderer').themes.github; +const darkCodeTheme = require('prism-react-renderer').themes.dracula; const math = require('remark-math'); const katex = require('rehype-katex'); @@ -210,7 +210,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, - additionalLanguages: ['rust'], + additionalLanguages: ['rust', 'bash'], magicComments: [ { className: 'theme-code-block-highlighted-line', diff --git a/docs/package-lock.json b/docs/package-lock.json index e5db154843e3..315bd3fc8e6e 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,62 +8,58 @@ "name": "docs", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "^2.4.3", - "@docusaurus/plugin-ideal-image": "^2.4.3", - "@docusaurus/preset-classic": "^2.4.3", - "@docusaurus/theme-mermaid": "^2.4.3", - "@mdx-js/react": "^1.6.22", + "@docusaurus/core": "^3.6.1", + "@docusaurus/plugin-ideal-image": "^3.6.1", + "@docusaurus/preset-classic": "^3.6.1", + "@docusaurus/theme-mermaid": "^3.6.1", + "@mdx-js/react": "^3.1.0", "@monaco-editor/react": "^4.4.6", - "mermaid": "^9.3.0", - "prism-react-renderer": "^1.3.5", + "mermaid": "^11.4.0", + "prism-react-renderer": "^2.4.0", "raw-loader": "^4.0.2", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-katex": "^3.0.1", - "rehype-katex": "^5.0.0", - "remark-math": "^3.0.1", + "rehype-katex": "^7.0.1", + "remark-math": "^6.0.0", "website_playground": "file:../website_playground/pkg" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^2.4.3" + "@docusaurus/module-type-aliases": "^3.6.1" }, "engines": { "node": ">=16.14" } }, "../website_playground/pkg": { - "name": "website_playground", "version": "0.0.0" }, "node_modules/@algolia/autocomplete-core": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", - "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", - "license": "MIT", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.6.tgz", + "integrity": "sha512-lkDoW4I7h2kKlIgf3pUt1LqvxyYKkVyiypoGLlUnhPSnCpmeOwudM6rNq6YYsCmdQtnDQoW5lUNNuj6ASg3qeg==", "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-plugin-algolia-insights": "1.17.6", + "@algolia/autocomplete-shared": "1.17.6" } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", - "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", - "license": "MIT", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.6.tgz", + "integrity": "sha512-17NnaacuFzSWVuZu4NKzVeaFIe9Abpw8w+/gjc7xhZFtqj+GadufzodIdchwiB2eM2cDdiR3icW7gbNTB3K2YA==", "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-shared": "1.17.6" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", - "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", - "license": "MIT", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.6.tgz", + "integrity": "sha512-Cvg5JENdSCMuClwhJ1ON1/jSuojaYMiUW2KePm18IkdCzPJj/NXojaOxw58RFtQFpJgfVW8h2E8mEoDtLlMdeA==", "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-shared": "1.17.6" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -71,10 +67,9 @@ } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", - "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", - "license": "MIT", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.6.tgz", + "integrity": "sha512-aq/3V9E00Tw2GC/PqgyPGXtqJUlVc17v4cn1EUhSc+O/4zd04Uwb3UmPm8KDaYQQOrkt1lwvCj2vG2wRE5IKhw==", "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" @@ -84,7 +79,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz", "integrity": "sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==", - "license": "MIT", "dependencies": { "@algolia/cache-common": "4.24.0" } @@ -92,23 +86,34 @@ "node_modules/@algolia/cache-common": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.24.0.tgz", - "integrity": "sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==", - "license": "MIT" + "integrity": "sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==" }, "node_modules/@algolia/cache-in-memory": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.24.0.tgz", "integrity": "sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==", - "license": "MIT", "dependencies": { "@algolia/cache-common": "4.24.0" } }, + "node_modules/@algolia/client-abtesting": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.13.0.tgz", + "integrity": "sha512-6CoQjlMi1pmQYMQO8tXfuGxSPf6iKX5FP9MuMe6IWmvC81wwTvOehnwchyBl2wuPVhcw2Ar53K53mQ60DAC64g==", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/client-account": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.24.0.tgz", "integrity": "sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==", - "license": "MIT", "dependencies": { "@algolia/client-common": "4.24.0", "@algolia/client-search": "4.24.0", @@ -119,7 +124,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0", "@algolia/transporter": "4.24.0" @@ -129,7 +133,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", - "license": "MIT", "dependencies": { "@algolia/client-common": "4.24.0", "@algolia/requester-common": "4.24.0", @@ -140,7 +143,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.24.0.tgz", "integrity": "sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==", - "license": "MIT", "dependencies": { "@algolia/client-common": "4.24.0", "@algolia/client-search": "4.24.0", @@ -152,7 +154,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0", "@algolia/transporter": "4.24.0" @@ -162,7 +163,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", - "license": "MIT", "dependencies": { "@algolia/client-common": "4.24.0", "@algolia/requester-common": "4.24.0", @@ -170,11 +170,23 @@ } }, "node_modules/@algolia/client-common": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.2.5.tgz", - "integrity": "sha512-ITE85veJWwClnoNyv7Zydh9U0eKA82cDy8pLw+2hzL+zlzFIvV68ihGOEQ/kXt8N4v+R4MFzvsxnIpMruQzEug==", - "license": "MIT", - "peer": true, + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.13.0.tgz", + "integrity": "sha512-2SP6bGGWOTN920MLZv8s7yIR3OqY03vEe4U+vb2MGdL8a/8EQznF3L/nTC/rGf/hvEfZlX2tGFxPJaF2waravg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.13.0.tgz", + "integrity": "sha512-ldHTe+LVgC6L4Wr6doAQQ7Ku0jAdhaaPg1T+IHzmmiRZb2Uq5OsjW2yC65JifOmzPCiMkIZE2mGRpWgkn5ktlw==", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, "engines": { "node": ">= 14.0.0" } @@ -183,7 +195,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.24.0.tgz", "integrity": "sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==", - "license": "MIT", "dependencies": { "@algolia/client-common": "4.24.0", "@algolia/requester-common": "4.24.0", @@ -194,22 +205,34 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0", "@algolia/transporter": "4.24.0" } }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.13.0.tgz", + "integrity": "sha512-pYo0jbLUtPDN1r341UHTaF2fgN5rbaZfDZqjPRKPM+FRlRmxFxqFQm1UUfpkSUWYGn7lECwDpbKYiKUf81MTwA==", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/client-search": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.2.5.tgz", - "integrity": "sha512-OVDLzm5BEUbJmjfMm7b0Xx8vkK+NyEh7whPHuap2qy0x7RxQDLMXjiKsBbt1WNq+9nfX6+M/f2t0CJ8ENVuyYQ==", - "license": "MIT", - "peer": true, + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.13.0.tgz", + "integrity": "sha512-s2ge3uZ6Zg2sPSFibqijgEYsuorxcc8KVHg3I95nOPHvFHdnBtSHymhZvq4sp/fu8ijt/Y8jLwkuqm5myn+2Sg==", "dependencies": { - "@algolia/client-common": "5.2.5", - "@algolia/requester-browser-xhr": "5.2.5", - "@algolia/requester-node-http": "5.2.5" + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" }, "engines": { "node": ">= 14.0.0" @@ -218,29 +241,53 @@ "node_modules/@algolia/events": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", - "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==", - "license": "MIT" + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, + "node_modules/@algolia/ingestion": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.13.0.tgz", + "integrity": "sha512-fm5LEOe4FPDOc1D+M9stEs8hfcdmbdD+pt9og5shql6ueTZJANDbFoQhDOpiPJizR/ps1GwmjkWfUEywx3sV+Q==", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } }, "node_modules/@algolia/logger-common": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.24.0.tgz", - "integrity": "sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==", - "license": "MIT" + "integrity": "sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==" }, "node_modules/@algolia/logger-console": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.24.0.tgz", "integrity": "sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==", - "license": "MIT", "dependencies": { "@algolia/logger-common": "4.24.0" } }, + "node_modules/@algolia/monitoring": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.13.0.tgz", + "integrity": "sha512-e8Hshlnm2G5fapyUgWTBwhJ22yXcnLtPC4LWZKx7KOvv35GcdoHtlUBX94I/sWCJLraUr65JvR8qOo3LXC43dg==", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/recommend": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.24.0.tgz", "integrity": "sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==", - "license": "MIT", "dependencies": { "@algolia/cache-browser-local-storage": "4.24.0", "@algolia/cache-common": "4.24.0", @@ -259,7 +306,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0", "@algolia/transporter": "4.24.0" @@ -269,7 +315,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", - "license": "MIT", "dependencies": { "@algolia/client-common": "4.24.0", "@algolia/requester-common": "4.24.0", @@ -280,7 +325,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0" } @@ -289,19 +333,16 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.2.5.tgz", - "integrity": "sha512-Ri73PphNy1ceig94xJW9bPdN7uIYFAjpsABpp2Fsun4DmeZD5a4rMCNwwOXXsbC8h+lUzW34zpUf+h4Nk+eaqA==", - "license": "MIT", - "peer": true, + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.13.0.tgz", + "integrity": "sha512-NV6oSCt5lFuzfsVQoSBpewEWf/h4ySr7pv2bfwu9yF/jc/g39pig8+YpuqsxlRWBm/lTGVA2V0Ai9ySwrNumIA==", "dependencies": { - "@algolia/client-common": "5.2.5" + "@algolia/client-common": "5.13.0" }, "engines": { "node": ">= 14.0.0" @@ -310,17 +351,25 @@ "node_modules/@algolia/requester-common": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz", - "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==", - "license": "MIT" + "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==" + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.13.0.tgz", + "integrity": "sha512-094bK4rumf+rXJazxv3mq6eKRM0ep5AxIo8T0YmOdldswQt79apeufFiPLN19nHEWH22xR2FelimD+T/wRSP+Q==", + "dependencies": { + "@algolia/client-common": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } }, "node_modules/@algolia/requester-node-http": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.2.5.tgz", - "integrity": "sha512-/tTdEuWcWHSe/mGMomWkuaFDoRcpfl/jvGISVTPRq3pJvM1FPAzxlh2MXge6C30aUS9bxh3V0aWwgKFCilzyMQ==", - "license": "MIT", - "peer": true, + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.13.0.tgz", + "integrity": "sha512-JY5xhEYMgki53Wm+A6R2jUpOUdD0zZnBq+PC5R1TGMNOYL1s6JjDrJeMsvaI2YWxYMUSoCnRoltN/yf9RI8n3A==", "dependencies": { - "@algolia/client-common": "5.2.5" + "@algolia/client-common": "5.13.0" }, "engines": { "node": ">= 14.0.0" @@ -330,7 +379,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.24.0.tgz", "integrity": "sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==", - "license": "MIT", "dependencies": { "@algolia/cache-common": "4.24.0", "@algolia/logger-common": "4.24.0", @@ -338,24 +386,44 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz", + "integrity": "sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==", + "dependencies": { + "package-manager-detector": "^0.2.0", + "tinyexec": "^0.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", + "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "license": "MIT", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -363,33 +431,33 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.21.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz", - "integrity": "sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz", - "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helpers": "^7.21.5", - "@babel/parser": "^7.21.8", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -403,89 +471,83 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", - "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", - "license": "MIT", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dependencies": { - "@babel/types": "^7.25.6", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz", - "integrity": "sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", "dependencies": { - "@babel/types": "^7.21.5" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz", - "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz", - "integrity": "sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.21.5", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.21.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6", - "semver": "^6.3.0" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -498,19 +560,18 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz", - "integrity": "sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.3.1", - "semver": "^6.3.0" + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -523,135 +584,92 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" + "resolve": "^1.14.2" }, "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", - "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz", - "integrity": "sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dependencies": { - "@babel/types": "^7.21.5" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dependencies": { - "@babel/types": "^7.21.4" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz", - "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-simple-access": "^7.21.5", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", - "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -661,214 +679,143 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz", - "integrity": "sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-member-expression-to-functions": "^7.21.5", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", - "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", "dependencies": { - "@babel/types": "^7.21.5" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", - "dependencies": { - "@babel/types": "^7.20.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz", - "integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "license": "MIT", + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/types": "^7.26.0" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=4" + "node": ">=6.0.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" + "node": ">=6.9.0" }, - "engines": { - "node": ">=4" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", - "license": "MIT", + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dependencies": { - "@babel/types": "^7.25.6" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -878,13 +825,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -893,31 +840,25 @@ "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "engines": { "node": ">=6.9.0" }, @@ -925,29 +866,23 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@babel/core": "^7.12.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -956,13 +891,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -971,13 +905,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -986,13 +919,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1001,28 +933,27 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1031,16 +962,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1049,13 +978,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1064,14 +994,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1080,13 +1008,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1095,15 +1022,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", - "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1112,49 +1037,61 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.12.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1163,70 +1100,85 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1235,120 +1187,85 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz", - "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1357,14 +1274,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1373,12 +1289,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1387,34 +1305,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "globals": "^11.1.0" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1423,13 +1322,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz", - "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/template": "^7.20.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1438,27 +1337,27 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", - "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1467,12 +1366,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1481,13 +1380,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1496,12 +1394,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz", - "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==", + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1510,14 +1410,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1526,12 +1425,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1540,12 +1439,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1554,13 +1454,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1569,14 +1468,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz", - "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==", + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dependencies": { - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-simple-access": "^7.21.5" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1585,15 +1483,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1602,13 +1499,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1617,27 +1513,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz", + "integrity": "sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1646,13 +1527,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1661,12 +1541,16 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", - "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1675,12 +1559,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/plugin-transform-react-jsx": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1689,12 +1573,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz", - "integrity": "sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==", + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1703,12 +1588,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" }, "engines": { "node": ">=6.9.0" @@ -1717,30 +1603,27 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.5.tgz", - "integrity": "sha512-ELdlq61FpoEkHO6gFRpfj0kUgSwQTGoaEU8eMRoS8Dv3v6e7BjEAj5WMtIBRdHUeAioMhKP5HyxNzNnP+heKbA==", + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/types": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1749,13 +1632,17 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -1764,27 +1651,20 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz", - "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "regenerator-transform": "^0.15.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1793,17 +1673,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz", - "integrity": "sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==", + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dependencies": { - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1812,21 +1688,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1835,13 +1702,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1850,12 +1716,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1864,12 +1730,16 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", + "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1878,12 +1748,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1892,15 +1762,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", - "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1909,12 +1777,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz", - "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==", + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1923,102 +1792,95 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, "node_modules/@babel/preset-env": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.5.tgz", - "integrity": "sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==", - "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", - "@babel/plugin-proposal-async-generator-functions": "^7.20.7", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.21.0", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.21.0", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.21.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.21.5", - "@babel/plugin-transform-async-to-generator": "^7.20.7", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.21.0", - "@babel/plugin-transform-classes": "^7.21.0", - "@babel/plugin-transform-computed-properties": "^7.21.5", - "@babel/plugin-transform-destructuring": "^7.21.3", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.21.5", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.20.11", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-modules-systemjs": "^7.20.11", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.21.3", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.21.5", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.20.7", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.21.5", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.21.5", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "dependencies": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.38.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2031,37 +1893,34 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-react": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", - "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", + "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2071,15 +1930,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz", - "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", + "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-typescript": "^7.21.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-typescript": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2088,59 +1947,52 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.21.5.tgz", - "integrity": "sha512-FRqFlFKNazWYykft5zvzuEl1YyTDGsIRrjV9rvxvYkUC7W/ueBng1X68Xd6uRMzAaJ0xMKn08/wem5YS1lpX8w==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.0.tgz", + "integrity": "sha512-YXHu5lN8kJCb1LOb9PgV6pvak43X2h4HvRApcN5SdWeaItQOzfn1hgP6jasD6KWQyJDBxrVmA9o9OivlnNJK/w==", "dependencies": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.11" + "core-js-pure": "^3.30.2", + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", - "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.6", - "@babel/parser": "^7.25.6", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2149,23 +2001,55 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@braintree/sanitize-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz", - "integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==" + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.0.tgz", + "integrity": "sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg==" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==" }, "node_modules/@colors/colors": { "version": "1.5.0", @@ -2185,21 +2069,19 @@ } }, "node_modules/@docsearch/css": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.1.tgz", - "integrity": "sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==", - "license": "MIT" + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.7.0.tgz", + "integrity": "sha512-1OorbTwi1eeDmr0v5t+ckSRlt1zM5GHjm92iIl3kUu7im3GHuP+csf6E0WBg8pdXQczTWP9J9+o9n+Vg6DH5cQ==" }, "node_modules/@docsearch/react": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.1.tgz", - "integrity": "sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==", - "license": "MIT", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.7.0.tgz", + "integrity": "sha512-8e6tdDfkYoxafEEPuX5eE1h9cTkLvhe4KgoFkO5JCddXSQONnN1FHcDZRI4r8894eMpbYq6rdJF0dVYh8ikwNQ==", "dependencies": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.1", - "algoliasearch": "^4.19.1" + "@algolia/autocomplete-core": "1.17.6", + "@algolia/autocomplete-preset-algolia": "1.17.6", + "@docsearch/css": "3.7.0", + "algoliasearch": "^5.12.0" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", @@ -2222,185 +2104,291 @@ } } }, - "node_modules/@docusaurus/core": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.3.tgz", - "integrity": "sha512-dWH5P7cgeNSIg9ufReX6gaCl/TmrGKD38Orbwuz05WPhAQtFXHd5B8Qym1TiXfvUNvwoYKkAJOJuGe8ou0Z7PA==", - "license": "MIT", + "node_modules/@docsearch/react/node_modules/@algolia/client-analytics": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.13.0.tgz", + "integrity": "sha512-pS3qyXiWTwKnrt/jE79fqkNqZp7kjsFNlJDcBGkSWid74DNc6DmArlkvPqyLxnoaYGjUGACT6g56n7E3mVV2TA==", "dependencies": { - "@babel/core": "^7.18.6", - "@babel/generator": "^7.18.7", + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@docsearch/react/node_modules/@algolia/client-personalization": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.13.0.tgz", + "integrity": "sha512-RnCfOSN4OUJDuMNHFca2M8lY64Tmw0kQOZikge4TknTqHmlbKJb8IbJE7Rol79Z80W2Y+B1ydcjV7DPje4GMRA==", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@docsearch/react/node_modules/@algolia/recommend": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.13.0.tgz", + "integrity": "sha512-53/wW96oaj1FKMzGdFcZ/epygfTppLDUvgI1thLkd475EtVZCH3ZZVUNCEvf1AtnNyH1RnItkFzX8ayWCpx2PQ==", + "dependencies": { + "@algolia/client-common": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@docsearch/react/node_modules/algoliasearch": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.13.0.tgz", + "integrity": "sha512-04lyQX3Ev/oLYQx+aagamQDXvkUUfX1mwrLrus15+9fNaYj28GDxxEzbwaRfvmHFcZyoxvup7mMtDTTw8SrTEQ==", + "dependencies": { + "@algolia/client-abtesting": "5.13.0", + "@algolia/client-analytics": "5.13.0", + "@algolia/client-common": "5.13.0", + "@algolia/client-insights": "5.13.0", + "@algolia/client-personalization": "5.13.0", + "@algolia/client-query-suggestions": "5.13.0", + "@algolia/client-search": "5.13.0", + "@algolia/ingestion": "1.13.0", + "@algolia/monitoring": "1.13.0", + "@algolia/recommend": "5.13.0", + "@algolia/requester-browser-xhr": "5.13.0", + "@algolia/requester-fetch": "5.13.0", + "@algolia/requester-node-http": "5.13.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@docusaurus/babel": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.6.1.tgz", + "integrity": "sha512-JcKaunW8Ml2nTnfnvFc55T00Y+aCpNWnf1KY/gG+wWxHYDH0IdXOOz+k6NAlEAerW8+VYLfUqRIqHZ7N/DVXvQ==", + "dependencies": { + "@babel/core": "^7.25.9", + "@babel/generator": "^7.25.9", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.18.6", - "@babel/preset-env": "^7.18.6", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.18.6", - "@babel/runtime": "^7.18.6", - "@babel/runtime-corejs3": "^7.18.6", - "@babel/traverse": "^7.18.8", - "@docusaurus/cssnano-preset": "2.4.3", - "@docusaurus/logger": "2.4.3", - "@docusaurus/mdx-loader": "2.4.3", - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "2.4.3", - "@docusaurus/utils-common": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "@slorber/static-site-generator-webpack-plugin": "^4.0.7", - "@svgr/webpack": "^6.2.1", - "autoprefixer": "^10.4.7", - "babel-loader": "^8.2.5", + "@babel/plugin-transform-runtime": "^7.25.9", + "@babel/preset-env": "^7.25.9", + "@babel/preset-react": "^7.25.9", + "@babel/preset-typescript": "^7.25.9", + "@babel/runtime": "^7.25.9", + "@babel/runtime-corejs3": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@docusaurus/logger": "3.6.1", + "@docusaurus/utils": "3.6.1", "babel-plugin-dynamic-import-node": "^2.3.3", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@docusaurus/bundler": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.6.1.tgz", + "integrity": "sha512-vHSEx8Ku9x/gfIC6k4xb8J2nTxagLia0KvZkPZhxfkD1+n8i+Dj4BZPWTmv+kCA17RbgAvECG0XRZ0/ZEspQBQ==", + "dependencies": { + "@babel/core": "^7.25.9", + "@docusaurus/babel": "3.6.1", + "@docusaurus/cssnano-preset": "3.6.1", + "@docusaurus/logger": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils": "3.6.1", + "autoprefixer": "^10.4.14", + "babel-loader": "^9.2.1", + "clean-css": "^5.3.2", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.8.1", + "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", + "file-loader": "^6.2.0", + "html-minifier-terser": "^7.2.0", + "mini-css-extract-plugin": "^2.9.1", + "null-loader": "^4.0.1", + "postcss": "^8.4.26", + "postcss-loader": "^7.3.3", + "react-dev-utils": "^12.0.1", + "terser-webpack-plugin": "^5.3.9", + "tslib": "^2.6.0", + "url-loader": "^4.1.1", + "webpack": "^5.95.0", + "webpackbar": "^6.0.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "@docusaurus/faster": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/faster": { + "optional": true + } + } + }, + "node_modules/@docusaurus/core": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.6.1.tgz", + "integrity": "sha512-cDKxPihiM2z7G+4QtpTczS7uxNfNG6naSqM65OmAJET0CFRHbc9mDlLFtQF0lsVES91SHqfcGaaLZmi2FjdwWA==", + "dependencies": { + "@docusaurus/babel": "3.6.1", + "@docusaurus/bundler": "3.6.1", + "@docusaurus/logger": "3.6.1", + "@docusaurus/mdx-loader": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-common": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", - "clean-css": "^5.3.0", - "cli-table3": "^0.6.2", + "cli-table3": "^0.6.3", "combine-promises": "^1.1.0", "commander": "^5.1.0", - "copy-webpack-plugin": "^11.0.0", - "core-js": "^3.23.3", - "css-loader": "^6.7.1", - "css-minimizer-webpack-plugin": "^4.0.0", - "cssnano": "^5.1.12", + "core-js": "^3.31.1", "del": "^6.1.1", - "detect-port": "^1.3.0", + "detect-port": "^1.5.1", "escape-html": "^1.0.3", - "eta": "^2.0.0", - "file-loader": "^6.2.0", - "fs-extra": "^10.1.0", - "html-minifier-terser": "^6.1.0", - "html-tags": "^3.2.0", - "html-webpack-plugin": "^5.5.0", - "import-fresh": "^3.3.0", + "eta": "^2.2.0", + "eval": "^0.1.8", + "fs-extra": "^11.1.1", + "html-tags": "^3.3.1", + "html-webpack-plugin": "^5.6.0", "leven": "^3.1.0", "lodash": "^4.17.21", - "mini-css-extract-plugin": "^2.6.1", - "postcss": "^8.4.14", - "postcss-loader": "^7.0.0", + "p-map": "^4.0.0", "prompts": "^2.4.2", "react-dev-utils": "^12.0.1", "react-helmet-async": "^1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.3.3", + "react-router": "^5.3.4", "react-router-config": "^5.1.1", - "react-router-dom": "^5.3.3", + "react-router-dom": "^5.3.4", "rtl-detect": "^1.0.4", - "semver": "^7.3.7", - "serve-handler": "^6.1.3", + "semver": "^7.5.4", + "serve-handler": "^6.1.6", "shelljs": "^0.8.5", - "terser-webpack-plugin": "^5.3.3", - "tslib": "^2.4.0", - "update-notifier": "^5.1.0", - "url-loader": "^4.1.1", - "wait-on": "^6.0.1", - "webpack": "^5.73.0", - "webpack-bundle-analyzer": "^4.5.0", - "webpack-dev-server": "^4.9.3", - "webpack-merge": "^5.8.0", - "webpackbar": "^5.0.2" + "tslib": "^2.6.0", + "update-notifier": "^6.0.2", + "webpack": "^5.95.0", + "webpack-bundle-analyzer": "^4.10.2", + "webpack-dev-server": "^4.15.2", + "webpack-merge": "^6.0.1" }, "bin": { "docusaurus": "bin/docusaurus.mjs" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "@mdx-js/react": "^3.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.3.tgz", - "integrity": "sha512-ZvGSRCi7z9wLnZrXNPG6DmVPHdKGd8dIn9pYbEOFiYihfv4uDR3UtxogmKf+rT8ZlKFf5Lqne8E8nt08zNM8CA==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.6.1.tgz", + "integrity": "sha512-ZxYUmNeyQHW2w4/PJ7d07jQDuxzmKr9uPAQ6IVe5dTkeIeV0mDBB3jOLeJkNoI42Ru9JKEqQ9aVDtM9ct6QHnw==", "dependencies": { - "cssnano-preset-advanced": "^5.3.8", - "postcss": "^8.4.14", - "postcss-sort-media-queries": "^4.2.1", - "tslib": "^2.4.0" + "cssnano-preset-advanced": "^6.1.2", + "postcss": "^8.4.38", + "postcss-sort-media-queries": "^5.2.0", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" } }, "node_modules/@docusaurus/logger": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.4.3.tgz", - "integrity": "sha512-Zxws7r3yLufk9xM1zq9ged0YHs65mlRmtsobnFkdZTxWXdTYlWWLWdKyNKAsVC+D7zg+pv2fGbyabdOnyZOM3w==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.6.1.tgz", + "integrity": "sha512-OvetI/nnOMBSqCkUzKAQhnIjhxduECK4qTu3tq/8/h/qqvLsvKURojm04WPE54L+Uy+UXMas0hnbBJd8zDlEOw==", "dependencies": { "chalk": "^4.1.2", - "tslib": "^2.4.0" + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" } }, "node_modules/@docusaurus/lqip-loader": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/lqip-loader/-/lqip-loader-2.4.3.tgz", - "integrity": "sha512-hdumVOGbI4eiQQsZvbbosnm86FNkp23GikNanC0MJIIz8j3sCg8I0GEmg9nnVZor/2tE4ud5AWqjsVrx1CwcjA==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/lqip-loader/-/lqip-loader-3.6.1.tgz", + "integrity": "sha512-H/VVvnvFupFhQ81FuTyA/XHxEZPKh99T6Wg6KgN+/yvcn7869RdgrlDhKDnXZ7j2u80eFsVNjAcPfW1cSAtK6A==", "dependencies": { - "@docusaurus/logger": "2.4.3", + "@docusaurus/logger": "3.6.1", "file-loader": "^6.2.0", "lodash": "^4.17.21", - "sharp": "^0.30.7", - "tslib": "^2.4.0" + "sharp": "^0.32.3", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" } }, "node_modules/@docusaurus/mdx-loader": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.3.tgz", - "integrity": "sha512-b1+fDnWtl3GiqkL0BRjYtc94FZrcDDBV1j8446+4tptB9BAOlePwG2p/pK6vGvfL53lkOsszXMghr2g67M0vCw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.18.8", - "@babel/traverse": "^7.18.8", - "@docusaurus/logger": "2.4.3", - "@docusaurus/utils": "2.4.3", - "@mdx-js/mdx": "^1.6.22", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.6.1.tgz", + "integrity": "sha512-KPIsYi0S3X3/rNrW3V1fgOu5t6ahYWc31zTHHod8pacFxdmk9Uf6uuw+Jd6Cly1ilgal+41Ku+s0gmMuqKqiqg==", + "dependencies": { + "@docusaurus/logger": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "@mdx-js/mdx": "^3.0.0", + "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", + "estree-util-value-to-estree": "^3.0.1", "file-loader": "^6.2.0", - "fs-extra": "^10.1.0", - "image-size": "^1.0.1", - "mdast-util-to-string": "^2.0.0", - "remark-emoji": "^2.2.0", + "fs-extra": "^11.1.1", + "image-size": "^1.0.2", + "mdast-util-mdx": "^3.0.0", + "mdast-util-to-string": "^4.0.0", + "rehype-raw": "^7.0.0", + "remark-directive": "^3.0.0", + "remark-emoji": "^4.0.0", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.0", "stringify-object": "^3.3.0", - "tslib": "^2.4.0", - "unified": "^9.2.2", - "unist-util-visit": "^2.0.3", + "tslib": "^2.6.0", + "unified": "^11.0.3", + "unist-util-visit": "^5.0.0", "url-loader": "^4.1.1", - "webpack": "^5.73.0" + "vfile": "^6.0.1", + "webpack": "^5.88.1" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.3.tgz", - "integrity": "sha512-cwkBkt1UCiduuvEAo7XZY01dJfRn7UR/75mBgOdb1hKknhrabJZ8YH+7savd/y9kLExPyrhe0QwdS9GuzsRRIA==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.6.1.tgz", + "integrity": "sha512-J+q1jgm7TnEfVIUZImSFeLA1rghb6nwtoB9siHdcgKpDqFJ9/S7xhQL2aEKE7iZMZYzpu+2F390E9A7GkdEJNA==", "dependencies": { - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "2.4.3", + "@docusaurus/types": "3.6.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" }, "peerDependencies": { "react": "*", @@ -2408,193 +2396,190 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.3.tgz", - "integrity": "sha512-PVhypqaA0t98zVDpOeTqWUTvRqCEjJubtfFUQ7zJNYdbYTbS/E/ytq6zbLVsN/dImvemtO/5JQgjLxsh8XLo8Q==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/logger": "2.4.3", - "@docusaurus/mdx-loader": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils": "2.4.3", - "@docusaurus/utils-common": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "cheerio": "^1.0.0-rc.12", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.6.1.tgz", + "integrity": "sha512-FUmsn3xg/XD/K/4FQd8XHrs92aQdZO5LUtpHnRvO1/6DY87SMz6B6ERAN9IGQQld//M2/LVTHkZy8oVhQZQHIQ==", + "dependencies": { + "@docusaurus/core": "3.6.1", + "@docusaurus/logger": "3.6.1", + "@docusaurus/mdx-loader": "3.6.1", + "@docusaurus/theme-common": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-common": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", - "fs-extra": "^10.1.0", + "fs-extra": "^11.1.1", "lodash": "^4.17.21", "reading-time": "^1.5.0", - "tslib": "^2.4.0", - "unist-util-visit": "^2.0.3", + "srcset": "^4.0.0", + "tslib": "^2.6.0", + "unist-util-visit": "^5.0.0", "utility-types": "^3.10.0", - "webpack": "^5.73.0" + "webpack": "^5.88.1" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "@docusaurus/plugin-content-docs": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.3.tgz", - "integrity": "sha512-N7Po2LSH6UejQhzTCsvuX5NOzlC+HiXOVvofnEPj0WhMu1etpLEXE6a4aTxrtg95lQ5kf0xUIdjX9sh3d3G76A==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/logger": "2.4.3", - "@docusaurus/mdx-loader": "2.4.3", - "@docusaurus/module-type-aliases": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "@types/react-router-config": "^5.0.6", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.1.tgz", + "integrity": "sha512-Uq8kyn5DYCDmkUlB9sWChhWghS4lUFNiQU+RXcAXJ3qCVXsBpPsh6RF+npQG1N+j4wAbjydM1iLLJJzp+x3eMQ==", + "dependencies": { + "@docusaurus/core": "3.6.1", + "@docusaurus/logger": "3.6.1", + "@docusaurus/mdx-loader": "3.6.1", + "@docusaurus/module-type-aliases": "3.6.1", + "@docusaurus/theme-common": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-common": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", - "fs-extra": "^10.1.0", - "import-fresh": "^3.3.0", + "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", "lodash": "^4.17.21", - "tslib": "^2.4.0", + "tslib": "^2.6.0", "utility-types": "^3.10.0", - "webpack": "^5.73.0" + "webpack": "^5.88.1" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.3.tgz", - "integrity": "sha512-txtDVz7y3zGk67q0HjG0gRttVPodkHqE0bpJ+7dOaTH40CQFLSh7+aBeGnPOTl+oCPG+hxkim4SndqPqXjQ8Bg==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.6.1.tgz", + "integrity": "sha512-TZtL+2zq20gqGalzoIT2rEF1T4YCZ26jTvlCJXs78+incIajfdHtmdOq7rQW0oV7oqTjpGllbp788nY/vY9jgw==", "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/mdx-loader": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "fs-extra": "^10.1.0", - "tslib": "^2.4.0", - "webpack": "^5.73.0" + "@docusaurus/core": "3.6.1", + "@docusaurus/mdx-loader": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0", + "webpack": "^5.88.1" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-debug": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.4.3.tgz", - "integrity": "sha512-LkUbuq3zCmINlFb+gAd4ZvYr+bPAzMC0hwND4F7V9bZ852dCX8YoWyovVUBKq4er1XsOwSQaHmNGtObtn8Av8Q==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.6.1.tgz", + "integrity": "sha512-DeKPZtoVExDSYCbzoz7y5Dhc6+YPqRWfVGwEEUyKopSyQYefp0OV8hvASmbJCn2WyThRgspOUhog3FSEhz+agw==", "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils": "2.4.3", - "fs-extra": "^10.1.0", - "react-json-view": "^1.21.3", - "tslib": "^2.4.0" + "@docusaurus/core": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils": "3.6.1", + "fs-extra": "^11.1.1", + "react-json-view-lite": "^1.2.0", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.3.tgz", - "integrity": "sha512-KzBV3k8lDkWOhg/oYGxlK5o9bOwX7KpPc/FTWoB+SfKhlHfhq7qcQdMi1elAaVEIop8tgK6gD1E58Q+XC6otSQ==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.6.1.tgz", + "integrity": "sha512-ZEoERiDHxSfhaEeT35ukQ892NzGHWiUvfxUsnPiRuGEhMoQlxMSp60shBuSZ1sUKuZlndoEl5qAXJg09Wls/Sg==", "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "tslib": "^2.4.0" + "@docusaurus/core": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.3.tgz", - "integrity": "sha512-5FMg0rT7sDy4i9AGsvJC71MQrqQZwgLNdDetLEGDHLfSHLvJhQbTCUGbGXknUgWXQJckcV/AILYeJy+HhxeIFA==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.6.1.tgz", + "integrity": "sha512-u/E9vXUsZxYaV6Brvfee8NiH/iR0cMml9P/ifz4EpH/Jfxdbw8rbCT0Nm/h7EFgEY48Uqkl5huSbIvFB9n8aTQ==", "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "tslib": "^2.4.0" + "@docusaurus/core": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "@types/gtag.js": "^0.0.12", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.3.tgz", - "integrity": "sha512-1jTzp71yDGuQiX9Bi0pVp3alArV0LSnHXempvQTxwCGAEzUWWaBg4d8pocAlTpbP9aULQQqhgzrs8hgTRPOM0A==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.6.1.tgz", + "integrity": "sha512-By+NKkGYV8tSo8/RyS1OXikOtqsko5jJZ/uioJfBjsBGgSbiMJ+Y/HogFBke0mgSvf7NPGKZTbYm5+FJ8YUtPQ==", "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "tslib": "^2.4.0" + "@docusaurus/core": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-ideal-image": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-ideal-image/-/plugin-ideal-image-2.4.3.tgz", - "integrity": "sha512-cwnOKz5HwR/WwNL5lzGOWppyhaHQ2dPj1/x9hwv5VPwNmDDnWsYEwfBOTq8AYT27vFrYAH1tx9UX7QurRaIa4A==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-ideal-image/-/plugin-ideal-image-3.6.1.tgz", + "integrity": "sha512-hiGRPPlsM02aEOPlQc9rVnrckbVR6HswG7yDpZOtBEhw+ysXFsl/8gzAxFBL4ogKjN28WrlMCn/6IIkxY/EyOQ==", "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/lqip-loader": "2.4.3", + "@docusaurus/core": "3.6.1", + "@docusaurus/lqip-loader": "3.6.1", "@docusaurus/responsive-loader": "^1.7.0", - "@docusaurus/theme-translations": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "@endiliey/react-ideal-image": "^0.0.11", + "@docusaurus/theme-translations": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "@slorber/react-ideal-image": "^0.0.12", "react-waypoint": "^10.3.0", - "sharp": "^0.30.7", - "tslib": "^2.4.0", - "webpack": "^5.73.0" + "sharp": "^0.32.3", + "tslib": "^2.6.0", + "webpack": "^5.88.1" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { "jimp": "*", - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "peerDependenciesMeta": { "jimp": { @@ -2603,67 +2588,53 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.3.tgz", - "integrity": "sha512-LRQYrK1oH1rNfr4YvWBmRzTL0LN9UAPxBbghgeFRBm5yloF6P+zv1tm2pe2hQTX/QP5bSKdnajCvfnScgKXMZQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/logger": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils": "2.4.3", - "@docusaurus/utils-common": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "fs-extra": "^10.1.0", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.6.1.tgz", + "integrity": "sha512-i8R/GTKew4Cufb+7YQTwfPcNOhKTJzZ1VZ5OqQwI9c3pZK2TltQyhqKDVN94KCTbSSKvOYYytYfRAB2uPnH1/A==", + "dependencies": { + "@docusaurus/core": "3.6.1", + "@docusaurus/logger": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-common": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "fs-extra": "^11.1.1", "sitemap": "^7.1.1", - "tslib": "^2.4.0" + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/preset-classic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.4.3.tgz", - "integrity": "sha512-tRyMliepY11Ym6hB1rAFSNGwQDpmszvWYJvlK1E+md4SW8i6ylNHtpZjaYFff9Mdk3i/Pg8ItQq9P0daOJAvQw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/plugin-content-blog": "2.4.3", - "@docusaurus/plugin-content-docs": "2.4.3", - "@docusaurus/plugin-content-pages": "2.4.3", - "@docusaurus/plugin-debug": "2.4.3", - "@docusaurus/plugin-google-analytics": "2.4.3", - "@docusaurus/plugin-google-gtag": "2.4.3", - "@docusaurus/plugin-google-tag-manager": "2.4.3", - "@docusaurus/plugin-sitemap": "2.4.3", - "@docusaurus/theme-classic": "2.4.3", - "@docusaurus/theme-common": "2.4.3", - "@docusaurus/theme-search-algolia": "2.4.3", - "@docusaurus/types": "2.4.3" - }, - "engines": { - "node": ">=16.14" - }, - "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" - } - }, - "node_modules/@docusaurus/react-loadable": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", - "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", - "dependencies": { - "@types/react": "*", - "prop-types": "^15.6.2" + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.6.1.tgz", + "integrity": "sha512-b90Y1XRH9e+oa/E3NmiFEFOwgYUd+knFcZUy81nM3FJs038WbEA0T55NQsuPW0s7nOsCShQ7dVFyKxV+Wp31Nw==", + "dependencies": { + "@docusaurus/core": "3.6.1", + "@docusaurus/plugin-content-blog": "3.6.1", + "@docusaurus/plugin-content-docs": "3.6.1", + "@docusaurus/plugin-content-pages": "3.6.1", + "@docusaurus/plugin-debug": "3.6.1", + "@docusaurus/plugin-google-analytics": "3.6.1", + "@docusaurus/plugin-google-gtag": "3.6.1", + "@docusaurus/plugin-google-tag-manager": "3.6.1", + "@docusaurus/plugin-sitemap": "3.6.1", + "@docusaurus/theme-classic": "3.6.1", + "@docusaurus/theme-common": "3.6.1", + "@docusaurus/theme-search-algolia": "3.6.1", + "@docusaurus/types": "3.6.1" + }, + "engines": { + "node": ">=18.0" }, "peerDependencies": { - "react": "*" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/responsive-loader": { @@ -2690,246 +2661,227 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.4.3.tgz", - "integrity": "sha512-QKRAJPSGPfDY2yCiPMIVyr+MqwZCIV2lxNzqbyUW0YkrlmdzzP3WuQJPMGLCjWgQp/5c9kpWMvMxjhpZx1R32Q==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/mdx-loader": "2.4.3", - "@docusaurus/module-type-aliases": "2.4.3", - "@docusaurus/plugin-content-blog": "2.4.3", - "@docusaurus/plugin-content-docs": "2.4.3", - "@docusaurus/plugin-content-pages": "2.4.3", - "@docusaurus/theme-common": "2.4.3", - "@docusaurus/theme-translations": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils": "2.4.3", - "@docusaurus/utils-common": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "@mdx-js/react": "^1.6.22", - "clsx": "^1.2.1", - "copy-text-to-clipboard": "^3.0.1", - "infima": "0.2.0-alpha.43", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.6.1.tgz", + "integrity": "sha512-5lVUmIXk7zp+n9Ki2lYWrmhbd6mssOlKCnnDJvY4QDi3EgjRisIu5g4yKXoWTIbiqE7m7q/dS9cbeShEtfkKng==", + "dependencies": { + "@docusaurus/core": "3.6.1", + "@docusaurus/logger": "3.6.1", + "@docusaurus/mdx-loader": "3.6.1", + "@docusaurus/module-type-aliases": "3.6.1", + "@docusaurus/plugin-content-blog": "3.6.1", + "@docusaurus/plugin-content-docs": "3.6.1", + "@docusaurus/plugin-content-pages": "3.6.1", + "@docusaurus/theme-common": "3.6.1", + "@docusaurus/theme-translations": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-common": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "copy-text-to-clipboard": "^3.2.0", + "infima": "0.2.0-alpha.45", "lodash": "^4.17.21", "nprogress": "^0.2.0", - "postcss": "^8.4.14", - "prism-react-renderer": "^1.3.5", - "prismjs": "^1.28.0", - "react-router-dom": "^5.3.3", - "rtlcss": "^3.5.0", - "tslib": "^2.4.0", + "postcss": "^8.4.26", + "prism-react-renderer": "^2.3.0", + "prismjs": "^1.29.0", + "react-router-dom": "^5.3.4", + "rtlcss": "^4.1.0", + "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/theme-common": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.4.3.tgz", - "integrity": "sha512-7KaDJBXKBVGXw5WOVt84FtN8czGWhM0lbyWEZXGp8AFfL6sZQfRTluFp4QriR97qwzSyOfQb+nzcDZZU4tezUw==", - "license": "MIT", - "dependencies": { - "@docusaurus/mdx-loader": "2.4.3", - "@docusaurus/module-type-aliases": "2.4.3", - "@docusaurus/plugin-content-blog": "2.4.3", - "@docusaurus/plugin-content-docs": "2.4.3", - "@docusaurus/plugin-content-pages": "2.4.3", - "@docusaurus/utils": "2.4.3", - "@docusaurus/utils-common": "2.4.3", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.6.1.tgz", + "integrity": "sha512-18iEYNpMvarGfq9gVRpGowSZD24vZ39Iz4acqaj64180i54V9el8tVnhNr/wRvrUm1FY30A1NHLqnMnDz4rYEQ==", + "dependencies": { + "@docusaurus/mdx-loader": "3.6.1", + "@docusaurus/module-type-aliases": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-common": "3.6.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", - "clsx": "^1.2.1", + "clsx": "^2.0.0", "parse-numeric-range": "^1.3.0", - "prism-react-renderer": "^1.3.5", - "tslib": "^2.4.0", - "use-sync-external-store": "^1.2.0", + "prism-react-renderer": "^2.3.0", + "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "@docusaurus/plugin-content-docs": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/theme-mermaid": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-2.4.3.tgz", - "integrity": "sha512-S1tZ3xpowtFiTrpTKmvVbRHUYGOlEG5CnPzWlO4huJT1sAwLR+pD6f9DYUlPv2+9NezF3EfUrUyW9xLH0UP58w==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.6.1.tgz", + "integrity": "sha512-ke00/VSFibzucbr64JXwPWsiu66zcqI8mnEbbmPSV1Yby5FRsfGQqcE+1cvUkAOVCl+zX8RNjv8vrRb4ilQDLQ==", "dependencies": { - "@docusaurus/core": "2.4.3", - "@docusaurus/module-type-aliases": "2.4.3", - "@docusaurus/theme-common": "2.4.3", - "@docusaurus/types": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "@mdx-js/react": "^1.6.22", - "mermaid": "^9.2.2", - "tslib": "^2.4.0" + "@docusaurus/core": "3.6.1", + "@docusaurus/module-type-aliases": "3.6.1", + "@docusaurus/theme-common": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "mermaid": ">=10.4", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.3.tgz", - "integrity": "sha512-jziq4f6YVUB5hZOB85ELATwnxBz/RmSLD3ksGQOLDPKVzat4pmI8tddNWtriPpxR04BNT+ZfpPUMFkNFetSW1Q==", - "license": "MIT", - "dependencies": { - "@docsearch/react": "^3.1.1", - "@docusaurus/core": "2.4.3", - "@docusaurus/logger": "2.4.3", - "@docusaurus/plugin-content-docs": "2.4.3", - "@docusaurus/theme-common": "2.4.3", - "@docusaurus/theme-translations": "2.4.3", - "@docusaurus/utils": "2.4.3", - "@docusaurus/utils-validation": "2.4.3", - "algoliasearch": "^4.13.1", - "algoliasearch-helper": "^3.10.0", - "clsx": "^1.2.1", - "eta": "^2.0.0", - "fs-extra": "^10.1.0", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.6.1.tgz", + "integrity": "sha512-BjmuiFRpQP1WEm8Mzu1Bb0Wdas6G65VHXDDNr7XTKgbstxalE6vuxt0ioXTDFS2YVep5748aVhKvnxR9gm2Liw==", + "dependencies": { + "@docsearch/react": "^3.5.2", + "@docusaurus/core": "3.6.1", + "@docusaurus/logger": "3.6.1", + "@docusaurus/plugin-content-docs": "3.6.1", + "@docusaurus/theme-common": "3.6.1", + "@docusaurus/theme-translations": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-validation": "3.6.1", + "algoliasearch": "^4.18.0", + "algoliasearch-helper": "^3.13.3", + "clsx": "^2.0.0", + "eta": "^2.2.0", + "fs-extra": "^11.1.1", "lodash": "^4.17.21", - "tslib": "^2.4.0", + "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/theme-translations": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.3.tgz", - "integrity": "sha512-H4D+lbZbjbKNS/Zw1Lel64PioUAIT3cLYYJLUf3KkuO/oc9e0QCVhIYVtUI2SfBCF2NNdlyhBDQEEMygsCedIg==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.6.1.tgz", + "integrity": "sha512-bNm5G6sueUezvyhsBegA1wwM38yW0BnqpZTE9KHO2yKnkERNMaV5x/yPJ/DNCOHjJtCcJ5Uz55g2AS75Go31xA==", "dependencies": { - "fs-extra": "^10.1.0", - "tslib": "^2.4.0" + "fs-extra": "^11.1.1", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" } }, "node_modules/@docusaurus/types": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.3.tgz", - "integrity": "sha512-W6zNLGQqfrp/EoPD0bhb9n7OobP+RHpmvVzpA+Z/IuU3Q63njJM24hmT0GYboovWcDtFmnIJC9wcyx4RVPQscw==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.6.1.tgz", + "integrity": "sha512-hCB1hj9DYutVYBisnPNobz9SzEmCcf1EetJv09O49Cov3BqOkm+vnnjB3d957YJMtpLGQoKBeN/FF1DZ830JwQ==", "dependencies": { + "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", "@types/react": "*", "commander": "^5.1.0", - "joi": "^17.6.0", + "joi": "^17.9.2", "react-helmet-async": "^1.3.0", "utility-types": "^3.10.0", - "webpack": "^5.73.0", - "webpack-merge": "^5.8.0" + "webpack": "^5.95.0", + "webpack-merge": "^5.9.0" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/types/node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" } }, "node_modules/@docusaurus/utils": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.3.tgz", - "integrity": "sha512-fKcXsjrD86Smxv8Pt0TBFqYieZZCPh4cbf9oszUq/AMhZn3ujwpKaVYZACPX8mmjtYx0JOgNx52CREBfiGQB4A==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.6.1.tgz", + "integrity": "sha512-nS3WCvepwrnBEgSG5vQu40XG95lC9Jeh/odV5u5IhU1eQFEGDst9xBi6IK5yZdsGvbuaXBZLZtOqWYtuuFa/rQ==", "dependencies": { - "@docusaurus/logger": "2.4.3", - "@svgr/webpack": "^6.2.1", + "@docusaurus/logger": "3.6.1", + "@docusaurus/types": "3.6.1", + "@docusaurus/utils-common": "3.6.1", + "@svgr/webpack": "^8.1.0", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", - "fs-extra": "^10.1.0", - "github-slugger": "^1.4.0", + "fs-extra": "^11.1.1", + "github-slugger": "^1.5.0", "globby": "^11.1.0", "gray-matter": "^4.0.3", + "jiti": "^1.20.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "micromatch": "^4.0.5", + "prompts": "^2.4.2", "resolve-pathname": "^3.0.0", "shelljs": "^0.8.5", - "tslib": "^2.4.0", + "tslib": "^2.6.0", "url-loader": "^4.1.1", - "webpack": "^5.73.0" + "utility-types": "^3.10.0", + "webpack": "^5.88.1" }, "engines": { - "node": ">=16.14" - }, - "peerDependencies": { - "@docusaurus/types": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/types": { - "optional": true - } + "node": ">=18.0" } }, "node_modules/@docusaurus/utils-common": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.3.tgz", - "integrity": "sha512-/jascp4GbLQCPVmcGkPzEQjNaAk3ADVfMtudk49Ggb+131B1WDD6HqlSmDf8MxGdy7Dja2gc+StHf01kiWoTDQ==", - "license": "MIT", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.6.1.tgz", + "integrity": "sha512-LX1qiTiC0aS8c92uZ+Wj2iNCNJyYZJIKY8/nZDKNMBfo759VYVS3RX3fKP3DznB+16sYp7++MyCz/T6fOGaRfw==", "dependencies": { - "tslib": "^2.4.0" + "@docusaurus/types": "3.6.1", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" - }, - "peerDependencies": { - "@docusaurus/types": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/types": { - "optional": true - } + "node": ">=18.0" } }, "node_modules/@docusaurus/utils-validation": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.3.tgz", - "integrity": "sha512-G2+Vt3WR5E/9drAobP+hhZQMaswRwDlp6qOMi7o7ZypB+VO7N//DZWhZEwhcRGepMDJGQEwtPv7UxtYwPL9PBw==", - "license": "MIT", - "dependencies": { - "@docusaurus/logger": "2.4.3", - "@docusaurus/utils": "2.4.3", - "joi": "^17.6.0", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.6.1.tgz", + "integrity": "sha512-+iMd6zRl5cJQm7nUP+7pSO/oAXsN79eHO34ME7l2YJt4GEAr70l5kkD58u2jEPpp+wSXT70c7x2A2lzJI1E8jw==", + "dependencies": { + "@docusaurus/logger": "3.6.1", + "@docusaurus/utils": "3.6.1", + "@docusaurus/utils-common": "3.6.1", + "fs-extra": "^11.2.0", + "joi": "^17.9.2", "js-yaml": "^4.1.0", - "tslib": "^2.4.0" + "lodash": "^4.17.21", + "tslib": "^2.6.0" }, "engines": { - "node": ">=16.14" - } - }, - "node_modules/@endiliey/react-ideal-image": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@endiliey/react-ideal-image/-/react-ideal-image-0.0.11.tgz", - "integrity": "sha512-QxMjt/Gvur/gLxSoCy7VIyGGGrGmDN+VHcXkN3R2ApoWX0EYUE+hMgPHSW/PV6VVebZ1Nd4t2UnGRBDihu16JQ==", - "engines": { - "node": ">= 8.9.0", - "npm": "> 3" - }, - "peerDependencies": { - "prop-types": ">=15", - "react": ">=0.14.x", - "react-waypoint": ">=9.0.2" + "node": ">=18.0" } }, "node_modules/@hapi/hoek": { @@ -2945,23 +2897,42 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, + "node_modules/@iconify/utils": { + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.33.tgz", + "integrity": "sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==", + "dependencies": { + "@antfu/install-pkg": "^0.4.0", + "@antfu/utils": "^0.7.10", + "@iconify/types": "^2.0.0", + "debug": "^4.3.6", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.0", + "mlly": "^1.7.1" + } + }, "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dependencies": { - "@sinclair/typebox": "^0.25.16" + "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -2976,7 +2947,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -2987,9 +2957,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "engines": { "node": ">=6.0.0" } @@ -2998,170 +2968,101 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" }, "node_modules/@mdx-js/mdx": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", - "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", - "dependencies": { - "@babel/core": "7.12.9", - "@babel/plugin-syntax-jsx": "7.12.1", - "@babel/plugin-syntax-object-rest-spread": "7.8.3", - "@mdx-js/util": "1.6.22", - "babel-plugin-apply-mdx-type-prop": "1.6.22", - "babel-plugin-extract-import-names": "1.6.22", - "camelcase-css": "2.0.1", - "detab": "2.0.4", - "hast-util-raw": "6.0.1", - "lodash.uniq": "4.5.0", - "mdast-util-to-hast": "10.0.1", - "remark-footnotes": "2.0.0", - "remark-mdx": "1.6.22", - "remark-parse": "8.0.3", - "remark-squeeze-paragraphs": "4.0.0", - "style-to-object": "0.3.0", - "unified": "9.2.0", - "unist-builder": "2.0.3", - "unist-util-visit": "2.0.3" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", + "integrity": "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/@mdx-js/mdx/node_modules/@babel/core": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", - "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.7", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.9", - "@babel/types": "^7.12.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@mdx-js/mdx/node_modules/@babel/plugin-syntax-jsx": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", - "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@mdx-js/mdx/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@mdx-js/mdx/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@mdx-js/mdx/node_modules/unified": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", - "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "node_modules/@mdx-js/react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", + "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", "dependencies": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" + "@types/mdx": "^2.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", - "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" }, "peerDependencies": { - "react": "^16.13.1 || ^17.0.0" + "@types/react": ">=16", + "react": ">=16" } }, - "node_modules/@mdx-js/util": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", - "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/@mermaid-js/parser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz", + "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==", + "dependencies": { + "langium": "3.0.0" } }, "node_modules/@monaco-editor/loader": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.3.3.tgz", - "integrity": "sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", + "integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==", "dependencies": { "state-local": "^1.0.6" }, @@ -3170,11 +3071,11 @@ } }, "node_modules/@monaco-editor/react": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.5.1.tgz", - "integrity": "sha512-NNDFdP+2HojtNhCkRfE6/D6ro6pBNihaOzMbGK84lNWzRu+CfBjwzGt4jmnqimLuqp5yE5viHS2vi+QOAnD5FQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz", + "integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==", "dependencies": { - "@monaco-editor/loader": "^1.3.3" + "@monaco-editor/loader": "^1.4.0" }, "peerDependencies": { "monaco-editor": ">= 0.25.0 < 1", @@ -3214,15 +3115,52 @@ "node": ">= 8" } }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@polka/url": { - "version": "1.0.0-next.21", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", - "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==" }, "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", "dependencies": { "@hapi/hoek": "^9.0.0" } @@ -3238,37 +3176,51 @@ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==" + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" }, "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@slorber/static-site-generator-webpack-plugin": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", - "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", - "dependencies": { - "eval": "^0.1.8", - "p-map": "^4.0.0", - "webpack-sources": "^3.2.2" - }, + "node_modules/@slorber/react-ideal-image": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@slorber/react-ideal-image/-/react-ideal-image-0.0.12.tgz", + "integrity": "sha512-u8KiDTEkMA7/KAeA5ywg/P7YG4zuKhWtswfVZDH8R8HXgQsFcHIYU2WaQnGuK/Du7Wdj90I+SdFmajSGFRvoKA==", "engines": { - "node": ">=14" + "node": ">= 8.9.0", + "npm": "> 3" + }, + "peerDependencies": { + "prop-types": ">=15", + "react": ">=0.14.x", + "react-waypoint": ">=9.0.2" + } + }, + "node_modules/@slorber/remark-comment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", + "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.1.0", + "micromark-util-symbol": "^1.0.1" } }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", - "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3309,11 +3261,11 @@ } }, "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", - "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3324,11 +3276,11 @@ } }, "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", - "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3339,11 +3291,11 @@ } }, "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", - "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3354,11 +3306,11 @@ } }, "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", - "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3369,9 +3321,9 @@ } }, "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", - "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", "engines": { "node": ">=12" }, @@ -3384,21 +3336,21 @@ } }, "node_modules/@svgr/babel-preset": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", - "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", - "@svgr/babel-plugin-remove-jsx-attribute": "*", - "@svgr/babel-plugin-remove-jsx-empty-expression": "*", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", - "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", - "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", - "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", - "@svgr/babel-plugin-transform-svg-component": "^6.5.1" + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3409,18 +3361,18 @@ } }, "node_modules/@svgr/core": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", - "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dependencies": { - "@babel/core": "^7.19.6", - "@svgr/babel-preset": "^6.5.1", - "@svgr/plugin-jsx": "^6.5.1", + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.1" + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3428,15 +3380,15 @@ } }, "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", - "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", "dependencies": { - "@babel/types": "^7.20.0", + "@babel/types": "^7.21.3", "entities": "^4.4.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3444,37 +3396,37 @@ } }, "node_modules/@svgr/plugin-jsx": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", - "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", "dependencies": { - "@babel/core": "^7.19.6", - "@svgr/babel-preset": "^6.5.1", - "@svgr/hast-util-to-babel-ast": "^6.5.1", + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", "svg-parser": "^2.0.4" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", "url": "https://github.com/sponsors/gregberge" }, "peerDependencies": { - "@svgr/core": "^6.0.0" + "@svgr/core": "*" } }, "node_modules/@svgr/plugin-svgo": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", - "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", "dependencies": { - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "svgo": "^2.8.0" + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3485,21 +3437,21 @@ } }, "node_modules/@svgr/webpack": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", - "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", "dependencies": { - "@babel/core": "^7.19.6", - "@babel/plugin-transform-react-constant-elements": "^7.18.12", - "@babel/preset-env": "^7.19.4", + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.18.6", - "@svgr/core": "^6.5.1", - "@svgr/plugin-jsx": "^6.5.1", - "@svgr/plugin-svgo": "^6.5.1" + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3507,14 +3459,14 @@ } }, "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dependencies": { - "defer-to-connect": "^1.0.1" + "defer-to-connect": "^2.0.1" }, "engines": { - "node": ">=6" + "node": ">=14.16" } }, "node_modules/@trysound/sax": { @@ -3525,175 +3477,497 @@ "node": ">=10.13.0" } }, + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" } }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "license": "MIT" + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" + "@types/d3-selection": "*" } }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "@types/d3-selection": "*" } }, - "node_modules/@types/hast": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", - "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", "dependencies": { - "@types/unist": "*" + "@types/d3-array": "*", + "@types/geojson": "*" } }, - "node_modules/@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==" }, - "node_modules/@types/http-proxy": { - "version": "1.17.11", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", - "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", "dependencies": { - "@types/node": "*" + "@types/d3-selection": "*" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dependencies": { + "@types/trusted-types": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "node_modules/@types/gtag.js": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", + "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/katex": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.11.1.tgz", - "integrity": "sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg==" + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==" }, "node_modules/@types/mdast": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", - "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "dependencies": { "@types/unist": "*" } }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==" + }, "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.1.tgz", - "integrity": "sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg==" + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, - "node_modules/@types/parse5": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", - "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==" }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==" }, "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/react": { - "version": "18.2.6", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.6.tgz", - "integrity": "sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==", + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, @@ -3707,9 +3981,9 @@ } }, "node_modules/@types/react-router-config": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.7.tgz", - "integrity": "sha512-pFFVXUIydHlcJP6wJm7sDii5mD/bCmmAY0wQzq+M+uX7bqS95AQqHZWP1iNMKrWVQSuHIzj5qi9BvrtLX2/T4w==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.11.tgz", + "integrity": "sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==", "dependencies": { "@types/history": "^4.7.11", "@types/react": "*", @@ -3735,233 +4009,221 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dependencies": { "@types/express": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dependencies": { - "@types/mime": "*", - "@types/node": "*" + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" } }, "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, "node_modules/@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, "node_modules/@types/ws": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", - "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", - "license": "MIT", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "license": "MIT" + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "license": "MIT" + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "license": "MIT" + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "license": "MIT", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "license": "MIT" + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", - "license": "MIT", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "license": "MIT", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "license": "Apache-2.0", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "license": "MIT" + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", - "license": "MIT", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", - "license": "MIT", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", - "license": "MIT", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", - "license": "MIT", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", - "license": "MIT", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause" + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0" + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "node_modules/accepts": { "version": "1.3.8", @@ -3994,10 +4256,18 @@ "node": ">= 0.6" } }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "bin": { "acorn": "bin/acorn" }, @@ -4005,19 +4275,21 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "peerDependencies": { - "acorn": "^8" + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } @@ -4043,14 +4315,14 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -4073,39 +4345,21 @@ } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "peerDependencies": { - "ajv": "^6.9.1" + "ajv": "^8.8.2" } }, "node_modules/algoliasearch": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.24.0.tgz", "integrity": "sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==", - "license": "MIT", "dependencies": { "@algolia/cache-browser-local-storage": "4.24.0", "@algolia/cache-common": "4.24.0", @@ -4125,10 +4379,9 @@ } }, "node_modules/algoliasearch-helper": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.4.tgz", - "integrity": "sha512-fvBCywguW9f+939S6awvRMstqMF1XXcd2qs1r1aGqL/PJ1go/DqN06tWmDVmhCDqBJanm++imletrQWf0G2S1g==", - "license": "MIT", + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.5.tgz", + "integrity": "sha512-lWvhdnc+aKOKx8jyA3bsdEgHzm/sglC4cYdMG4xSQyRiPLJVJtH/IVYZG3Hp6PkTEhQqhyVYkeP9z2IlcHJsWw==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -4140,7 +4393,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0", "@algolia/transporter": "4.24.0" @@ -4150,7 +4402,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", - "license": "MIT", "dependencies": { "@algolia/client-common": "4.24.0", "@algolia/requester-common": "4.24.0", @@ -4161,7 +4412,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0" } @@ -4170,7 +4420,6 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", - "license": "MIT", "dependencies": { "@algolia/requester-common": "4.24.0" } @@ -4201,6 +4450,31 @@ "node": ">=8" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-html-community": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", @@ -4249,8 +4523,7 @@ "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "node_modules/argparse": { "version": "2.0.1", @@ -4258,9 +4531,9 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/array-union": { "version": "2.1.0", @@ -4270,11 +4543,13 @@ "node": ">=8" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "bin": { + "astring": "bin/astring" + } }, "node_modules/at-least-node": { "version": "1.0.0", @@ -4285,9 +4560,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "funding": [ { "type": "opencollective", @@ -4296,14 +4571,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -4316,53 +4595,27 @@ "postcss": "^8.1.0" } }, - "node_modules/axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", - "dependencies": { - "follow-redirects": "^1.14.7" - } + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" }, "node_modules/babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-plugin-apply-mdx-type-prop": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", - "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", - "dependencies": { - "@babel/helper-plugin-utils": "7.10.4", - "@mdx-js/util": "1.6.22" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node": ">= 14.15.0" }, "peerDependencies": { - "@babel/core": "^7.11.6" + "@babel/core": "^7.12.0", + "webpack": ">=5" } }, - "node_modules/babel-plugin-apply-mdx-type-prop/node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, "node_modules/babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -4371,72 +4624,54 @@ "object.assign": "^4.1.0" } }, - "node_modules/babel-plugin-extract-import-names": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", - "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", "dependencies": { - "@babel/helper-plugin-utils": "7.10.4" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/babel-plugin-extract-import-names/node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -4447,11 +4682,46 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base16": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", - "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", - "license": "MIT" + "node_modules/bare-events": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.2.tgz", + "integrity": "sha512-EFZHSIBkDgSHIwj2l2QZfP4U5OcD4xFAOwhSb/vlr9PIqyGJGvB/nfClJbcnh3EY4jtPE4zsb5ztae96bVF79A==", + "optional": true, + "dependencies": { + "streamx": "^2.20.0" + } }, "node_modules/base64-js": { "version": "1.5.1", @@ -4486,11 +4756,14 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { @@ -4504,10 +4777,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "license": "MIT", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -4517,7 +4789,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -4531,7 +4803,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4540,24 +4811,31 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } @@ -4601,7 +4879,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -4610,9 +4887,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "funding": [ { "type": "opencollective", @@ -4627,12 +4904,11 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4677,58 +4953,46 @@ "node": ">= 0.8" } }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "engines": { + "node": ">=14.16" + } + }, "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" } }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, + "node_modules/cacheable-request/node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -4771,14 +5035,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "engines": { - "node": ">= 6" - } - }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -4791,9 +5047,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001655", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", - "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", + "version": "1.0.30001679", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz", + "integrity": "sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==", "funding": [ { "type": "opencollective", @@ -4807,13 +5063,12 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/ccount": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", - "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -4834,28 +5089,45 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "engines": { + "node": ">=10" + } + }, "node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -4897,16 +5169,34 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4919,6 +5209,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -4929,17 +5222,17 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "engines": { "node": ">=6.0" } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "funding": [ { "type": "github", @@ -4951,9 +5244,9 @@ } }, "node_modules/clean-css": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", - "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", "dependencies": { "source-map": "~0.6.0" }, @@ -4961,6 +5254,14 @@ "node": ">= 10.0" } }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -4981,9 +5282,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dependencies": { "string-width": "^4.2.0" }, @@ -5025,29 +5326,18 @@ "node": ">=6" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { "node": ">=6" } }, "node_modules/collapse-white-space": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", - "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -5101,17 +5391,17 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, "node_modules/combine-promises": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz", - "integrity": "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", + "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==", "engines": { "node": ">=10" } }, "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -5125,10 +5415,10 @@ "node": ">= 6" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" }, "node_modules/compressible": { "version": "2.0.18", @@ -5142,30 +5432,38 @@ } }, "node_modules/compressible/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "engines": { "node": ">= 0.6" } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", + "negotiator": "~0.6.4", "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/compression/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5179,30 +5477,41 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/yeoman/configstore?sponsor=1" } }, "node_modules/connect-history-api-fallback": { @@ -5214,9 +5523,12 @@ } }, "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } }, "node_modules/consolidated-events": { "version": "2.0.2", @@ -5235,21 +5547,19 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "license": "MIT", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -5263,7 +5573,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -5294,32 +5603,6 @@ "webpack": "^5.1.0" } }, - "node_modules/copy-webpack-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/copy-webpack-plugin/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -5332,13 +5615,13 @@ } }, "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", - "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", "dependencies": { "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", "merge2": "^1.4.1", "slash": "^4.0.0" }, @@ -5349,29 +5632,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/copy-webpack-plugin/node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -5384,9 +5644,9 @@ } }, "node_modules/core-js": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.2.tgz", - "integrity": "sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5394,11 +5654,11 @@ } }, "node_modules/core-js-compat": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", - "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", + "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", "dependencies": { - "browserslist": "^4.21.5" + "browserslist": "^4.24.2" }, "funding": { "type": "opencollective", @@ -5406,9 +5666,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.30.2.tgz", - "integrity": "sha512-p/npFUJXXBkCCTIlEGBdghofn00jWG6ZOtdoIXSJmAu2QBvN0IqpZXWweOytcwE6cfx8ZvVUy1vw8zxhe4Y2vg==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.39.0.tgz", + "integrity": "sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5429,33 +5689,34 @@ } }, "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.12" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -5466,37 +5727,54 @@ } }, "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dependencies": { + "type-fest": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/css-declaration-sorter": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", - "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=10" }, - "peerDependencies": { - "postcss": "^8.0.9" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/css-declaration-sorter": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" } }, "node_modules/css-loader": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.4.tgz", - "integrity": "sha512-0Y5uHtK5BswfaGJ+jrO+4pPg1msFBc0pwPIE1VqfpmVn6YbDfYfXMj8rfd7nt+4goAhJueO+H/I40VWJfcP1mQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.21", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.1", - "postcss-modules-scope": "^3.0.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "engines": { "node": ">= 12.13.0" @@ -5506,20 +5784,29 @@ "url": "https://opencollective.com/webpack" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/css-minimizer-webpack-plugin": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", - "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", "dependencies": { - "cssnano": "^5.1.8", - "jest-worker": "^29.1.2", - "postcss": "^8.4.17", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" }, "engines": { "node": ">= 14.15.0" @@ -5552,55 +5839,6 @@ } } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -5617,15 +5855,15 @@ } }, "node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=8.0.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/css-what": { @@ -5651,122 +5889,137 @@ } }, "node_modules/cssnano": { - "version": "5.1.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", - "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", "dependencies": { - "cssnano-preset-default": "^5.2.14", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/cssnano" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/cssnano-preset-advanced": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz", - "integrity": "sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==", - "license": "MIT", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", + "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", "dependencies": { - "autoprefixer": "^10.4.12", - "cssnano-preset-default": "^5.2.14", - "postcss-discard-unused": "^5.1.0", - "postcss-merge-idents": "^5.1.1", - "postcss-reduce-idents": "^5.2.0", - "postcss-zindex": "^5.1.0" + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.0", + "cssnano-preset-default": "^6.1.2", + "postcss-discard-unused": "^6.0.5", + "postcss-merge-idents": "^6.0.3", + "postcss-reduce-idents": "^6.0.3", + "postcss-zindex": "^6.0.2" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/cssnano-preset-default": { - "version": "5.2.14", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", - "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", - "dependencies": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.1", - "postcss-convert-values": "^5.1.3", - "postcss-discard-comments": "^5.1.2", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.7", - "postcss-merge-rules": "^5.1.4", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.4", - "postcss-minify-selectors": "^5.2.1", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.1", - "postcss-normalize-repeat-style": "^5.1.1", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.1", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.2", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "dependencies": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, "node_modules/cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dependencies": { - "css-tree": "^1.1.2" + "css-tree": "~2.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/cytoscape": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz", - "integrity": "sha512-7MW3Iz57mCUo6JQCho6CmPBCbTlJr7LzyEtIkutG255HLVd4XuBg2I9BkTZLI/e4HoaOB/BiAzXuQybQ95+r9Q==", - "dependencies": { - "heap": "^0.2.6", - "lodash": "^4.17.21" - }, + "version": "3.30.3", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.3.tgz", + "integrity": "sha512-HncJ9gGJbVtw7YXtIs3+6YAFSSiKsom0amWc33Z7QbylbY2JGMrA0yz4EwrdTScZxnwclXeEZHzO5pxoy0ZE4g==", "engines": { "node": ">=0.10" } @@ -5807,9 +6060,9 @@ "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" }, "node_modules/d3": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz", - "integrity": "sha512-q2WHStdhiBtD8DMmhDPyJmXUxr6VWRngKyiJ5EfXMxPw+tqT6BhNjhJZ4w3BHsNm3QoVfZLY8Orq/qPFczwKRA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", "dependencies": { "d3-array": "3", "d3-axis": "3", @@ -5847,9 +6100,9 @@ } }, "node_modules/d3-array": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz", - "integrity": "sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", "dependencies": { "internmap": "1 - 2" }, @@ -5973,17 +6226,6 @@ "node": ">= 10" } }, - "node_modules/d3-dsv/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/d3-ease": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", @@ -6025,9 +6267,9 @@ } }, "node_modules/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", "dependencies": { "d3-array": "2.5.0 - 3" }, @@ -6086,6 +6328,41 @@ "node": ">=12" } }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, "node_modules/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", @@ -6102,9 +6379,9 @@ } }, "node_modules/d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" @@ -6196,25 +6473,30 @@ } }, "node_modules/dagre-d3-es": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz", - "integrity": "sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz", + "integrity": "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==", "dependencies": { - "d3": "^7.8.2", + "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "node_modules/dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -6225,15 +6507,30 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dependencies": { - "mimic-response": "^1.0.0" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-extend": { @@ -6264,15 +6561,17 @@ } }, "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -6294,10 +6593,11 @@ } }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -6330,11 +6630,11 @@ } }, "node_modules/delaunator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", - "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", "dependencies": { - "robust-predicates": "^3.0.0" + "robust-predicates": "^3.0.2" } }, "node_modules/depd": { @@ -6345,6 +6645,14 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -6354,22 +6662,10 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detab": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz", - "integrity": "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==", - "dependencies": { - "repeat-string": "^1.5.4" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { "node": ">=8" } @@ -6380,9 +6676,9 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" }, "node_modules/detect-port": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", - "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", + "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", "dependencies": { "address": "^1.0.1", "debug": "4" @@ -6390,6 +6686,9 @@ "bin": { "detect": "bin/detect-port.js", "detect-port": "bin/detect-port.js" + }, + "engines": { + "node": ">= 4.0.0" } }, "node_modules/detect-port-alt": { @@ -6421,6 +6720,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -6432,15 +6743,10 @@ "node": ">=8" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" - }, "node_modules/dns-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", - "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -6495,9 +6801,9 @@ } }, "node_modules/dompurify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz", - "integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==" + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==" }, "node_modules/domutils": { "version": "3.1.0", @@ -6522,14 +6828,17 @@ } }, "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", "dependencies": { "is-obj": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/dot-prop/node_modules/is-obj": { @@ -6545,11 +6854,6 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, - "node_modules/duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==" - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -6561,21 +6865,20 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", - "license": "ISC" - }, - "node_modules/elkjs": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", - "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" + "version": "1.5.55", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", + "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==" }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==" + }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -6585,18 +6888,18 @@ } }, "node_modules/emoticon": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-3.2.0.tgz", - "integrity": "sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz", + "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -6613,7 +6916,6 @@ "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -6645,7 +6947,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -6657,31 +6958,62 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-module-lexer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", - "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==" + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/escape-html": { @@ -6751,45 +7083,140 @@ "node": ">=4.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/eta": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", - "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", - "engines": { - "node": ">=6.0.0" + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" }, "funding": { - "url": "https://github.com/eta-dev/eta?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/eval": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", - "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", "dependencies": { - "@types/node": "*", - "require-like": ">= 0.1.1" + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" }, - "engines": { - "node": ">= 0.8" - } - }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-value-to-estree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.2.1.tgz", + "integrity": "sha512-Vt2UOjyPbNQQgT5eJh+K5aATti0OjCIAGc9SgMdOFYbohuifsWclR74l0iZTJwePMgWYdX1hlVS+dedH9XV8kw==", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/remcohaszing" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eta": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", + "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eval": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "dependencies": { + "@types/node": "*", + "require-like": ">= 0.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -6825,17 +7252,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execa/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -6845,37 +7261,36 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "license": "MIT", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -6886,11 +7301,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, "node_modules/express/node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -6916,9 +7326,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/express/node_modules/range-parser": { "version": "1.2.1", @@ -6949,10 +7359,15 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" + }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -6969,22 +7384,31 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "dependencies": { - "punycode": "^1.3.2" - } + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==" }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -6996,36 +7420,6 @@ "node": ">=0.8.0" } }, - "node_modules/fbemitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", - "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", - "license": "BSD-3-Clause", - "dependencies": { - "fbjs": "^3.0.0" - } - }, - "node_modules/fbjs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", - "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", - "license": "MIT", - "dependencies": { - "cross-fetch": "^3.1.5", - "fbjs-css-vars": "^1.0.0", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^1.0.35" - } - }, - "node_modules/fbjs-css-vars": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", - "license": "MIT" - }, "node_modules/feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -7037,6 +7431,28 @@ "node": ">=0.4.0" } }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", @@ -7056,10 +7472,38 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/file-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/file-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/file-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -7085,7 +7529,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -7094,12 +7537,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -7124,57 +7567,53 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flux": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", - "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", - "license": "BSD-3-Clause", - "dependencies": { - "fbemitter": "^3.0.0", - "fbjs": "^3.0.1" - }, - "peerDependencies": { - "react": "^15.0.2 || ^16.0.0 || ^17.0.0" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" } }, "node_modules/follow-redirects": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.8.tgz", - "integrity": "sha512-xgrmBhBToVKay1q2Tao5LI26B83UhrB/vM1avwVSDzt8rx3rO6AizBAaF46EgksTVr+rFTQaqZZ9MVBfUe4nig==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -7222,6 +7661,29 @@ } } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", @@ -7251,6 +7713,11 @@ "node": ">=10" } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", @@ -7276,6 +7743,22 @@ "node": ">=6" } }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -7285,15 +7768,15 @@ } }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "engines": { "node": "*" }, "funding": { "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fresh": { @@ -7310,33 +7793,45 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.14" } }, "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==" }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7353,7 +7848,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -7374,14 +7868,14 @@ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" }, "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/github-from-package": { @@ -7398,6 +7892,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -7427,8 +7922,7 @@ "node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, "node_modules/global-dirs": { "version": "3.0.1", @@ -7518,7 +8012,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -7527,24 +8020,38 @@ } }, "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" }, "engines": { - "node": ">=8.6" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, "node_modules/graceful-fs": { @@ -7600,22 +8107,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7628,7 +8129,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -7637,9 +8137,9 @@ } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -7659,18 +8159,20 @@ } }, "node_modules/has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", "engines": { - "node": ">=8" - } + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -7678,35 +8180,65 @@ "node": ">= 0.4" } }, - "node_modules/hast-to-hyperscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", - "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==", + "node_modules/hast-util-from-dom": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.0.tgz", + "integrity": "sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg==", "dependencies": { - "@types/unist": "^2.0.3", - "comma-separated-tokens": "^1.0.0", - "property-information": "^5.3.0", - "space-separated-tokens": "^1.0.0", - "style-to-object": "^0.3.0", - "unist-util-is": "^4.0.0", - "web-namespaces": "^1.0.0" + "@types/hast": "^3.0.0", + "hastscript": "^8.0.0", + "web-namespaces": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz", - "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==", + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", "dependencies": { - "@types/parse5": "^5.0.0", - "hastscript": "^6.0.0", - "property-information": "^5.0.0", - "vfile": "^4.0.0", - "vfile-location": "^3.2.0", - "web-namespaces": "^1.0.0" + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^8.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" }, "funding": { "type": "opencollective", @@ -7714,59 +8246,131 @@ } }, "node_modules/hast-util-is-element": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", - "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dependencies": { + "@types/hast": "^3.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/hast-util-parse-selector": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", - "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/hast-util-raw": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz", - "integrity": "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==", - "dependencies": { - "@types/hast": "^2.0.0", - "hast-util-from-parse5": "^6.0.0", - "hast-util-to-parse5": "^6.0.0", - "html-void-elements": "^1.0.0", - "parse5": "^6.0.0", - "unist-util-position": "^3.0.0", - "vfile": "^4.0.0", - "web-namespaces": "^1.0.0", - "xtend": "^4.0.0", - "zwitch": "^1.0.0" + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.4.tgz", + "integrity": "sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-raw/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + "node_modules/hast-util-to-estree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", + "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree/node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "node_modules/hast-util-to-estree/node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", + "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, "node_modules/hast-util-to-parse5": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", - "integrity": "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", "dependencies": { - "hast-to-hyperscript": "^9.0.0", - "property-information": "^5.0.0", - "web-namespaces": "^1.0.0", - "xtend": "^4.0.0", - "zwitch": "^1.0.0" + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" }, "funding": { "type": "opencollective", @@ -7774,13 +8378,26 @@ } }, "node_modules/hast-util-to-text": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz", - "integrity": "sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "dependencies": { - "hast-util-is-element": "^1.0.0", - "repeat-string": "^1.0.0", - "unist-util-find-after": "^3.0.0" + "@types/hast": "^3.0.0" }, "funding": { "type": "opencollective", @@ -7788,15 +8405,15 @@ } }, "node_modules/hastscript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", - "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" }, "funding": { "type": "opencollective", @@ -7811,11 +8428,6 @@ "he": "bin/he" } }, - "node_modules/heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" - }, "node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -7881,36 +8493,51 @@ } }, "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" }, "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", "dependencies": { "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", "param-case": "^3.0.4", "relateurl": "^0.2.7", - "terser": "^5.10.0" + "terser": "^5.15.1" }, "bin": { "html-minifier-terser": "cli.js" }, "engines": { - "node": ">=12" + "node": "^14.13.1 || >=16.0.0" } }, "node_modules/html-minifier-terser/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "engines": { - "node": ">= 12" + "node": ">=14" } }, "node_modules/html-tags": { @@ -7925,18 +8552,18 @@ } }, "node_modules/html-void-elements": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", - "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/html-webpack-plugin": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz", - "integrity": "sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", + "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", @@ -7952,7 +8579,44 @@ "url": "https://opencollective.com/html-webpack-plugin" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/html-webpack-plugin/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" } }, "node_modules/htmlparser2": { @@ -8017,9 +8681,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", @@ -8050,6 +8714,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -8059,12 +8735,11 @@ } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -8101,17 +8776,17 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "engines": { "node": ">= 4" } }, "node_modules/image-size": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", - "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", "dependencies": { "queue": "6.0.2" }, @@ -8119,7 +8794,7 @@ "image-size": "bin/image-size.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.x" } }, "node_modules/immer": { @@ -8147,11 +8822,11 @@ } }, "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/imurmurhash": { @@ -8171,10 +8846,9 @@ } }, "node_modules/infima": { - "version": "0.2.0-alpha.43", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", - "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", - "license": "MIT", + "version": "0.2.0-alpha.45", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.45.tgz", + "integrity": "sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==", "engines": { "node": ">=12" } @@ -8183,6 +8857,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -8199,9 +8874,9 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" }, "node_modules/internmap": { "version": "2.0.3", @@ -8228,29 +8903,29 @@ } }, "node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "engines": { "node": ">= 10" } }, "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" }, "funding": { "type": "github", @@ -8273,59 +8948,35 @@ "node": ">=8" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dependencies": { - "ci-info": "^2.0.0" + "ci-info": "^3.2.0" }, "bin": { "is-ci": "bin.js" } }, - "node_modules/is-ci/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -8381,9 +9032,9 @@ } }, "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -8405,11 +9056,11 @@ } }, "node_modules/is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", + "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8419,7 +9070,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -8449,11 +9099,14 @@ } }, "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-plain-object": { @@ -8499,24 +9152,6 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, - "node_modules/is-whitespace-character": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", - "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-word-character": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", - "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -8529,9 +9164,12 @@ } }, "node_modules/is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", + "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", + "engines": { + "node": ">=12" + } }, "node_modules/isarray": { "version": "0.0.1", @@ -8552,11 +9190,11 @@ } }, "node_modules/jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -8568,12 +9206,12 @@ } }, "node_modules/jest-worker": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", - "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dependencies": { "@types/node": "*", - "jest-util": "^29.5.0", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -8596,21 +9234,21 @@ } }, "node_modules/jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "bin": { "jiti": "bin/jiti.js" } }, "node_modules/joi": { - "version": "17.9.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz", - "integrity": "sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==", + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "dependencies": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } @@ -8632,20 +9270,20 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -8653,9 +9291,9 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/json5": { "version": "2.2.3", @@ -8680,15 +9318,15 @@ } }, "node_modules/katex": { - "version": "0.13.24", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.13.24.tgz", - "integrity": "sha512-jZxYuKCma3VS5UuxOx/rFV1QyGSl3Uy/i0kTJF3HgQ5xMinCQVF8Zd4bMY/9aI9b9A2pjIBOsjSSm68ykTAr8w==", + "version": "0.16.11", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz", + "integrity": "sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" ], "dependencies": { - "commander": "^8.0.0" + "commander": "^8.3.0" }, "bin": { "katex": "cli.js" @@ -8703,17 +9341,17 @@ } }, "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dependencies": { - "json-buffer": "3.0.0" + "json-buffer": "3.0.1" } }, "node_modules/khroma": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.0.0.tgz", - "integrity": "sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" }, "node_modules/kind-of": { "version": "6.0.3", @@ -8731,32 +9369,47 @@ "node": ">=6" } }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==" + }, + "node_modules/langium": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz", + "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", "dependencies": { - "package-json": "^6.3.0" + "package-json": "^8.1.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/launch-editor": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", - "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", "dependencies": { "picocolors": "^1.0.0", - "shell-quote": "^1.7.3" + "shell-quote": "^1.8.1" } }, "node_modules/layout-base": { @@ -8773,11 +9426,14 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -8806,15 +9462,33 @@ "node": ">=8.9.0" } }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^6.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -8827,23 +9501,11 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, - "node_modules/lodash.curry": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", - "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", - "license": "MIT" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, - "node_modules/lodash.flow": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", - "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==", - "license": "MIT" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -8854,6 +9516,15 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -8874,11 +9545,14 @@ } }, "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "engines": { - "node": ">=0.10.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru-cache": { @@ -8889,56 +9563,375 @@ "yallist": "^3.0.2" } }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", + "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", + "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", "dependencies": { - "semver": "^6.0.0" + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/markdown-escapes": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", - "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", + "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-squeeze-paragraphs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz", - "integrity": "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==", + "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", "dependencies": { - "unist-util-remove": "^2.0.0" + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", - "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "unist-util-visit": "^2.0.0" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" }, "funding": { "type": "opencollective", @@ -8946,125 +9939,1905 @@ } }, "node_modules/mdast-util-to-hast": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", - "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.0.tgz", + "integrity": "sha512-mxCfEYvADJqOiHfGpJXLs4/fAjHz448rH0pfY5fAoxiz70rQiDSzUUy4dNET2T08i46IVpjohPd6WWbzmRHiPA==", + "dependencies": { + "@braintree/sanitize-url": "^7.0.1", + "@iconify/utils": "^2.1.32", + "@mermaid-js/parser": "^0.3.0", + "@types/d3": "^7.4.3", + "@types/dompurify": "^3.0.5", + "cytoscape": "^3.29.2", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.11", + "dayjs": "^1.11.10", + "dompurify": "^3.0.11 <3.1.7", + "katex": "^0.16.9", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^13.0.2", + "roughjs": "^4.6.6", + "stylis": "^4.3.1", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.1" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", + "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", + "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", + "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.1.tgz", + "integrity": "sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg==", + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.2.tgz", + "integrity": "sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-space/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", + "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-events-to-acorn/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-normalize-identifier/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-types": "^2.0.0" } }, - "node_modules/mdast-util-to-string": { + "node_modules/micromark-util-sanitize-uri": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/memfs": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.1.tgz", - "integrity": "sha512-UWbFJKvj5k+nETdteFndTpYxdeTMox/ULeqX5k/dpaQJCCFmj5EeKv3dBcyO2xmkRAx2vppRu5dVG7SOtsGOzA==", + "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "fs-monkey": "^1.0.3" - }, - "engines": { - "node": ">= 4.0.0" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { + "node_modules/micromark-util-subtokenize/node_modules/micromark-util-symbol": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "node_modules/mermaid": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.4.3.tgz", - "integrity": "sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw==", + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@braintree/sanitize-url": "^6.0.0", - "cytoscape": "^3.23.0", - "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.1.0", - "d3": "^7.4.0", - "dagre-d3-es": "7.0.9", - "dayjs": "^1.11.7", - "dompurify": "2.4.3", - "elkjs": "^0.8.2", - "khroma": "^2.0.0", - "lodash-es": "^4.17.21", - "non-layered-tidy-tree-layout": "^2.0.2", - "stylis": "^4.1.2", - "ts-dedent": "^2.2.0", - "uuid": "^9.0.0", - "web-worker": "^1.2.0" + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/mermaid/node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/micromark/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } + "node_modules/micromark/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -9112,19 +11885,23 @@ } }, "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mini-css-extract-plugin": { - "version": "2.7.6", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", - "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", "dependencies": { - "schema-utils": "^4.0.0" + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" }, "engines": { "node": ">= 12.13.0" @@ -9137,55 +11914,6 @@ "webpack": "^5.0.0" } }, - "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -9215,24 +11943,35 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "node_modules/mlly": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", + "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", + "dependencies": { + "acorn": "^8.12.1", + "pathe": "^1.1.2", + "pkg-types": "^1.2.0", + "ufo": "^1.5.4" + } + }, "node_modules/monaco-editor": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.38.0.tgz", - "integrity": "sha512-11Fkh6yzEmwx7O0YoLxeae0qEGFwmyPRlVxpg7oF9czOOCB/iCjdJrG5I67da5WiXK3YJCxoz9TJFE8Tfq/v9A==", + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.0.tgz", + "integrity": "sha512-OeWhNpABLCeTqubfqLMXGsqf6OmPU6pHM85kF3dhy6kq5hnhuVS1p3VrEW/XhWHc71P2tHyS5JFySD8mgs1crw==", "peer": true }, "node_modules/mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "engines": { "node": ">=10" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -9256,7 +11995,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -9270,9 +12008,9 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "engines": { "node": ">= 0.6" } @@ -9292,9 +12030,9 @@ } }, "node_modules/node-abi": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz", - "integrity": "sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==", + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", "dependencies": { "semver": "^7.3.5" }, @@ -9303,36 +12041,22 @@ } }, "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" }, "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", "dependencies": { - "whatwg-url": "^5.0.0" + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node": ">=18" } }, "node_modules/node-forge": { @@ -9346,13 +12070,7 @@ "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "license": "MIT" - }, - "node_modules/non-layered-tidy-tree-layout": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", - "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -9371,11 +12089,11 @@ } }, "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9395,8 +12113,7 @@ "node_modules/nprogress": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", - "license": "MIT" + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" }, "node_modules/nth-check": { "version": "2.1.1", @@ -9406,7 +12123,71 @@ "boolbase": "^1.0.0" }, "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/null-loader": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", + "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/null-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/null-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/null-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/null-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/object-assign": { @@ -9421,7 +12202,6 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9438,12 +12218,12 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -9525,36 +12305,39 @@ } }, "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "engines": { - "node": ">=6" + "node": ">=12.20" } }, "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { @@ -9592,27 +12375,26 @@ } }, "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" }, "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } + "node_modules/package-manager-detector": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.2.tgz", + "integrity": "sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==" }, "node_modules/param-case": { "version": "3.0.4", @@ -9635,22 +12417,29 @@ } }, "node_modules/parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", "dependencies": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -9674,22 +12463,22 @@ "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" }, "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dependencies": { - "entities": "^4.4.0" + "entities": "^4.5.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "dependencies": { - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "parse5": "^7.0.0" }, "funding": { @@ -9713,12 +12502,17 @@ "tslib": "^2.0.3" } }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==" + }, "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/path-is-absolute": { @@ -9748,9 +12542,9 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", + "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", "dependencies": { "isarray": "0.0.1" } @@ -9763,11 +12557,15 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" + }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "license": "ISC" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -9781,14 +12579,27 @@ } }, "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", "dependencies": { - "find-up": "^4.0.0" + "find-up": "^6.3.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", + "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.2", + "pathe": "^1.1.2" } }, "node_modules/pkg-up": { @@ -9825,6 +12636,20 @@ "node": ">=6" } }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pkg-up/node_modules/p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -9844,10 +12669,24 @@ "node": ">=4" } }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/postcss": { - "version": "8.4.45", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", - "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -9862,128 +12701,128 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", "dependencies": { - "postcss-selector-parser": "^6.0.9", + "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, "peerDependencies": { "postcss": "^8.2.2" } }, "node_modules/postcss-colormin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", - "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", - "colord": "^2.9.1", + "colord": "^2.9.3", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-convert-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", - "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-comments": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-unused": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", - "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", - "license": "MIT", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", + "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-loader": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.0.tgz", - "integrity": "sha512-qLAFjvR2BFNz1H930P7mj1iuWJFjGey/nVhimfOAAQ1ZyPpcClAxP8+A55Sl8mBvM+K2a9Pjgdj10KpANWrNfw==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", + "integrity": "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==", "dependencies": { - "cosmiconfig": "^8.1.3", - "jiti": "^1.18.2", - "klona": "^2.0.6", - "semver": "^7.3.8" + "cosmiconfig": "^8.3.5", + "jiti": "^1.20.0", + "semver": "^7.5.4" }, "engines": { "node": ">= 14.15.0" @@ -9997,135 +12836,117 @@ "webpack": "^5.0.0" } }, - "node_modules/postcss-loader/node_modules/cosmiconfig": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", - "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - } - }, "node_modules/postcss-merge-idents": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", - "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", - "license": "MIT", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", + "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", "dependencies": { - "cssnano-utils": "^3.1.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-merge-longhand": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", - "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", "dependencies": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.1" + "stylehacks": "^6.1.1" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-merge-rules": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", - "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-params": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", - "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", "dependencies": { - "browserslist": "^4.21.4", - "cssnano-utils": "^3.1.0", + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-selectors": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", - "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -10134,9 +12955,9 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.1.tgz", - "integrity": "sha512-Zr/dB+IlXaEqdoslLHhhqecwj73vc3rDmOpsBNBEVk7P2aqAlz+Ijy0fFbU5Ie9PtreDOIgGa9MsLWakVGl+fA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -10150,9 +12971,9 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -10178,193 +12999,191 @@ } }, "node_modules/postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-positions": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", - "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-repeat-style": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", - "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-unicode": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", - "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", "dependencies": { - "normalize-url": "^6.0.1", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-ordered-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", - "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", "dependencies": { - "cssnano-utils": "^3.1.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-idents": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", - "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", - "license": "MIT", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", + "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-initial": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", - "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -10374,47 +13193,46 @@ } }, "node_modules/postcss-sort-media-queries": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", - "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", - "license": "MIT", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", + "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", "dependencies": { - "sort-css-media-queries": "2.1.0" + "sort-css-media-queries": "2.2.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "postcss": "^8.4.16" + "postcss": "^8.4.23" } }, "node_modules/postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", "dependencies": { "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" + "svgo": "^3.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >= 18" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-value-parser": { @@ -10423,21 +13241,20 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postcss-zindex": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", - "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", - "license": "MIT", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", + "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", @@ -10459,12 +13276,30 @@ "node": ">=10" } }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/pretty-error": { @@ -10485,18 +13320,21 @@ } }, "node_modules/prism-react-renderer": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", - "integrity": "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.0.tgz", + "integrity": "sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw==", + "dependencies": { + "@types/prismjs": "^1.26.0", + "clsx": "^2.0.0" + }, "peerDependencies": { - "react": ">=0.14.9" + "react": ">=16.0.0" } }, "node_modules/prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "license": "MIT", "engines": { "node": ">=6" } @@ -10506,15 +13344,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "license": "MIT", - "dependencies": { - "asap": "~2.0.3" - } - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -10538,17 +13367,19 @@ } }, "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "dependencies": { - "xtend": "^4.0.0" - }, + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -10570,43 +13401,42 @@ } }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } }, "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", + "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", "dependencies": { - "escape-goat": "^2.0.0" + "escape-goat": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pure-color": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", - "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==", - "license": "MIT" - }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "license": "BSD-3-Clause", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -10642,6 +13472,22 @@ } ] }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -10662,7 +13508,6 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -10677,11 +13522,21 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/raw-loader": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", @@ -10698,9 +13553,37 @@ "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/raw-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/raw-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" } }, + "node_modules/raw-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/raw-loader/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", @@ -10741,29 +13624,16 @@ } }, "node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/react-base16-styling": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", - "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", - "license": "MIT", - "dependencies": { - "base16": "^1.0.0", - "lodash.curry": "^4.0.1", - "lodash.flow": "^3.3.0", - "pure-color": "^1.2.0" - } - }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -10814,9 +13684,9 @@ } }, "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "engines": { "node": ">= 12.13.0" } @@ -10863,17 +13733,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/react-dev-utils/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "17.0.2" + "react": "^18.3.1" } }, "node_modules/react-error-overlay": { @@ -10907,20 +13795,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/react-json-view": { - "version": "1.21.3", - "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", - "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", - "license": "MIT", - "dependencies": { - "flux": "^4.0.1", - "react-base16-styling": "^0.6.0", - "react-lifecycles-compat": "^3.0.4", - "react-textarea-autosize": "^8.3.2" + "node_modules/react-json-view-lite": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.5.0.tgz", + "integrity": "sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==", + "engines": { + "node": ">=14" }, "peerDependencies": { - "react": "^17.0.0 || ^16.3.0 || ^15.5.4", - "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" } }, "node_modules/react-katex": { @@ -10935,44 +13818,13 @@ "react": ">=15.3.2 <=18" } }, - "node_modules/react-katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/react-katex/node_modules/katex": { - "version": "0.16.11", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz", - "integrity": "sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "license": "MIT", - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", - "license": "MIT" - }, "node_modules/react-loadable": { "name": "@docusaurus/react-loadable", - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", - "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", + "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "dependencies": { - "@types/react": "*", - "prop-types": "^15.6.2" + "@types/react": "*" }, "peerDependencies": { "react": "*" @@ -11041,23 +13893,6 @@ "react": ">=15" } }, - "node_modules/react-textarea-autosize": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", - "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.13", - "use-composed-ref": "^1.3.0", - "use-latest": "^1.2.1" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-waypoint": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/react-waypoint/-/react-waypoint-10.3.0.tgz", @@ -11073,9 +13908,9 @@ } }, "node_modules/react-waypoint/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/readable-stream": { "version": "3.6.2", @@ -11117,6 +13952,66 @@ "node": ">= 0.10" } }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.0.tgz", + "integrity": "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -11134,9 +14029,9 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dependencies": { "regenerate": "^1.4.2" }, @@ -11145,27 +14040,27 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dependencies": { "@babel/runtime": "^7.8.4" } }, "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", "dependencies": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -11174,80 +14069,91 @@ } }, "node_modules/registry-auth-token": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", - "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", "dependencies": { - "rc": "1.2.8" + "@pnpm/npm-conf": "^2.1.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=14" } }, "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", "dependencies": { - "rc": "^1.2.8" + "rc": "1.2.8" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" + }, "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", + "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", "dependencies": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, "node_modules/rehype-katex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-5.0.0.tgz", - "integrity": "sha512-ksSuEKCql/IiIadOHiKRMjypva9BLhuwQNascMqaoGLDVd0k2NlE2wMvgZ3rpItzRKCd6vs8s7MFbb8pcR0AEg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", "dependencies": { - "@types/katex": "^0.11.0", - "hast-util-to-text": "^2.0.0", - "katex": "^0.13.0", - "rehype-parse": "^7.0.0", - "unified": "^9.0.0", - "unist-util-visit": "^2.0.0" + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-parse": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz", - "integrity": "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==", + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", "dependencies": { - "hast-util-from-parse5": "^6.0.0", - "parse5": "^6.0.0" + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-parse/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, "node_modules/relateurl": { "version": "0.2.7", @@ -11257,179 +14163,135 @@ "node": ">= 0.10" } }, - "node_modules/remark-emoji": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz", - "integrity": "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==", + "node_modules/remark-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.0.tgz", + "integrity": "sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==", "dependencies": { - "emoticon": "^3.2.0", - "node-emoji": "^1.10.0", - "unist-util-visit": "^2.0.3" - } - }, - "node_modules/remark-footnotes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", - "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==", + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/remark-math": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-3.0.1.tgz", - "integrity": "sha512-epT77R/HK0x7NqrWHdSV75uNLwn8g9qTyMqCRCDujL0vj/6T6+yhdrR7mjELWtkse+Fw02kijAaBuVcHBor1+Q==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/remark-emoji": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", + "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", + "dependencies": { + "@types/mdast": "^4.0.2", + "emoticon": "^4.0.1", + "mdast-util-find-and-replace": "^3.0.1", + "node-emoji": "^2.1.0", + "unified": "^11.0.4" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/remark-mdx": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", - "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", + "node_modules/remark-frontmatter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", + "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", "dependencies": { - "@babel/core": "7.12.9", - "@babel/helper-plugin-utils": "7.10.4", - "@babel/plugin-proposal-object-rest-spread": "7.12.1", - "@babel/plugin-syntax-jsx": "7.12.1", - "@mdx-js/util": "1.6.22", - "is-alphabetical": "1.0.4", - "remark-parse": "8.0.3", - "unified": "9.2.0" + "@types/mdast": "^4.0.0", + "mdast-util-frontmatter": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/remark-mdx/node_modules/@babel/core": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", - "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.7", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.9", - "@babel/types": "^7.12.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" + "node_modules/remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/babel" + "url": "https://opencollective.com/unified" } }, - "node_modules/remark-mdx/node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, - "node_modules/remark-mdx/node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/remark-mdx/node_modules/@babel/plugin-syntax-jsx": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", - "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "node_modules/remark-mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.0.tgz", + "integrity": "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/remark-mdx/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/remark-mdx/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/remark-mdx/node_modules/unified": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", - "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", "dependencies": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/remark-parse": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", - "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", - "dependencies": { - "ccount": "^1.0.0", - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^2.0.0", - "vfile-location": "^3.0.0", - "xtend": "^4.0.1" + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/remark-squeeze-paragraphs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", - "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", "dependencies": { - "mdast-squeeze-paragraphs": "^4.0.0" + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", @@ -11559,11 +14421,11 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -11574,6 +14436,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -11588,11 +14455,17 @@ "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" }, "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dependencies": { - "lowercase-keys": "^1.0.0" + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/retry": { @@ -11616,100 +14489,53 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/robust-predicates": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", - "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" - }, - "node_modules/rtl-detect": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz", - "integrity": "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" - }, - "node_modules/rtlcss": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz", - "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.3.11", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "rtlcss": "bin/rtlcss.js" - } - }, - "node_modules/rtlcss/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rtlcss/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" + "bin": { + "rimraf": "bin.js" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rtlcss/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" } }, - "node_modules/rtlcss/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", + "node_modules/rtl-detect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", + "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==" + }, + "node_modules/rtlcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", + "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", "dependencies": { - "p-limit": "^3.0.2" + "escalade": "^3.1.1", + "picocolors": "^1.0.0", + "postcss": "^8.4.21", + "strip-json-comments": "^3.1.1" }, - "engines": { - "node": ">=10" + "bin": { + "rtlcss": "bin/rtlcss.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=12.0.0" } }, "node_modules/run-parallel": { @@ -11739,14 +14565,6 @@ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -11772,30 +14590,30 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" } }, "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 8.9.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", @@ -11803,10 +14621,9 @@ } }, "node_modules/search-insights": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.1.tgz", - "integrity": "sha512-HHFjYH/0AqXacETlIbe9EYc3UNlQYGNNTY0fZ/sWl6SweX+GDxq9NB5+RVoPLgEFuOtCz7M9dhYxqDnhbbF0eQ==", - "license": "MIT", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.2.tgz", + "integrity": "sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==", "peer": true }, "node_modules/section-matter": { @@ -11827,10 +14644,11 @@ "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" }, "node_modules/selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dependencies": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" }, "engines": { @@ -11841,7 +14659,6 @@ "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -11850,29 +14667,23 @@ } }, "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dependencies": { - "semver": "^6.3.0" + "semver": "^7.3.5" }, "engines": { - "node": ">=8" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -11905,10 +14716,13 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/send/node_modules/range-parser": { "version": "1.2.1", @@ -11919,32 +14733,31 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/serve-handler": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", - "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", "mime-types": "2.1.18", "minimatch": "3.1.2", "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", + "path-to-regexp": "3.3.0", "range-parser": "1.2.0" } }, "node_modules/serve-handler/node_modules/path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==" }, "node_modules/serve-index": { "version": "1.9.1", @@ -12017,14 +14830,14 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -12034,7 +14847,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -12047,12 +14859,6 @@ "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -12075,22 +14881,22 @@ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, "node_modules/sharp": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz", - "integrity": "sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==", + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", "hasInstallScript": true, "dependencies": { "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", "prebuild-install": "^7.1.1", - "semver": "^7.3.7", + "semver": "^7.5.4", "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", + "tar-fs": "^3.0.4", "tunnel-agent": "^0.6.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=14.15.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -12143,7 +14949,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -12205,31 +15010,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/simple-get/node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/simple-get/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -12244,13 +15024,13 @@ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, "node_modules/sirv": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", - "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", "dependencies": { - "@polka/url": "^1.0.0-next.20", - "mrmime": "^1.0.0", - "totalist": "^1.0.0" + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" }, "engines": { "node": ">= 10" @@ -12265,7 +15045,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", - "license": "MIT", "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", @@ -12283,8 +15062,18 @@ "node_modules/sitemap/node_modules/@types/node": { "version": "17.0.45", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "license": "MIT" + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/slash": { "version": "3.0.0", @@ -12294,6 +15083,15 @@ "node": ">=8" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -12304,28 +15102,34 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/sort-css-media-queries": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", - "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==", - "license": "MIT", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", + "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", "engines": { "node": ">= 6.3.0" } }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "license": "BSD-3-Clause", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -12339,10 +15143,18 @@ "source-map": "^0.6.0" } }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -12381,26 +15193,22 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + "node_modules/srcset": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", + "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/state-local": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" }, - "node_modules/state-toggle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", - "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -12410,9 +15218,22 @@ } }, "node_modules/std-env": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", - "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==" + }, + "node_modules/streamx": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "dependencies": { + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } }, "node_modules/string_decoder": { "version": "1.3.0", @@ -12439,9 +15260,9 @@ } }, "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -12450,9 +15271,9 @@ } }, "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -12463,6 +15284,19 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -12507,7 +15341,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", "engines": { "node": ">=8" }, @@ -12516,32 +15349,32 @@ } }, "node_modules/style-to-object": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", - "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", "dependencies": { - "inline-style-parser": "0.1.1" + "inline-style-parser": "0.2.4" } }, "node_modules/stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", "dependencies": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz", + "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==" }, "node_modules/supports-color": { "version": "7.2.0", @@ -12571,94 +15404,35 @@ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" }, "node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/svgo/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/svgo/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/svgo/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/svgo/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" }, "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/svgo" } }, - "node_modules/svgo/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" } }, "node_modules/tapable": { @@ -12670,36 +15444,32 @@ } }, "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, "node_modules/terser": { - "version": "5.31.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", - "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", - "license": "BSD-2-Clause", + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -12717,7 +15487,6 @@ "version": "5.3.10", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", @@ -12747,6 +15516,29 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/terser-webpack-plugin/node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -12760,10 +15552,15 @@ "node": ">= 10.13.0" } }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -12796,6 +15593,11 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/text-decoder": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", + "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -12807,36 +15609,24 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, "node_modules/tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" }, "node_modules/tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "engines": { - "node": ">=6" - } + "node_modules/tinyexec": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", + "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==" }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -12853,38 +15643,26 @@ } }, "node_modules/totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "engines": { "node": ">=6" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==", - "deprecated": "Use String.prototype.trim() instead" - }, - "node_modules/trim-trailing-lines": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", - "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==", + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -12899,9 +15677,9 @@ } }, "node_modules/tslib": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz", - "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -12929,7 +15707,6 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -12942,7 +15719,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -12951,7 +15727,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -12968,58 +15743,40 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, - "node_modules/ua-parser-js": { - "version": "1.0.38", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.38.tgz", - "integrity": "sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "engines": { - "node": "*" - } + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==" }, - "node_modules/unherit": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", - "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", - "dependencies": { - "inherits": "^2.0.0", - "xtend": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", "engines": { "node": ">=4" } @@ -13037,9 +15794,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "engines": { "node": ">=4" } @@ -13053,16 +15810,17 @@ } }, "node_modules/unified": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", - "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "dependencies": { - "bail": "^1.0.0", + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", @@ -13070,70 +15828,62 @@ } }, "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", "dependencies": { - "crypto-random-string": "^2.0.0" + "crypto-random-string": "^4.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/unist-builder": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", - "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "node": ">=12" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/unist-util-find-after": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz", - "integrity": "sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", "dependencies": { - "unist-util-is": "^4.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-generated": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", - "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/unist-util-position": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", - "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-remove": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz", - "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==", + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", "dependencies": { - "unist-util-is": "^4.0.0" + "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", @@ -13141,11 +15891,12 @@ } }, "node_modules/unist-util-remove-position": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", - "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", "dependencies": { - "unist-util-visit": "^2.0.0" + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" }, "funding": { "type": "opencollective", @@ -13153,11 +15904,11 @@ } }, "node_modules/unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "dependencies": { - "@types/unist": "^2.0.2" + "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", @@ -13165,13 +15916,13 @@ } }, "node_modules/unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, "funding": { "type": "opencollective", @@ -13179,12 +15930,12 @@ } }, "node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, "funding": { "type": "opencollective", @@ -13192,9 +15943,9 @@ } }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "engines": { "node": ">= 10.0.0" } @@ -13208,9 +15959,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "funding": [ { "type": "opencollective", @@ -13225,10 +15976,9 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -13238,118 +15988,73 @@ } }, "node_modules/update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", - "dependencies": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", + "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", + "dependencies": { + "boxen": "^7.0.0", + "chalk": "^5.0.1", + "configstore": "^6.0.0", + "has-yarn": "^3.0.0", + "import-lazy": "^4.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" + "is-npm": "^6.0.0", + "is-yarn-global": "^0.4.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.3.7", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, "node_modules/update-notifier/node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", + "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" + "ansi-align": "^3.0.1", + "camelcase": "^7.0.1", + "chalk": "^5.2.0", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", "engines": { - "node": ">=6" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/update-notifier/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/update-notifier/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/update-notifier/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/update-notifier/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/update-notifier/node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/update-notifier/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/uri-js": { @@ -13360,14 +16065,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, "node_modules/url-loader": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", @@ -13394,6 +16091,34 @@ } } }, + "node_modules/url-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/url-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/url-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/url-loader/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -13414,9 +16139,9 @@ } }, "node_modules/url-loader/node_modules/schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -13430,65 +16155,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/use-composed-ref": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", - "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/use-isomorphic-layout-effect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-latest": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", - "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", - "license": "MIT", - "dependencies": { - "use-isomorphic-layout-effect": "^1.1.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13500,9 +16166,9 @@ "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" }, "node_modules/utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", "engines": { "node": ">= 4" } @@ -13516,9 +16182,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -13537,14 +16207,12 @@ } }, "node_modules/vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" }, "funding": { "type": "opencollective", @@ -13552,50 +16220,78 @@ } }, "node_modules/vfile-location": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", - "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/wait-on": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz", - "integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==", + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", "dependencies": { - "axios": "^0.25.0", - "joi": "^17.6.0", - "lodash": "^4.17.21", - "minimist": "^1.2.5", - "rxjs": "^7.5.4" + "vscode-languageserver-protocol": "3.17.5" }, "bin": { - "wait-on": "bin/wait-on" - }, - "engines": { - "node": ">=10.0.0" + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" } }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -13613,38 +16309,26 @@ } }, "node_modules/web-namespaces": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", - "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/web-worker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", - "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", - "license": "MIT", + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dependencies": { - "@types/estree": "^1.0.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", @@ -13679,19 +16363,21 @@ } }, "node_modules/webpack-bundle-analyzer": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz", - "integrity": "sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", - "lodash": "^4.17.20", + "html-escaper": "^2.0.2", "opener": "^1.5.2", - "sirv": "^1.0.7", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { @@ -13713,7 +16399,6 @@ "version": "5.3.4", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "license": "MIT", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.3", @@ -13732,37 +16417,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/webpack-dev-middleware/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -13790,28 +16444,10 @@ "node": ">= 0.6" } }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/webpack-dev-server": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz", - "integrity": "sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ==", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -13819,7 +16455,7 @@ "@types/serve-index": "^1.9.1", "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.0.11", "chokidar": "^3.5.3", @@ -13841,7 +16477,7 @@ "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", + "webpack-dev-middleware": "^5.3.4", "ws": "^8.13.0" }, "bin": { @@ -13866,60 +16502,10 @@ } } }, - "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-server/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/webpack-dev-server/node_modules/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -13937,15 +16523,16 @@ } }, "node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dependencies": { "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" + "flat": "^5.0.2", + "wildcard": "^2.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, "node_modules/webpack-sources": { @@ -13956,6 +16543,34 @@ "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/webpack/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -13979,7 +16594,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -13994,22 +16608,72 @@ } }, "node_modules/webpackbar": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", - "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-6.0.1.tgz", + "integrity": "sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==", "dependencies": { - "chalk": "^4.1.0", - "consola": "^2.15.3", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "consola": "^3.2.3", + "figures": "^3.2.0", + "markdown-table": "^2.0.0", "pretty-time": "^1.1.0", - "std-env": "^3.0.1" + "std-env": "^3.7.0", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.21.3" }, "peerDependencies": { "webpack": "3 || 4 || 5" } }, + "node_modules/webpackbar/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/webpackbar/node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webpackbar/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpackbar/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/website_playground": { "resolved": "../website_playground/pkg", "link": true @@ -14035,16 +16699,6 @@ "node": ">=0.8.0" } }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -14095,9 +16749,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -14117,9 +16771,9 @@ } }, "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -14150,7 +16804,6 @@ "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -14168,11 +16821,14 @@ } }, "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/xml-js": { @@ -14186,14 +16842,6 @@ "xml-js": "bin/cli.js" } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -14208,20 +16856,20 @@ } }, "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/zwitch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", - "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" diff --git a/docs/package.json b/docs/package.json index b59fcbc3c556..88bcabc662bd 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,24 +15,24 @@ "wasm-pack": "cd ../website_playground/ && wasm-pack build" }, "dependencies": { - "@docusaurus/core": "^2.4.3", - "@docusaurus/plugin-ideal-image": "^2.4.3", - "@docusaurus/preset-classic": "^2.4.3", - "@docusaurus/theme-mermaid": "^2.4.3", - "@mdx-js/react": "^1.6.22", + "@docusaurus/core": "^3.6.1", + "@docusaurus/plugin-ideal-image": "^3.6.1", + "@docusaurus/preset-classic": "^3.6.1", + "@docusaurus/theme-mermaid": "^3.6.1", + "@mdx-js/react": "^3.1.0", "@monaco-editor/react": "^4.4.6", - "mermaid": "^9.3.0", - "prism-react-renderer": "^1.3.5", + "mermaid": "^11.4.0", + "prism-react-renderer": "^2.4.0", "raw-loader": "^4.0.2", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-katex": "^3.0.1", - "rehype-katex": "^5.0.0", - "remark-math": "^3.0.1", + "rehype-katex": "^7.0.1", + "remark-math": "^6.0.0", "website_playground": "file:../website_playground/pkg" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^2.4.3" + "@docusaurus/module-type-aliases": "^3.6.1" }, "browserslist": { "production": [ @@ -47,6 +47,6 @@ ] }, "engines": { - "node": ">=16.14" + "node": ">=18.0" } } diff --git a/docs/src/theme/prism-include-languages.js b/docs/src/theme/prism-include-languages.js index 827cb9065199..c8fe35db80e1 100644 --- a/docs/src/theme/prism-include-languages.js +++ b/docs/src/theme/prism-include-languages.js @@ -4,27 +4,25 @@ export default function prismIncludeLanguages(PrismObject) { themeConfig: {prism}, } = siteConfig; const {additionalLanguages} = prism; - // Prism components work on the Prism instance on the window, while prism- - // react-renderer uses its own Prism instance. We temporarily mount the - // instance onto window, import components to enhance it, then remove it to - // avoid polluting global namespace. - // You can mutate PrismObject: registering plugins, deleting languages... As - // long as you don't re-assign it + + const PrismBefore = globalThis.Prism; globalThis.Prism = PrismObject; additionalLanguages.forEach((lang) => { // eslint-disable-next-line global-require, import/no-dynamic-require require(`prismjs/components/prism-${lang}`); }); - const rustLanguage = Prism.languages.rust; Prism.languages["rust,ignore"] = Prism.languages.rust; const origTokenize = PrismObject.tokenize; - PrismObject.tokenize = (text, grammar) => { - if (grammar == rustLanguage) { - text = text.split("\n").filter(line => !line.startsWith("# ")).join("\n"); + PrismObject.hooks.add("after-tokenize", function(env) { + if (env.language === "rust") { + let code = env.code.split("\n").filter(line => !line.startsWith("# ")).join("\n"); + env.tokens = origTokenize(code, env.grammar); } - return origTokenize(text, grammar); - }; + }); delete globalThis.Prism; + if (typeof PrismBefore !== 'undefined') { + globalThis.Prism = PrismObject; + } } diff --git a/hydroflow_macro/build.rs b/hydroflow_macro/build.rs index d630504d1a82..adf5e9459b2f 100644 --- a/hydroflow_macro/build.rs +++ b/hydroflow_macro/build.rs @@ -3,7 +3,7 @@ use std::env::VarError; use std::fmt::Write as _FmtWrite; use std::fs::File; -use std::io::{BufReader, BufWriter, Result, Write}; +use std::io::{BufWriter, Read, Result, Write}; use std::path::{Path, PathBuf}; use hydroflow_lang::graph::ops::{PortListSpec, OPERATORS}; @@ -26,14 +26,22 @@ fn book_file_writer(filename: impl AsRef) -> Result> { Ok(BufWriter::new(File::create(pathbuf)?)) } -fn write_operator_docgen(op_name: &str, mut write: &mut impl Write) -> Result<()> { +fn write_operator_docgen(op_name: &str, write: &mut impl Write) -> Result<()> { let doctest_path = PathBuf::from_iter([ std::env!("CARGO_MANIFEST_DIR"), "../docs/docgen", &*format!("{}.md", op_name), ]); - let mut read = BufReader::new(File::open(doctest_path)?); - std::io::copy(&mut read, &mut write)?; + let mut read_string = String::new(); + File::open(doctest_path)?.read_to_string(&mut read_string)?; + write!( + write, + "{}", + read_string + .split(" tests/compile-fail/send_bincode_lifetime.rs:7:5 + | +6 | fn test<'a, 'b>(p1: &Process<'a, P1>, p2: &Process<'b, P2>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +7 | p1.source_iter(q!(0..10)).send_bincode(p2).for_each(q!(|n| println!("{}", n))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `hydroflow_plus::Process<'_, P1>`, which makes the generic argument `'_` invariant + = note: the struct `hydroflow_plus::Process<'a, P>` is invariant over the parameter `'a` + = help: see for more information about variance + +error: lifetime may not live long enough + --> tests/compile-fail/send_bincode_lifetime.rs:7:5 + | +6 | fn test<'a, 'b>(p1: &Process<'a, P1>, p2: &Process<'b, P2>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +7 | p1.source_iter(q!(0..10)).send_bincode(p2).for_each(q!(|n| println!("{}", n))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `hydroflow_plus::Process<'_, P2>`, which makes the generic argument `'_` invariant + = note: the struct `hydroflow_plus::Process<'a, P>` is invariant over the parameter `'a` + = help: see for more information about variance + +help: `'a` and `'b` must be the same: replace one with the other diff --git a/hydroflow_plus/tests/compile_fail.rs b/hydroflow_plus/tests/compile_fail.rs new file mode 100644 index 000000000000..39816f304b6b --- /dev/null +++ b/hydroflow_plus/tests/compile_fail.rs @@ -0,0 +1,5 @@ +#[test] +fn test_all() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compile-fail/*.rs"); +} diff --git a/hydroflow_plus_test/examples/first_ten_distributed.rs b/hydroflow_plus_test/examples/first_ten_distributed.rs index 109464ad4f92..2963b1bc0e96 100644 --- a/hydroflow_plus_test/examples/first_ten_distributed.rs +++ b/hydroflow_plus_test/examples/first_ten_distributed.rs @@ -40,8 +40,11 @@ async fn main() { }; let builder = hydroflow_plus::FlowBuilder::new(); - let (external_process, external_port, p1, p2) = - hydroflow_plus_test::distributed::first_ten::first_ten_distributed(&builder); + let external = builder.external_process(); + let p1 = builder.process(); + let p2 = builder.process(); + let external_port = + hydroflow_plus_test::distributed::first_ten::first_ten_distributed(&external, &p1, &p2); let nodes = builder .with_process( &p1, @@ -51,7 +54,7 @@ async fn main() { &p2, TrybuildHost::new(create_host(&mut deployment)).rustflags(rustflags), ) - .with_external(&external_process, deployment.Localhost()) + .with_external(&external, deployment.Localhost()) .deploy(&mut deployment); deployment.deploy().await.unwrap(); diff --git a/hydroflow_plus_test/src/distributed/first_ten.rs b/hydroflow_plus_test/src/distributed/first_ten.rs index aacce28f290f..5efc0c0b2790 100644 --- a/hydroflow_plus_test/src/distributed/first_ten.rs +++ b/hydroflow_plus_test/src/distributed/first_ten.rs @@ -12,33 +12,20 @@ pub struct P1 {} pub struct P2 {} pub fn first_ten_distributed<'a>( - flow: &FlowBuilder<'a>, -) -> ( - ExternalProcess<'a, ()>, - ExternalBincodeSink, - Process<'a, P1>, - Process<'a, P2>, -) { - let external_process = flow.external_process::<()>(); - let process = flow.process::(); - let second_process = flow.process::(); - - let (numbers_external_port, numbers_external) = - external_process.source_external_bincode(&process); + external: &ExternalProcess<'a, ()>, + process: &Process<'a, P1>, + second_process: &Process<'a, P2>, +) -> ExternalBincodeSink { + let (numbers_external_port, numbers_external) = external.source_external_bincode(process); numbers_external.for_each(q!(|n| println!("hi: {:?}", n))); let numbers = process.source_iter(q!(0..10)); numbers .map(q!(|n| SendOverNetwork { n })) - .send_bincode(&second_process) + .send_bincode(second_process) .for_each(q!(|n| println!("{}", n.n))); - ( - external_process, - numbers_external_port, - process, - second_process, - ) + numbers_external_port } #[cfg(test)] @@ -52,25 +39,27 @@ mod tests { let mut deployment = Deployment::new(); let builder = hydroflow_plus::FlowBuilder::new(); - let (external_process, external_port, first_node, second_node) = - super::first_ten_distributed(&builder); + let external = builder.external_process(); + let p1 = builder.process(); + let p2 = builder.process(); + let external_port = super::first_ten_distributed(&external, &p1, &p2); let built = builder.with_default_optimize(); insta::assert_debug_snapshot!(built.ir()); let nodes = built - .with_process(&first_node, deployment.Localhost()) - .with_process(&second_node, deployment.Localhost()) - .with_external(&external_process, deployment.Localhost()) + .with_process(&p1, deployment.Localhost()) + .with_process(&p2, deployment.Localhost()) + .with_external(&external, deployment.Localhost()) .deploy(&mut deployment); deployment.deploy().await.unwrap(); let mut external_port = nodes.connect_sink_bincode(external_port).await; - let mut first_node_stdout = nodes.get_process(&first_node).stdout().await; - let mut second_node_stdout = nodes.get_process(&second_node).stdout().await; + let mut first_node_stdout = nodes.get_process(&p1).stdout().await; + let mut second_node_stdout = nodes.get_process(&p2).stdout().await; deployment.start().await.unwrap(); diff --git a/stageleft/src/lib.rs b/stageleft/src/lib.rs index d0b40c64d1ee..6113fd5979d6 100644 --- a/stageleft/src/lib.rs +++ b/stageleft/src/lib.rs @@ -99,7 +99,7 @@ pub trait QuotedContext { } pub struct BorrowBounds<'a> { - _marker: PhantomData<&'a &'a mut ()>, + _marker: PhantomData &'a ()>, } impl QuotedContext for BorrowBounds<'_> { diff --git a/template/hydroflow_plus/examples/first_ten_distributed.rs b/template/hydroflow_plus/examples/first_ten_distributed.rs index 472a6c7774d6..a84f542c2ad1 100644 --- a/template/hydroflow_plus/examples/first_ten_distributed.rs +++ b/template/hydroflow_plus/examples/first_ten_distributed.rs @@ -5,7 +5,9 @@ async fn main() { let mut deployment = Deployment::new(); let flow = hydroflow_plus::FlowBuilder::new(); - let (p1, p2) = hydroflow_plus_template::first_ten_distributed::first_ten_distributed(&flow); + let p1 = flow.process(); + let p2 = flow.process(); + hydroflow_plus_template::first_ten_distributed::first_ten_distributed(&p1, &p2); let _nodes = flow .with_process(&p1, deployment.Localhost()) diff --git a/template/hydroflow_plus/examples/first_ten_distributed_gcp.rs b/template/hydroflow_plus/examples/first_ten_distributed_gcp.rs index 4193dc98ef95..1386023e4b95 100644 --- a/template/hydroflow_plus/examples/first_ten_distributed_gcp.rs +++ b/template/hydroflow_plus/examples/first_ten_distributed_gcp.rs @@ -18,7 +18,9 @@ async fn main() { let vpc = Arc::new(RwLock::new(GcpNetwork::new(&gcp_project, None))); let flow = hydroflow_plus::FlowBuilder::new(); - let (p1, p2) = hydroflow_plus_template::first_ten_distributed::first_ten_distributed(&flow); + let p1 = flow.process(); + let p2 = flow.process(); + hydroflow_plus_template::first_ten_distributed::first_ten_distributed(&p1, &p2); let _nodes = flow .with_process( diff --git a/template/hydroflow_plus/src/first_ten_distributed.rs b/template/hydroflow_plus/src/first_ten_distributed.rs index 5385d002c726..960526dc6a43 100644 --- a/template/hydroflow_plus/src/first_ten_distributed.rs +++ b/template/hydroflow_plus/src/first_ten_distributed.rs @@ -3,16 +3,9 @@ use hydroflow_plus::*; pub struct P1 {} pub struct P2 {} -pub fn first_ten_distributed<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, P1>, Process<'a, P2>) { - let process = flow.process::(); - let second_process = flow.process::(); - - let numbers = process.source_iter(q!(0..10)); - numbers - .send_bincode(&second_process) - .for_each(q!(|n| println!("{}", n))); - - (process, second_process) +pub fn first_ten_distributed<'a>(p1: &Process<'a, P1>, p2: &Process<'a, P2>) { + let numbers = p1.source_iter(q!(0..10)); + numbers.send_bincode(p2).for_each(q!(|n| println!("{}", n))); } #[cfg(test)] @@ -28,7 +21,9 @@ mod tests { let localhost = deployment.Localhost(); let flow = hydroflow_plus::FlowBuilder::new(); - let (p1, p2) = super::first_ten_distributed(&flow); + let p1 = flow.process(); + let p2 = flow.process(); + super::first_ten_distributed(&p1, &p2); let nodes = flow .with_process(&p1, localhost.clone()) From f2213af316d23423c5f8f22d3f3a6deaa5b43f6a Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 8 Nov 2024 18:39:06 -0800 Subject: [PATCH 52/74] docs(hydroflow_plus): show how to run the `first_ten` example (#1560) --- .../quickstart/first-dataflow.mdx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx index 16d6b00bcc70..8a3c3ef6eff7 100644 --- a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx +++ b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx @@ -44,4 +44,21 @@ To create a `Process`, we call `flow.process()`. After the dataflow has been cre Finally, we call `flow.deploy(&mut deployment)` to provision the dataflow program on the target machine. This returns a struct with handles to the instantiated machines, which we must store in the `_nodes` variable to prevent them from being dropped. Then, we can start the dataflow program and block until `Ctrl-C` using `deployment.run_ctrl_c()`. +We can then launch the program using the following command: + +```bash +#shell-command-next-line +cargo run --example first_ten +[() (process 0)] 0 +[() (process 0)] 1 +[() (process 0)] 2 +[() (process 0)] 3 +[() (process 0)] 4 +[() (process 0)] 5 +[() (process 0)] 6 +[() (process 0)] 7 +[() (process 0)] 8 +[() (process 0)] 9 +``` + In the next section, we will look at how to distribute this program across multiple processes. From 2c17d65838c6c217388005570d552d802d7b3b51 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Sat, 9 Nov 2024 11:17:41 -0800 Subject: [PATCH 53/74] docs: include rustdoc in Docusaurus build (#1561) --- build_docs.bash | 8 +++++++- docs/docusaurus.config.js | 4 ++++ docs/static/.gitignore | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 docs/static/.gitignore diff --git a/build_docs.bash b/build_docs.bash index 00c5768d5130..478f93614b07 100644 --- a/build_docs.bash +++ b/build_docs.bash @@ -14,7 +14,13 @@ cd website_playground CARGO_CFG_HYDROFLOW_GENERATE_DOCS="1" RUSTFLAGS="--cfg procmacro2_semver_exempt --cfg super_unstable" CC="$PWD/../clang+llvm-13.0.0-$PLATFORM/bin/clang" wasm-pack build -cd ../docs +cd .. + +RUSTDOCFLAGS="-Dwarnings" cargo doc --no-deps + +cp -r target/doc docs/static/rustdoc + +cd docs npm ci diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 3fe6ab3c9fd3..846e3e20dd41 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -132,6 +132,10 @@ const config = { type: 'docSidebar', sidebarId: 'deploySidebar', label: 'Hydro Deploy', + }, + { + href: 'pathname:///rustdoc/hydroflow/', + label: 'Rustdoc', } ] }, diff --git a/docs/static/.gitignore b/docs/static/.gitignore new file mode 100644 index 000000000000..1d109a9866c6 --- /dev/null +++ b/docs/static/.gitignore @@ -0,0 +1 @@ +rustdoc/ From 9f3c8c468c58b7ec50d1c104fc24db0920d13c0d Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Tue, 12 Nov 2024 12:38:32 -0800 Subject: [PATCH 54/74] refactor(hydroflow_plus)!: don't re-export all of `hydroflow` (#1562) Reduces namespace pollution when wildcard-importing `hydroflow_plus`. --- hydroflow_plus/src/builder/compiled.rs | 8 +++--- hydroflow_plus/src/builder/deploy.rs | 8 +++--- hydroflow_plus/src/builder/mod.rs | 4 +-- hydroflow_plus/src/deploy/deploy_graph.rs | 25 ++++++++----------- hydroflow_plus/src/deploy/macro_runtime.rs | 20 ++++++--------- hydroflow_plus/src/deploy/trybuild.rs | 19 ++++++++------ hydroflow_plus/src/lib.rs | 3 +-- .../src/local/chat_app.rs | 21 ++++++++-------- .../src/local/count_elems.rs | 13 +++++----- .../src/local/graph_reachability.rs | 14 +++++------ .../src/local/negation.rs | 23 ++++++++--------- .../src/local/teed_join.rs | 23 ++++++++--------- .../src/first_ten_distributed.rs | 2 +- 13 files changed, 88 insertions(+), 95 deletions(-) diff --git a/hydroflow_plus/src/builder/compiled.rs b/hydroflow_plus/src/builder/compiled.rs index 59698115f911..1db322926722 100644 --- a/hydroflow_plus/src/builder/compiled.rs +++ b/hydroflow_plus/src/builder/compiled.rs @@ -31,10 +31,10 @@ impl<'a> CompiledFlow<'a, usize> { let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") .expect("hydroflow_plus should be present in `Cargo.toml`"); let root = match hydroflow_crate { - proc_macro_crate::FoundCrate::Itself => quote! { hydroflow_plus }, + proc_macro_crate::FoundCrate::Itself => quote! { hydroflow_plus::hydroflow }, proc_macro_crate::FoundCrate::Name(name) => { let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); - quote! { #ident } + quote! { #ident::hydroflow } } }; @@ -89,10 +89,10 @@ impl<'a> FreeVariable> for CompiledFlow<'a, ()> { let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") .expect("hydroflow_plus should be present in `Cargo.toml`"); let root = match hydroflow_crate { - proc_macro_crate::FoundCrate::Itself => quote! { hydroflow_plus }, + proc_macro_crate::FoundCrate::Itself => quote! { hydroflow_plus::hydroflow }, proc_macro_crate::FoundCrate::Name(name) => { let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); - quote! { #ident } + quote! { #ident::hydroflow } } }; diff --git a/hydroflow_plus/src/builder/deploy.rs b/hydroflow_plus/src/builder/deploy.rs index bb6690575305..b44936ac2a6a 100644 --- a/hydroflow_plus/src/builder/deploy.rs +++ b/hydroflow_plus/src/builder/deploy.rs @@ -12,14 +12,16 @@ use stageleft::Quoted; use super::built::build_inner; use super::compiled::CompiledFlow; -use crate::deploy::{ExternalSpec, IntoProcessSpec, LocalDeploy, Node, RegisterPort}; +use crate::deploy::{ + ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec, LocalDeploy, Node, ProcessSpec, + RegisterPort, +}; use crate::ir::HfPlusLeaf; use crate::location::external_process::{ ExternalBincodeSink, ExternalBincodeStream, ExternalBytesPort, }; -use crate::location::{ExternalProcess, Location, LocationId}; +use crate::location::{Cluster, ExternalProcess, Location, LocationId, Process}; use crate::staging_util::Invariant; -use crate::{Cluster, ClusterSpec, Deploy, Process, ProcessSpec}; pub struct DeployFlow<'a, D: LocalDeploy<'a>> { pub(super) ir: Vec, diff --git a/hydroflow_plus/src/builder/mod.rs b/hydroflow_plus/src/builder/mod.rs index dd2330fed21d..a28357e1ac22 100644 --- a/hydroflow_plus/src/builder/mod.rs +++ b/hydroflow_plus/src/builder/mod.rs @@ -7,11 +7,11 @@ use compiled::CompiledFlow; use deploy::{DeployFlow, DeployResult}; use stageleft::*; -use crate::deploy::{ExternalSpec, IntoProcessSpec, LocalDeploy}; +use crate::deploy::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec, LocalDeploy}; use crate::ir::HfPlusLeaf; use crate::location::{Cluster, ExternalProcess, Process}; use crate::staging_util::Invariant; -use crate::{ClusterSpec, Deploy, RuntimeContext}; +use crate::RuntimeContext; pub mod built; pub mod compiled; diff --git a/hydroflow_plus/src/deploy/deploy_graph.rs b/hydroflow_plus/src/deploy/deploy_graph.rs index ba5a91ee4a76..44be80c17f58 100644 --- a/hydroflow_plus/src/deploy/deploy_graph.rs +++ b/hydroflow_plus/src/deploy/deploy_graph.rs @@ -13,7 +13,9 @@ use hydro_deploy::hydroflow_crate::ports::{ use hydro_deploy::hydroflow_crate::tracing_options::TracingOptions; use hydro_deploy::hydroflow_crate::HydroflowCrateService; use hydro_deploy::{CustomService, Deployment, Host, HydroflowCrate}; -use hydroflow::futures::StreamExt; +use hydroflow::bytes::Bytes; +use hydroflow::futures::{Sink, SinkExt, Stream, StreamExt}; +use hydroflow::lang::graph::HydroflowGraph; use hydroflow::util::deploy::{ConnectedSink, ConnectedSource}; use nameof::name_of; use serde::de::DeserializeOwned; @@ -27,8 +29,6 @@ use trybuild_internals_api::path; use super::deploy_runtime::*; use super::trybuild::{compile_graph_trybuild, create_trybuild}; use super::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec, Node, ProcessSpec, RegisterPort}; -use crate::futures::SinkExt; -use crate::lang::graph::HydroflowGraph; pub struct HydroDeploy {} @@ -542,49 +542,46 @@ impl<'a> RegisterPort<'a, HydroDeploy> for DeployExternal { fn as_bytes_sink( &self, key: usize, - ) -> impl Future>>> + 'a - { + ) -> impl Future>>> + 'a { let port = self.raw_port(key); async move { let sink = port.connect().await.into_sink(); - sink as Pin>> + sink as Pin>> } } fn as_bincode_sink( &self, key: usize, - ) -> impl Future>>> + 'a { + ) -> impl Future>>> + 'a { let port = self.raw_port(key); async move { let sink = port.connect().await.into_sink(); Box::pin(sink.with(|item| async move { Ok(bincode::serialize(&item).unwrap().into()) })) - as Pin>> + as Pin>> } } fn as_bytes_source( &self, key: usize, - ) -> impl Future>>> + 'a - { + ) -> impl Future>>> + 'a { let port = self.raw_port(key); async move { let source = port.connect().await.into_source(); - Box::pin(source.map(|r| r.unwrap().freeze())) - as Pin>> + Box::pin(source.map(|r| r.unwrap().freeze())) as Pin>> } } fn as_bincode_source( &self, key: usize, - ) -> impl Future>>> + 'a { + ) -> impl Future>>> + 'a { let port = self.raw_port(key); async move { let source = port.connect().await.into_source(); Box::pin(source.map(|item| bincode::deserialize(&item.unwrap()).unwrap())) - as Pin>> + as Pin>> } } } diff --git a/hydroflow_plus/src/deploy/macro_runtime.rs b/hydroflow_plus/src/deploy/macro_runtime.rs index a565b20fb561..5ff3c2533177 100644 --- a/hydroflow_plus/src/deploy/macro_runtime.rs +++ b/hydroflow_plus/src/deploy/macro_runtime.rs @@ -1,13 +1,16 @@ use std::cell::RefCell; +use std::future::Future; use std::pin::Pin; use std::rc::Rc; +use hydroflow::bytes::Bytes; +use hydroflow::futures::{Sink, Stream}; use hydroflow::util::deploy::DeployPorts; +use hydroflow_lang::graph::HydroflowGraph; use stageleft::{Quoted, RuntimeData}; use super::HydroflowPlusMeta; use crate::deploy::{ClusterSpec, Deploy, ExternalSpec, Node, ProcessSpec, RegisterPort}; -use crate::lang::graph::HydroflowGraph; pub struct DeployRuntime {} @@ -197,9 +200,7 @@ impl<'a> RegisterPort<'a, DeployRuntime> for DeployRuntimeNode { fn as_bytes_sink( &self, _key: usize, - ) -> impl std::future::Future< - Output = Pin>>, - > + 'a { + ) -> impl Future>>> + 'a { async { panic!() } } @@ -210,9 +211,7 @@ impl<'a> RegisterPort<'a, DeployRuntime> for DeployRuntimeNode { fn as_bincode_sink( &self, _key: usize, - ) -> impl std::future::Future< - Output = Pin>>, - > + 'a { + ) -> impl Future>>> + 'a { async { panic!() } } @@ -223,9 +222,7 @@ impl<'a> RegisterPort<'a, DeployRuntime> for DeployRuntimeNode { fn as_bytes_source( &self, _key: usize, - ) -> impl std::future::Future< - Output = Pin>>, - > + 'a { + ) -> impl Future>>> + 'a { async { panic!() } } @@ -236,8 +233,7 @@ impl<'a> RegisterPort<'a, DeployRuntime> for DeployRuntimeNode { fn as_bincode_source( &self, _key: usize, - ) -> impl std::future::Future>>> + 'a - { + ) -> impl Future>>> + 'a { async { panic!() } } } diff --git a/hydroflow_plus/src/deploy/trybuild.rs b/hydroflow_plus/src/deploy/trybuild.rs index c9ed72535ff8..a517659579e3 100644 --- a/hydroflow_plus/src/deploy/trybuild.rs +++ b/hydroflow_plus/src/deploy/trybuild.rs @@ -1,14 +1,13 @@ use std::fs; use std::path::PathBuf; +use hydroflow_lang::graph::{partition_graph, HydroflowGraph}; use stageleft::internal::quote; use trybuild_internals_api::cargo::{self, Metadata}; use trybuild_internals_api::env::Update; use trybuild_internals_api::run::{PathDependency, Project}; use trybuild_internals_api::{dependencies, features, path, Runner}; -use crate::lang::graph::{partition_graph, HydroflowGraph}; - pub static IS_TEST: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); pub fn init_test() { @@ -19,26 +18,30 @@ pub fn compile_graph_trybuild(graph: HydroflowGraph, extra_stmts: Vec let partitioned_graph = partition_graph(graph).expect("Failed to partition (cycle detected)."); let mut diagnostics = Vec::new(); - let tokens = - partitioned_graph.as_code("e! { hydroflow_plus }, true, quote!(), &mut diagnostics); + let tokens = partitioned_graph.as_code( + "e! { hydroflow_plus::hydroflow }, + true, + quote!(), + &mut diagnostics, + ); let source_ast: syn::File = syn::parse_quote! { #![feature(box_patterns)] - #![allow(unused_crate_dependencies, missing_docs)] + #![allow(unused_imports, unused_crate_dependencies, missing_docs)] use hydroflow_plus::*; #[allow(unused)] - fn __hfplus_runtime<'a>(__hydroflow_plus_trybuild_cli: &'a hydroflow_plus::util::deploy::DeployPorts) -> hydroflow_plus::Hydroflow<'a> { + fn __hfplus_runtime<'a>(__hydroflow_plus_trybuild_cli: &'a hydroflow_plus::hydroflow::util::deploy::DeployPorts) -> hydroflow_plus::Hydroflow<'a> { #(#extra_stmts)* #tokens } #[tokio::main] async fn main() { - let ports = hydroflow_plus::util::deploy::init_no_ack_start().await; + let ports = hydroflow_plus::hydroflow::util::deploy::init_no_ack_start().await; let flow = __hfplus_runtime(&ports); println!("ack start"); - hydroflow_plus::util::deploy::launch_flow(flow).await; + hydroflow_plus::hydroflow::util::deploy::launch_flow(flow).await; } }; source_ast diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index b71cc879b8e1..c4aaeb977f5e 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -2,8 +2,8 @@ stageleft::stageleft_no_entry_crate!(); +pub use hydroflow; pub use hydroflow::scheduled::graph::Hydroflow; -pub use hydroflow::*; pub use stageleft::*; pub mod runtime_support { @@ -26,7 +26,6 @@ pub mod location; pub use location::{Cluster, ClusterId, Location, Process, Tick}; pub mod deploy; -pub use deploy::{ClusterSpec, Deploy, ProcessSpec}; pub mod cycle; diff --git a/hydroflow_plus_test_local/src/local/chat_app.rs b/hydroflow_plus_test_local/src/local/chat_app.rs index 2c1d82f9ce3f..566b5effbe03 100644 --- a/hydroflow_plus_test_local/src/local/chat_app.rs +++ b/hydroflow_plus_test_local/src/local/chat_app.rs @@ -1,8 +1,7 @@ +use hydroflow::tokio::sync::mpsc::UnboundedSender; +use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::SingleProcessGraph; -use hydroflow_plus::tokio::sync::mpsc::UnboundedSender; -use hydroflow_plus::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::*; -use stageleft::{q, Quoted, RuntimeData}; #[stageleft::entry] pub fn chat_app<'a>( @@ -44,14 +43,14 @@ pub fn chat_app<'a>( #[stageleft::runtime] #[cfg(test)] mod tests { - use hydroflow_plus::assert_graphvis_snapshots; - use hydroflow_plus::util::collect_ready; + use hydroflow::assert_graphvis_snapshots; + use hydroflow::util::collect_ready; #[test] fn test_chat_app_no_replay() { - let (users_send, users) = hydroflow_plus::util::unbounded_channel(); - let (messages_send, messages) = hydroflow_plus::util::unbounded_channel(); - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (users_send, users) = hydroflow::util::unbounded_channel(); + let (messages_send, messages) = hydroflow::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut chat_server = super::chat_app!(users, messages, &out, false); assert_graphvis_snapshots!(chat_server); @@ -92,9 +91,9 @@ mod tests { #[test] fn test_chat_app_replay() { - let (users_send, users) = hydroflow_plus::util::unbounded_channel(); - let (messages_send, messages) = hydroflow_plus::util::unbounded_channel(); - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (users_send, users) = hydroflow::util::unbounded_channel(); + let (messages_send, messages) = hydroflow::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut chat_server = super::chat_app!(users, messages, &out, true); assert_graphvis_snapshots!(chat_server); diff --git a/hydroflow_plus_test_local/src/local/count_elems.rs b/hydroflow_plus_test_local/src/local/count_elems.rs index 275c956d7e52..8e25dedf0db3 100644 --- a/hydroflow_plus_test_local/src/local/count_elems.rs +++ b/hydroflow_plus_test_local/src/local/count_elems.rs @@ -1,8 +1,7 @@ +use hydroflow::tokio::sync::mpsc::UnboundedSender; +use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::SingleProcessGraph; -use hydroflow_plus::tokio::sync::mpsc::UnboundedSender; -use hydroflow_plus::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::*; -use stageleft::{q, Quoted, RuntimeData}; pub fn count_elems_generic<'a, T: 'a>( flow: FlowBuilder<'a>, @@ -38,13 +37,13 @@ pub fn count_elems<'a>( #[stageleft::runtime] #[cfg(test)] mod tests { - use hydroflow_plus::assert_graphvis_snapshots; - use hydroflow_plus::util::collect_ready; + use hydroflow::assert_graphvis_snapshots; + use hydroflow::util::collect_ready; #[test] pub fn test_count() { - let (in_send, input) = hydroflow_plus::util::unbounded_channel(); - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (in_send, input) = hydroflow::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut count = super::count_elems!(input, &out); assert_graphvis_snapshots!(count); diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index b03aefb230e2..d88a42fa3e62 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -1,6 +1,6 @@ +use hydroflow::tokio::sync::mpsc::UnboundedSender; +use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::SingleProcessGraph; -use hydroflow_plus::tokio::sync::mpsc::UnboundedSender; -use hydroflow_plus::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::*; #[stageleft::entry] @@ -35,14 +35,14 @@ pub fn graph_reachability<'a>( #[stageleft::runtime] #[cfg(test)] mod tests { - use hydroflow_plus::assert_graphvis_snapshots; - use hydroflow_plus::util::collect_ready; + use hydroflow::assert_graphvis_snapshots; + use hydroflow::util::collect_ready; #[test] pub fn test_reachability() { - let (roots_send, roots) = hydroflow_plus::util::unbounded_channel(); - let (edges_send, edges) = hydroflow_plus::util::unbounded_channel(); - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (roots_send, roots) = hydroflow::util::unbounded_channel(); + let (edges_send, edges) = hydroflow::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut reachability = super::graph_reachability!(roots, edges, &out); assert_graphvis_snapshots!(reachability); diff --git a/hydroflow_plus_test_local/src/local/negation.rs b/hydroflow_plus_test_local/src/local/negation.rs index b771dd760353..a0924f70a93b 100644 --- a/hydroflow_plus_test_local/src/local/negation.rs +++ b/hydroflow_plus_test_local/src/local/negation.rs @@ -1,7 +1,6 @@ +use hydroflow::tokio::sync::mpsc::UnboundedSender; use hydroflow_plus::deploy::SingleProcessGraph; -use hydroflow_plus::tokio::sync::mpsc::UnboundedSender; use hydroflow_plus::*; -use stageleft::{q, Quoted, RuntimeData}; #[stageleft::entry] pub fn test_difference<'a>( @@ -63,12 +62,12 @@ pub fn test_anti_join<'a>( #[stageleft::runtime] #[cfg(test)] mod tests { - use hydroflow_plus::assert_graphvis_snapshots; - use hydroflow_plus::util::collect_ready; + use hydroflow::assert_graphvis_snapshots; + use hydroflow::util::collect_ready; #[test] fn test_difference_tick_tick() { - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut flow = super::test_difference!(&out, false, false); assert_graphvis_snapshots!(flow); @@ -84,7 +83,7 @@ mod tests { #[test] fn test_difference_tick_static() { - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut flow = super::test_difference!(&out, false, true); assert_graphvis_snapshots!(flow); @@ -100,7 +99,7 @@ mod tests { #[test] fn test_difference_static_tick() { - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut flow = super::test_difference!(&out, true, false); assert_graphvis_snapshots!(flow); @@ -119,7 +118,7 @@ mod tests { #[test] fn test_difference_static_static() { - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut flow = super::test_difference!(&out, true, true); assert_graphvis_snapshots!(flow); @@ -135,7 +134,7 @@ mod tests { #[test] fn test_anti_join_tick_tick() { - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut flow = super::test_anti_join!(&out, false, false); assert_graphvis_snapshots!(flow); @@ -151,7 +150,7 @@ mod tests { #[test] fn test_anti_join_tick_static() { - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut flow = super::test_anti_join!(&out, false, true); assert_graphvis_snapshots!(flow); @@ -167,7 +166,7 @@ mod tests { #[test] fn test_anti_join_static_tick() { - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut flow = super::test_anti_join!(&out, true, false); assert_graphvis_snapshots!(flow); @@ -186,7 +185,7 @@ mod tests { #[test] fn test_anti_join_static_static() { - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut flow = super::test_anti_join!(&out, true, true); assert_graphvis_snapshots!(flow); diff --git a/hydroflow_plus_test_local/src/local/teed_join.rs b/hydroflow_plus_test_local/src/local/teed_join.rs index 3771e481a7e7..36e575f4109e 100644 --- a/hydroflow_plus_test_local/src/local/teed_join.rs +++ b/hydroflow_plus_test_local/src/local/teed_join.rs @@ -1,9 +1,8 @@ +use hydroflow::futures::stream::Stream; +use hydroflow::tokio::sync::mpsc::UnboundedSender; +use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::MultiGraph; -use hydroflow_plus::futures::stream::Stream; -use hydroflow_plus::tokio::sync::mpsc::UnboundedSender; -use hydroflow_plus::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::*; -use stageleft::{q, Quoted, RuntimeData}; struct N0 {} struct N1 {} @@ -48,13 +47,13 @@ pub fn teed_join<'a, S: Stream + Unpin + 'a>( #[stageleft::runtime] #[cfg(test)] mod tests { - use hydroflow_plus::assert_graphvis_snapshots; - use hydroflow_plus::util::collect_ready; + use hydroflow::assert_graphvis_snapshots; + use hydroflow::util::collect_ready; #[test] fn test_teed_join() { - let (in_send, input) = hydroflow_plus::util::unbounded_channel(); - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (in_send, input) = hydroflow::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut joined = super::teed_join!(input, &out, false, 0); assert_graphvis_snapshots!(joined); @@ -71,8 +70,8 @@ mod tests { #[test] fn test_teed_join_twice() { - let (in_send, input) = hydroflow_plus::util::unbounded_channel(); - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (in_send, input) = hydroflow::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut joined = super::teed_join!(input, &out, true, 0); assert_graphvis_snapshots!(joined); @@ -89,8 +88,8 @@ mod tests { #[test] fn test_teed_join_multi_node() { - let (_, input) = hydroflow_plus::util::unbounded_channel(); - let (out, mut out_recv) = hydroflow_plus::util::unbounded_channel(); + let (_, input) = hydroflow::util::unbounded_channel(); + let (out, mut out_recv) = hydroflow::util::unbounded_channel(); let mut joined = super::teed_join!(input, &out, true, 1); assert_graphvis_snapshots!(joined); diff --git a/template/hydroflow_plus/src/first_ten_distributed.rs b/template/hydroflow_plus/src/first_ten_distributed.rs index 960526dc6a43..c325b6973191 100644 --- a/template/hydroflow_plus/src/first_ten_distributed.rs +++ b/template/hydroflow_plus/src/first_ten_distributed.rs @@ -12,7 +12,7 @@ pub fn first_ten_distributed<'a>(p1: &Process<'a, P1>, p2: &Process<'a, P2>) { mod tests { use hydro_deploy::Deployment; use hydroflow_plus::deploy::DeployCrateWrapper; - use hydroflow_plus::futures::StreamExt; + use hydroflow_plus::hydroflow::futures::StreamExt; use tokio_stream::wrappers::UnboundedReceiverStream; #[tokio::test] From eb1ad3a54705efb06ee3f0647deaa9a52731ae6e Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 14 Nov 2024 16:56:23 -0800 Subject: [PATCH 55/74] fix(hydroflow_plus)!: rename `union` to `chain` and restrict LHS to be bounded (#1565) Returning a `Stream` from `union` on unbounded streams was unsound, since the order of outputs is not deterministic. --- hydroflow_lang/src/graph/ops/chain.rs | 42 ++++++ hydroflow_lang/src/graph/ops/mod.rs | 1 + hydroflow_plus/src/ir.rs | 18 +-- hydroflow_plus/src/optional.rs | 8 +- hydroflow_plus/src/rewrites/persist_pullup.rs | 4 +- hydroflow_plus/src/singleton.rs | 2 +- hydroflow_plus/src/stream.rs | 24 ++-- hydroflow_plus_test/src/cluster/paxos.rs | 15 +- .../src/cluster/paxos_bench.rs | 10 +- hydroflow_plus_test/src/cluster/paxos_kv.rs | 2 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 52 +++---- .../src/local/graph_reachability.rs | 14 +- ...ity__tests__reachability@graphvis_dot.snap | 132 ++++++++++++------ ..._tests__reachability@graphvis_mermaid.snap | 109 +++++++++------ 14 files changed, 275 insertions(+), 158 deletions(-) create mode 100644 hydroflow_lang/src/graph/ops/chain.rs diff --git a/hydroflow_lang/src/graph/ops/chain.rs b/hydroflow_lang/src/graph/ops/chain.rs new file mode 100644 index 000000000000..96006fc396af --- /dev/null +++ b/hydroflow_lang/src/graph/ops/chain.rs @@ -0,0 +1,42 @@ +use crate::graph::PortIndexValue; + +use super::{ + DelayType, OperatorCategory, OperatorConstraints, RANGE_0, RANGE_1 +}; + +/// > 2 input streams of the same type, 1 output stream of the same type +/// +/// Chains together a pair of streams, with all the elements of the first emitted before the second. +/// +/// Since `chain` has multiple input streams, it needs to be assigned to +/// a variable to reference its multiple input ports across statements. +/// +/// ```hydroflow +/// source_iter(vec!["hello", "world"]) -> [0]my_chain; +/// source_iter(vec!["stay", "gold"]) -> [1]my_chain; +/// my_chain = chain() +/// -> map(|x| x.to_uppercase()) +/// -> assert_eq(["HELLO", "WORLD", "STAY", "GOLD"]); +/// ``` +pub const CHAIN: OperatorConstraints = OperatorConstraints { + name: "chain", + categories: &[OperatorCategory::MultiIn], + persistence_args: RANGE_0, + type_args: RANGE_0, + hard_range_inn: &(2..=2), + soft_range_inn: &(2..=2), + hard_range_out: RANGE_1, + soft_range_out: RANGE_1, + num_args: 0, + is_external_input: false, + has_singleton_output: false, + ports_inn: None, + ports_out: None, + input_delaytype_fn: |idx| match idx { + PortIndexValue::Int(idx) if idx.value == 0 => { + Some(DelayType::Stratum) + } + _else => None, + }, + write_fn: super::union::UNION.write_fn, +}; diff --git a/hydroflow_lang/src/graph/ops/mod.rs b/hydroflow_lang/src/graph/ops/mod.rs index 2843e2f5b0d3..52ad4105e03c 100644 --- a/hydroflow_lang/src/graph/ops/mod.rs +++ b/hydroflow_lang/src/graph/ops/mod.rs @@ -244,6 +244,7 @@ declare_ops![ anti_join_multiset::ANTI_JOIN_MULTISET, assert::ASSERT, assert_eq::ASSERT_EQ, + chain::CHAIN, cross_join::CROSS_JOIN, cross_join_multiset::CROSS_JOIN_MULTISET, cross_singleton::CROSS_SINGLETON, diff --git a/hydroflow_plus/src/ir.rs b/hydroflow_plus/src/ir.rs index 8f8aee468bf1..4006bc3b5df9 100644 --- a/hydroflow_plus/src/ir.rs +++ b/hydroflow_plus/src/ir.rs @@ -286,7 +286,7 @@ pub enum HfPlusNode { Unpersist(Box), Delta(Box), - Union(Box, Box), + Chain(Box, Box), CrossProduct(Box, Box), CrossSingleton(Box, Box), Join(Box, Box), @@ -457,7 +457,7 @@ impl<'a> HfPlusNode { HfPlusNode::Unpersist(inner) => transform(inner.as_mut(), seen_tees), HfPlusNode::Delta(inner) => transform(inner.as_mut(), seen_tees), - HfPlusNode::Union(left, right) => { + HfPlusNode::Chain(left, right) => { transform(left.as_mut(), seen_tees); transform(right.as_mut(), seen_tees); } @@ -678,7 +678,7 @@ impl<'a> HfPlusNode { } } - HfPlusNode::Union(left, right) => { + HfPlusNode::Chain(left, right) => { let (left_ident, left_location_id) = left.emit(graph_builders, built_tees, next_stmt_id); let (right_ident, right_location_id) = @@ -686,29 +686,29 @@ impl<'a> HfPlusNode { assert_eq!( left_location_id, right_location_id, - "union inputs must be in the same location" + "chain inputs must be in the same location" ); let union_id = *next_stmt_id; *next_stmt_id += 1; - let union_ident = + let chain_ident = syn::Ident::new(&format!("stream_{}", union_id), Span::call_site()); let builder = graph_builders.entry(left_location_id).or_default(); builder.add_statement(parse_quote! { - #union_ident = union(); + #chain_ident = chain(); }); builder.add_statement(parse_quote! { - #left_ident -> [0]#union_ident; + #left_ident -> [0]#chain_ident; }); builder.add_statement(parse_quote! { - #right_ident -> [1]#union_ident; + #right_ident -> [1]#chain_ident; }); - (union_ident, left_location_id) + (chain_ident, left_location_id) } HfPlusNode::CrossSingleton(left, right) => { diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index e5530352bd02..4387101308bd 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -240,7 +240,7 @@ impl<'a, T, L: Location<'a>, B> Optional { if L::is_top_level() { Optional::new( self.location, - HfPlusNode::Persist(Box::new(HfPlusNode::Union( + HfPlusNode::Persist(Box::new(HfPlusNode::Chain( Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), Box::new(HfPlusNode::Unpersist(Box::new(other.ir_node.into_inner()))), ))), @@ -248,7 +248,7 @@ impl<'a, T, L: Location<'a>, B> Optional { } else { Optional::new( self.location, - HfPlusNode::Union( + HfPlusNode::Chain( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), ), @@ -288,7 +288,7 @@ impl<'a, T, L: Location<'a>, B> Optional { if L::is_top_level() { Singleton::new( self.location, - HfPlusNode::Persist(Box::new(HfPlusNode::Union( + HfPlusNode::Persist(Box::new(HfPlusNode::Chain( Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), Box::new(HfPlusNode::Unpersist(Box::new(other.ir_node.into_inner()))), ))), @@ -296,7 +296,7 @@ impl<'a, T, L: Location<'a>, B> Optional { } else { Singleton::new( self.location, - HfPlusNode::Union( + HfPlusNode::Chain( Box::new(self.ir_node.into_inner()), Box::new(other.ir_node.into_inner()), ), diff --git a/hydroflow_plus/src/rewrites/persist_pullup.rs b/hydroflow_plus/src/rewrites/persist_pullup.rs index 50ead7f9e2f4..21c2a130eb99 100644 --- a/hydroflow_plus/src/rewrites/persist_pullup.rs +++ b/hydroflow_plus/src/rewrites/persist_pullup.rs @@ -88,8 +88,8 @@ fn persist_pullup_node( input: behind_persist, })), - HfPlusNode::Union(box HfPlusNode::Persist(left), box HfPlusNode::Persist(right)) => { - HfPlusNode::Persist(Box::new(HfPlusNode::Union(left, right))) + HfPlusNode::Chain(box HfPlusNode::Persist(left), box HfPlusNode::Persist(right)) => { + HfPlusNode::Persist(Box::new(HfPlusNode::Chain(left, right))) } HfPlusNode::CrossProduct(box HfPlusNode::Persist(left), box HfPlusNode::Persist(right)) => { diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index b068d1d9bd1f..834eef5aea28 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -57,7 +57,7 @@ impl<'a, T, L: Location<'a>> CycleCollectionWithInitial<'a, TickCycleMarker> let location_id = location.id(); Singleton::new( location, - HfPlusNode::Union( + HfPlusNode::Chain( Box::new(HfPlusNode::CycleSource { ident, location_kind: location_id, diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index fc766eab4a04..6dcaf3eed927 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -260,18 +260,6 @@ impl<'a, T, L: Location<'a>, B> Stream { ) } - pub fn union(self, other: Stream) -> Stream { - check_matching_location(&self.location, &other.location); - - Stream::new( - self.location, - HfPlusNode::Union( - Box::new(self.ir_node.into_inner()), - Box::new(other.ir_node.into_inner()), - ), - ) - } - pub fn enumerate(self) -> Stream<(usize, T), L, B> { Stream::new( self.location, @@ -402,6 +390,18 @@ impl<'a, T, L: Location<'a>> Stream { HfPlusNode::Sort(Box::new(self.ir_node.into_inner())), ) } + + pub fn chain(self, other: Stream) -> Stream { + check_matching_location(&self.location, &other.location); + + Stream::new( + self.location, + HfPlusNode::Chain( + Box::new(self.ir_node.into_inner()), + Box::new(other.ir_node.into_inner()), + ), + ) + } } impl<'a, K, V1, L: Location<'a>, B> Stream<(K, V1), L, B> { diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 26ac4a535675..c2aa8ce4a882 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -115,7 +115,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( recommit_after_leader_election(proposers, p_relevant_p1bs, p_ballot_num.clone(), f); let p_log_to_recommit = p_log_to_try_commit - .union(p_log_holes) + .chain(p_log_holes) .continue_if(just_became_leader); // Only resend p1b stuff once the moment we become leader. let (p_to_replicas, a_log, a_to_proposers_p2b) = sequence_payload( @@ -226,9 +226,12 @@ fn p_max_ballot<'a>( p_received_p2b_ballots: Stream, Unbounded>, p_to_proposers_i_am_leader: Stream, Unbounded>, ) -> Singleton, Unbounded> { + let ballot_batcher = proposers.tick(); p_received_p1b_ballots - .union(p_received_p2b_ballots) - .union(p_to_proposers_i_am_leader) + .tick_batch(&ballot_batcher) + .chain(p_received_p2b_ballots.tick_batch(&ballot_batcher)) + .chain(p_to_proposers_i_am_leader.tick_batch(&ballot_batcher)) + .all_ticks() .max() .unwrap_or(proposers.singleton(q!(Ballot { num: 0, @@ -624,7 +627,7 @@ fn p_p2a<'a, P: PaxosPayload>( })); // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); let p_to_acceptors_p2a = p_log_to_recommit - .union(p_indexed_payloads.clone()) + .chain(p_indexed_payloads.clone()) .continue_if(p_is_leader.clone()) .all_ticks() .broadcast_bincode_interleaved(acceptors); @@ -694,7 +697,7 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( } )); let a_log = a_p2as_to_place_in_log - .union(a_new_checkpoint.into_stream()) + .chain(a_new_checkpoint.into_stream()) .persist() .fold( q!(|| (None, HashMap::new())), @@ -753,7 +756,7 @@ fn p_p2b<'a, P: PaxosPayload>( let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposer_tick.cycle(); let p_p2b = a_to_proposers_p2b .tick_batch(proposer_tick) - .union(p_persisted_p2bs); + .chain(p_persisted_p2bs); let p_count_matching_p2bs = p_p2b .clone() .filter_map(q!(|p2b| if p2b.ballot == p2b.max_ballot { diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index c602512bf2f4..49159f00fafc 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -123,7 +123,7 @@ fn bench_client<'a>( replica_payload.key, sender ))) - .union(c_pending_quorum_payloads); + .chain(c_pending_quorum_payloads); let c_received_quorum_payloads = c_received_payloads .clone() .fold_keyed( @@ -152,7 +152,7 @@ fn bench_client<'a>( ))); c_to_proposers_complete_cycle.complete( c_new_payloads_when_leader_elected - .union(c_new_payloads_when_committed) + .chain(c_new_payloads_when_committed) .all_ticks(), ); @@ -169,8 +169,8 @@ fn bench_client<'a>( .map(q!(|key| (key as usize, SystemTime::now()))); let c_new_timers = c_timers .clone() // Update c_timers in tick+1 so we can record differences during this tick (to track latency) - .union(c_new_timers_when_leader_elected) - .union(c_updated_timers.clone()) + .chain(c_new_timers_when_leader_elected) + .chain(c_updated_timers.clone()) .reduce_keyed(q!(|curr_time, new_time| { if new_time > *curr_time { *curr_time = new_time; @@ -190,7 +190,7 @@ fn bench_client<'a>( .map(q!(|(_virtual_id, (prev_time, curr_time))| Some( curr_time.duration_since(prev_time).unwrap().as_micros() ))) - .union(c_latency_reset.into_stream()) + .chain(c_latency_reset.into_stream()) .all_ticks() .flatten() .fold( diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 42048ff104ac..cca964893f46 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -103,7 +103,7 @@ pub fn replica<'a, K: KvKey, V: KvValue>( // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); let r_sorted_payloads = p_to_replicas .tick_batch(&replica_tick) - .union(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet + .chain(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet .sort(); // Create a cycle since we'll use this seq before we define it let (r_highest_seq_complete_cycle, r_highest_seq) = diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index df35cf749539..4bd55606df93 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -40,12 +40,12 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , proposer_id : p_id , }) { received_max_ballot . num + 1 } else { ballot_num } } }), input: CrossSingleton( Tee { - inner: : Union( + inner: : Chain( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( - Union( - Union( + Chain( + Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), input: CycleSource { @@ -93,7 +93,7 @@ expression: built.ir() ), }, Tee { - inner: : Union( + inner: : Chain( CycleSource { ident: Ident { sym: cycle_4, @@ -358,7 +358,7 @@ expression: built.ir() }, }, Tee { - inner: : Union( + inner: : Chain( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( @@ -482,11 +482,11 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( - Union( + Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( - Union( + Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | max_slot + 1 }), input: Tee { @@ -656,7 +656,7 @@ expression: built.ir() input: FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { - inner: : Union( + inner: : Chain( Tee { inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), @@ -731,11 +731,11 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( - Union( + Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( - Union( + Chain( FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } } }), input: CrossSingleton( @@ -896,7 +896,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in (prev_checkpoint . unwrap_or (0)) .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = Some (new_checkpoint) ; } CheckpointOrP2a :: P2a (p2a) => { if prev_checkpoint . map (| prev | p2a . slot > prev) . unwrap_or (true) && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), input: Persist( - Union( + Chain( FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( @@ -1006,7 +1006,7 @@ expression: built.ir() sym: cycle_1, }, location_kind: Tick( - 4, + 5, Cluster( 3, ), @@ -1019,7 +1019,7 @@ expression: built.ir() input: CrossSingleton( Tee { inner: : Sort( - Union( + Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { @@ -1081,7 +1081,7 @@ expression: built.ir() sym: cycle_1, }, location_kind: Tick( - 4, + 5, Cluster( 3, ), @@ -1100,7 +1100,7 @@ expression: built.ir() Tee { inner: , }, - Union( + Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < usize > > ({ use hydroflow_plus :: __staged :: optional :: * ; | v | Some (v) }), input: CycleSource { @@ -1108,7 +1108,7 @@ expression: built.ir() sym: cycle_2, }, location_kind: Tick( - 4, + 5, Cluster( 3, ), @@ -1140,7 +1140,7 @@ expression: built.ir() sym: cycle_2, }, location_kind: Tick( - 4, + 5, Cluster( 3, ), @@ -1180,7 +1180,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 4, + 5, Cluster( 3, ), @@ -1190,7 +1190,7 @@ expression: built.ir() inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , usize) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if max_checkpointed_seq . map (| m | new_highest_seq - m >= checkpoint_frequency) . unwrap_or (true) { Some (new_highest_seq) } else { None } }), input: CrossSingleton( - Union( + Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < usize > > ({ use hydroflow_plus :: __staged :: optional :: * ; | v | Some (v) }), input: Reduce { @@ -1201,7 +1201,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 4, + 5, Cluster( 3, ), @@ -1304,7 +1304,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Union( + inner: : Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , (u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), input: Network { @@ -1383,7 +1383,7 @@ expression: built.ir() location_kind: Cluster( 2, ), - input: Union( + input: Chain( FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , KvPayload { key : i as u32 , value : c_id })) }), input: Tee { @@ -1435,8 +1435,8 @@ expression: built.ir() input: DeferTick( ReduceKeyed { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < std :: time :: SystemTime , std :: time :: SystemTime , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_time , new_time | { if new_time > * curr_time { * curr_time = new_time ; } } }), - input: Union( - Union( + input: Chain( + Chain( Tee { inner: : CycleSource { ident: Ident { @@ -1486,7 +1486,7 @@ expression: built.ir() input: Persist( FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < u128 > , core :: option :: Option < u128 > > ({ use hydroflow_plus :: __staged :: stream :: * ; | d | d }), - input: Union( + input: Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( @@ -1522,7 +1522,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (usize , bool) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | total , (batch_size , reset) | { if reset { * total = 0 ; } else { * total += batch_size ; } } }), input: Persist( - Union( + Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | batch_size | (batch_size , false) }), input: Map { diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index d88a42fa3e62..8483c6f0aa80 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -15,17 +15,18 @@ pub fn graph_reachability<'a>( let roots = process.source_stream(roots); let edges = process.source_stream(edges); - let (set_reached_cycle, reached_cycle) = process.forward_ref(); + let reachability_tick = process.tick(); + let (set_reached_cycle, reached_cycle) = reachability_tick.cycle(); - let reached = roots.union(reached_cycle); + let reached = roots.tick_batch(&reachability_tick).chain(reached_cycle); let reachable = reached .clone() .map(q!(|r| (r, ()))) - .join(edges) + .join(edges.tick_batch(&reachability_tick).persist()) .map(q!(|(_from, (_, to))| to)); - set_reached_cycle.complete(reachable); + set_reached_cycle.complete_next_tick(reached.clone().chain(reachable)); - reached.unique().for_each(q!(|v| { + reached.all_ticks().unique().for_each(q!(|v| { reached_out.send(v).unwrap(); })); @@ -55,6 +56,9 @@ mod tests { edges_send.send((3, 4)).unwrap(); edges_send.send((4, 5)).unwrap(); + reachability.run_tick(); + reachability.run_tick(); + reachability.run_tick(); reachability.run_tick(); assert_eq!( diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_dot.snap index 9cf66d710d8e..7e84019f8a9c 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_dot.snap @@ -6,91 +6,131 @@ digraph { node [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace", style=filled]; edge [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace"]; n1v1 [label="(n1v1) source_stream(roots)", shape=invhouse, fillcolor="#88aaff"] - n2v1 [label="(n2v1) union()", shape=invhouse, fillcolor="#88aaff"] + n2v1 [label="(n2v1) chain()", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) tee()", shape=house, fillcolor="#ffff88"] n4v1 [label="(n4v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (u32, ()),\l >({\l use crate::__staged::local::graph_reachability::*;\l |r| (r, ())\l }),\l)\l", shape=house, fillcolor="#ffff88"] n5v1 [label="(n5v1) source_stream(edges)", shape=invhouse, fillcolor="#88aaff"] - n6v1 [label="(n6v1) join_multiset::<'static, 'static>()", shape=invhouse, fillcolor="#88aaff"] - n7v1 [label="(n7v1) multiset_delta()", shape=invhouse, fillcolor="#88aaff"] - n8v1 [label="(n8v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, ((), u32)),\l u32,\l >({\l use crate::__staged::local::graph_reachability::*;\l |(_from, (_, to))| to\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] - n9v1 [label="(n9v1) persist::<'static>()", shape=house, fillcolor="#ffff88"] - n10v1 [label="(n10v1) unique::<'tick>()", shape=house, fillcolor="#ffff88"] - n11v1 [label="(n11v1) multiset_delta()", shape=house, fillcolor="#ffff88"] - n12v1 [label="(n12v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::graph_reachability::*;\l let reached_out = reached_out;\l |v| {\l reached_out.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] - n13v1 [label="(n13v1) handoff", shape=parallelogram, fillcolor="#ddddff"] - n1v1 -> n2v1 [label="0"] - n8v1 -> n2v1 [label="1"] + n6v1 [label="(n6v1) join_multiset::<'tick, 'static>()", shape=invhouse, fillcolor="#88aaff"] + n7v1 [label="(n7v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, ((), u32)),\l u32,\l >({\l use crate::__staged::local::graph_reachability::*;\l |(_from, (_, to))| to\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] + n8v1 [label="(n8v1) chain()", shape=invhouse, fillcolor="#88aaff"] + n9v1 [label="(n9v1) defer_tick_lazy()", shape=invhouse, fillcolor="#88aaff"] + n10v1 [label="(n10v1) persist::<'static>()", shape=house, fillcolor="#ffff88"] + n11v1 [label="(n11v1) unique::<'tick>()", shape=house, fillcolor="#ffff88"] + n12v1 [label="(n12v1) multiset_delta()", shape=house, fillcolor="#ffff88"] + n13v1 [label="(n13v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::graph_reachability::*;\l let reached_out = reached_out;\l |v| {\l reached_out.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n14v1 [label="(n14v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n15v1 [label="(n15v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n16v1 [label="(n16v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n17v1 [label="(n17v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n18v1 [label="(n18v1) identity()", shape=invhouse, fillcolor="#88aaff"] + n19v1 [label="(n19v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n20v1 [label="(n20v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n1v1 -> n14v1 + n9v1 -> n2v1 [label="1"] n2v1 -> n3v1 n3v1 -> n4v1 - n4v1 -> n13v1 - n5v1 -> n6v1 [label="1"] + n4v1 -> n15v1 + n5v1 -> n20v1 n6v1 -> n7v1 - n7v1 -> n8v1 - n3v1 -> n9v1 - n9v1 -> n10v1 + n3v1 -> n16v1 + n7v1 -> n8v1 [label="1"] + n8v1 -> n17v1 + n3v1 -> n10v1 n10v1 -> n11v1 n11v1 -> n12v1 - n13v1 -> n6v1 [label="0"] + n12v1 -> n13v1 + n14v1 -> n2v1 [label="0", color=red] + n15v1 -> n6v1 [label="0"] + n16v1 -> n8v1 [label="0", color=red] + n17v1 -> n18v1 + n18v1 -> n19v1 + n19v1 -> n9v1 [color=red] + n20v1 -> n6v1 [label="1"] subgraph "cluster n1v1" { fillcolor="#dddddd" style=filled label = "sg_1v1\nstratum 0" - n13v1 n1v1 - n5v1 + subgraph "cluster_sg_1v1_var_stream_0" { + label="var stream_0" + n1v1 + } + } + subgraph "cluster n2v1" { + fillcolor="#dddddd" + style=filled + label = "sg_2v1\nstratum 2" n6v1 n7v1 n8v1 + subgraph "cluster_sg_2v1_var_stream_5" { + label="var stream_5" + n6v1 + } + subgraph "cluster_sg_2v1_var_stream_6" { + label="var stream_6" + n7v1 + } + subgraph "cluster_sg_2v1_var_stream_7" { + label="var stream_7" + n8v1 + } + } + subgraph "cluster n3v1" { + fillcolor="#dddddd" + style=filled + label = "sg_3v1\nstratum 1" + n9v1 n2v1 n3v1 n4v1 - n9v1 n10v1 n11v1 n12v1 - subgraph "cluster_sg_1v1_var_stream_0" { - label="var stream_0" - n1v1 - } - subgraph "cluster_sg_1v1_var_stream_1" { + n13v1 + subgraph "cluster_sg_3v1_var_stream_1" { label="var stream_1" n2v1 } - subgraph "cluster_sg_1v1_var_stream_10" { + subgraph "cluster_sg_3v1_var_stream_10" { label="var stream_10" n11v1 } - subgraph "cluster_sg_1v1_var_stream_2" { + subgraph "cluster_sg_3v1_var_stream_11" { + label="var stream_11" + n12v1 + } + subgraph "cluster_sg_3v1_var_stream_2" { label="var stream_2" n3v1 } - subgraph "cluster_sg_1v1_var_stream_3" { + subgraph "cluster_sg_3v1_var_stream_3" { label="var stream_3" n4v1 } - subgraph "cluster_sg_1v1_var_stream_4" { - label="var stream_4" - n5v1 - } - subgraph "cluster_sg_1v1_var_stream_5" { - label="var stream_5" - n6v1 - } - subgraph "cluster_sg_1v1_var_stream_6" { - label="var stream_6" - n7v1 - } - subgraph "cluster_sg_1v1_var_stream_7" { - label="var stream_7" - n8v1 - } - subgraph "cluster_sg_1v1_var_stream_8" { + subgraph "cluster_sg_3v1_var_stream_8" { label="var stream_8" n9v1 } - subgraph "cluster_sg_1v1_var_stream_9" { + subgraph "cluster_sg_3v1_var_stream_9" { label="var stream_9" n10v1 } } + subgraph "cluster n4v1" { + fillcolor="#dddddd" + style=filled + label = "sg_4v1\nstratum 3" + n18v1 + } + subgraph "cluster n5v1" { + fillcolor="#dddddd" + style=filled + label = "sg_5v1\nstratum 0" + n5v1 + subgraph "cluster_sg_5v1_var_stream_4" { + label="var stream_4" + n5v1 + } + } } diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_mermaid.snap index 29f937404769..3ddd47390ae3 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_mermaid.snap @@ -9,76 +9,103 @@ classDef pushClass fill:#ff8,stroke:#000,text-align:left,white-space:pre classDef otherClass fill:#fdc,stroke:#000,text-align:left,white-space:pre linkStyle default stroke:#aaa 1v1[\"(1v1) source_stream(roots)"/]:::pullClass -2v1[\"(2v1) union()"/]:::pullClass +2v1[\"(2v1) chain()"/]:::pullClass 3v1[/"(3v1) tee()"\]:::pushClass 4v1[/"
(4v1)
map(
stageleft::runtime_support::fn1_type_hint::<
u32,
(u32, ()),
>({
use crate::__staged::local::graph_reachability::*;
|r| (r, ())
}),
)
"\]:::pushClass 5v1[\"(5v1) source_stream(edges)"/]:::pullClass -6v1[\"(6v1) join_multiset::<'static, 'static>()"/]:::pullClass -7v1[\"(7v1) multiset_delta()"/]:::pullClass -8v1[\"
(8v1)
map(
stageleft::runtime_support::fn1_type_hint::<
(u32, ((), u32)),
u32,
>({
use crate::__staged::local::graph_reachability::*;
|(_from, (_, to))| to
}),
)
"/]:::pullClass -9v1[/"(9v1) persist::<'static>()"\]:::pushClass -10v1[/"(10v1) unique::<'tick>()"\]:::pushClass -11v1[/"(11v1) multiset_delta()"\]:::pushClass -12v1[/"
(12v1)
for_each(
stageleft::runtime_support::fn1_type_hint::<
u32,
(),
>({
use crate::__staged::local::graph_reachability::*;
let reached_out = reached_out;
|v| {
reached_out.send(v).unwrap();
}
}),
)
"\]:::pushClass -13v1["(13v1) handoff"]:::otherClass -1v1-->|0|2v1 -8v1-->|1|2v1 +6v1[\"(6v1) join_multiset::<'tick, 'static>()"/]:::pullClass +7v1[\"
(7v1)
map(
stageleft::runtime_support::fn1_type_hint::<
(u32, ((), u32)),
u32,
>({
use crate::__staged::local::graph_reachability::*;
|(_from, (_, to))| to
}),
)
"/]:::pullClass +8v1[\"(8v1) chain()"/]:::pullClass +9v1[\"(9v1) defer_tick_lazy()"/]:::pullClass +10v1[/"(10v1) persist::<'static>()"\]:::pushClass +11v1[/"(11v1) unique::<'tick>()"\]:::pushClass +12v1[/"(12v1) multiset_delta()"\]:::pushClass +13v1[/"
(13v1)
for_each(
stageleft::runtime_support::fn1_type_hint::<
u32,
(),
>({
use crate::__staged::local::graph_reachability::*;
let reached_out = reached_out;
|v| {
reached_out.send(v).unwrap();
}
}),
)
"\]:::pushClass +14v1["(14v1) handoff"]:::otherClass +15v1["(15v1) handoff"]:::otherClass +16v1["(16v1) handoff"]:::otherClass +17v1["(17v1) handoff"]:::otherClass +18v1[\"(18v1) identity()"/]:::pullClass +19v1["(19v1) handoff"]:::otherClass +20v1["(20v1) handoff"]:::otherClass +1v1-->14v1 +9v1-->|1|2v1 2v1-->3v1 3v1-->4v1 -4v1-->13v1 -5v1-->|1|6v1 +4v1-->15v1 +5v1-->20v1 6v1-->7v1 -7v1-->8v1 -3v1-->9v1 -9v1-->10v1 +3v1-->16v1 +7v1-->|1|8v1 +8v1-->17v1 +3v1-->10v1 10v1-->11v1 11v1-->12v1 -13v1-->|0|6v1 +12v1-->13v1 +14v1--x|0|2v1; linkStyle 14 stroke:red +15v1-->|0|6v1 +16v1--x|0|8v1; linkStyle 16 stroke:red +17v1-->18v1 +18v1-->19v1 +19v1--o9v1; linkStyle 19 stroke:red +20v1-->|1|6v1 subgraph sg_1v1 ["sg_1v1 stratum 0"] - 13v1 1v1 - 5v1 + subgraph sg_1v1_var_stream_0 ["var stream_0"] + 1v1 + end +end +subgraph sg_2v1 ["sg_2v1 stratum 2"] 6v1 7v1 8v1 + subgraph sg_2v1_var_stream_5 ["var stream_5"] + 6v1 + end + subgraph sg_2v1_var_stream_6 ["var stream_6"] + 7v1 + end + subgraph sg_2v1_var_stream_7 ["var stream_7"] + 8v1 + end +end +subgraph sg_3v1 ["sg_3v1 stratum 1"] + 9v1 2v1 3v1 4v1 - 9v1 10v1 11v1 12v1 - subgraph sg_1v1_var_stream_0 ["var stream_0"] - 1v1 - end - subgraph sg_1v1_var_stream_1 ["var stream_1"] + 13v1 + subgraph sg_3v1_var_stream_1 ["var stream_1"] 2v1 end - subgraph sg_1v1_var_stream_10 ["var stream_10"] + subgraph sg_3v1_var_stream_10 ["var stream_10"] 11v1 end - subgraph sg_1v1_var_stream_2 ["var stream_2"] + subgraph sg_3v1_var_stream_11 ["var stream_11"] + 12v1 + end + subgraph sg_3v1_var_stream_2 ["var stream_2"] 3v1 end - subgraph sg_1v1_var_stream_3 ["var stream_3"] + subgraph sg_3v1_var_stream_3 ["var stream_3"] 4v1 end - subgraph sg_1v1_var_stream_4 ["var stream_4"] - 5v1 - end - subgraph sg_1v1_var_stream_5 ["var stream_5"] - 6v1 - end - subgraph sg_1v1_var_stream_6 ["var stream_6"] - 7v1 - end - subgraph sg_1v1_var_stream_7 ["var stream_7"] - 8v1 - end - subgraph sg_1v1_var_stream_8 ["var stream_8"] + subgraph sg_3v1_var_stream_8 ["var stream_8"] 9v1 end - subgraph sg_1v1_var_stream_9 ["var stream_9"] + subgraph sg_3v1_var_stream_9 ["var stream_9"] 10v1 end end +subgraph sg_4v1 ["sg_4v1 stratum 3"] + 18v1 +end +subgraph sg_5v1 ["sg_5v1 stratum 0"] + 5v1 + subgraph sg_5v1_var_stream_4 ["var stream_4"] + 5v1 + end +end From 22de01f5f566cd8cf7cb3bd31ae8bed99bf1e9ab Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 15 Nov 2024 15:44:12 -0800 Subject: [PATCH 56/74] feat(hydroflow_plus): add `round_robin` helpers for networking (#1566) Also fixes compiler crashes when using `.enumerate()` on an un-batched stream. --- hydroflow_plus/src/ir.rs | 22 ++++-- hydroflow_plus/src/stream.rs | 74 ++++++++++++++++++- hydroflow_plus_test/src/cluster/map_reduce.rs | 14 +--- ...ter__map_reduce__tests__map_reduce_ir.snap | 9 ++- ..._tests__map_reduce_ir@surface_graph_0.snap | 4 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 7 +- 6 files changed, 99 insertions(+), 31 deletions(-) diff --git a/hydroflow_plus/src/ir.rs b/hydroflow_plus/src/ir.rs index 4006bc3b5df9..567bdc34ed57 100644 --- a/hydroflow_plus/src/ir.rs +++ b/hydroflow_plus/src/ir.rs @@ -311,7 +311,10 @@ pub enum HfPlusNode { }, DeferTick(Box), - Enumerate(Box), + Enumerate { + is_static: bool, + input: Box, + }, Inspect { f: DebugExpr, input: Box, @@ -500,7 +503,7 @@ impl<'a> HfPlusNode { HfPlusNode::DeferTick(input) => { transform(input.as_mut(), seen_tees); } - HfPlusNode::Enumerate(input) => { + HfPlusNode::Enumerate { input, .. } => { transform(input.as_mut(), seen_tees); } HfPlusNode::Inspect { input, .. } => { @@ -987,7 +990,7 @@ impl<'a> HfPlusNode { (defer_tick_ident, input_location_id) } - HfPlusNode::Enumerate(input) => { + HfPlusNode::Enumerate { is_static, input } => { let (input_ident, input_location_id) = input.emit(graph_builders, built_tees, next_stmt_id); @@ -998,9 +1001,16 @@ impl<'a> HfPlusNode { syn::Ident::new(&format!("stream_{}", enumerate_id), Span::call_site()); let builder = graph_builders.entry(input_location_id).or_default(); - builder.add_statement(parse_quote! { - #enumerate_ident = #input_ident -> enumerate(); - }); + + if *is_static { + builder.add_statement(parse_quote! { + #enumerate_ident = #input_ident -> enumerate::<'static>(); + }); + } else { + builder.add_statement(parse_quote! { + #enumerate_ident = #input_ident -> enumerate::<'tick>(); + }); + } (enumerate_ident, input_location_id) } diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 6dcaf3eed927..550afa52a37b 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -261,10 +261,23 @@ impl<'a, T, L: Location<'a>, B> Stream { } pub fn enumerate(self) -> Stream<(usize, T), L, B> { - Stream::new( - self.location, - HfPlusNode::Enumerate(Box::new(self.ir_node.into_inner())), - ) + if L::is_top_level() { + Stream::new( + self.location, + HfPlusNode::Persist(Box::new(HfPlusNode::Enumerate { + is_static: true, + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + })), + ) + } else { + Stream::new( + self.location, + HfPlusNode::Enumerate { + is_static: false, + input: Box::new(self.ir_node.into_inner()), + }, + ) + } } pub fn unique(self) -> Stream @@ -789,6 +802,21 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { .send_bincode(other) } + pub fn round_robin_bincode( + self, + other: &Cluster<'a, C2>, + ) -> Stream, Cluster<'a, C2>, Unbounded> + where + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, + T: Clone + Serialize + DeserializeOwned, + { + let ids = other.members(); + + self.enumerate() + .map(q!(|(i, w)| (ids[i % ids.len()], w))) + .send_bincode(other) + } + pub fn broadcast_bincode_interleaved( self, other: &Cluster<'a, C2>, @@ -800,6 +828,17 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { self.broadcast_bincode(other).map(q!(|(_, b)| b)) } + pub fn round_robin_bincode_interleaved( + self, + other: &Cluster<'a, C2>, + ) -> Stream, Unbounded> + where + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, + T: Clone + Serialize + DeserializeOwned, + { + self.round_robin_bincode(other).map(q!(|(_, b)| b)) + } + pub fn broadcast_bytes( self, other: &Cluster<'a, C2>, @@ -817,6 +856,21 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { .send_bytes(other) } + pub fn round_robin_bytes( + self, + other: &Cluster<'a, C2>, + ) -> Stream, Cluster<'a, C2>, Unbounded> + where + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, + T: Clone, + { + let ids = other.members(); + + self.enumerate() + .map(q!(|(i, w)| (ids[i % ids.len()], w))) + .send_bytes(other) + } + pub fn broadcast_bytes_interleaved( self, other: &Cluster<'a, C2>, @@ -828,6 +882,18 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { { self.broadcast_bytes(other).map(q!(|(_, b)| b)) } + + pub fn round_robin_bytes_interleaved( + self, + other: &Cluster<'a, C2>, + ) -> Stream, Unbounded> + where + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + + 'a, + T: Clone, + { + self.round_robin_bytes(other).map(q!(|(_, b)| b)) + } } #[cfg(test)] diff --git a/hydroflow_plus_test/src/cluster/map_reduce.rs b/hydroflow_plus_test/src/cluster/map_reduce.rs index 27ebdc7eb598..97173bf45578 100644 --- a/hydroflow_plus_test/src/cluster/map_reduce.rs +++ b/hydroflow_plus_test/src/cluster/map_reduce.rs @@ -11,18 +11,8 @@ pub fn map_reduce<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, Leader>, Cluster<' .source_iter(q!(vec!["abc", "abc", "xyz", "abc"])) .map(q!(|s| s.to_string())); - let all_ids_vec = cluster.members(); - let words_partitioned = words - .tick_batch(&process.tick()) - .enumerate() - .map(q!(|(i, w)| ( - ClusterId::from_raw((i % all_ids_vec.len()) as u32), - w - ))) - .all_ticks(); - - words_partitioned - .send_bincode(&cluster) + words + .round_robin_bincode(&cluster) .map(q!(|string| (string, ()))) .tick_batch(&cluster.tick()) .fold_keyed(q!(|| 0), q!(|count, _| *count += 1)) diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap index 5daa117e821c..86e08bc03193 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap @@ -78,9 +78,10 @@ expression: built.ir() ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use crate :: __staged :: cluster :: map_reduce :: * ; let all_ids_vec = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ClusterId :: from_raw ((i % all_ids_vec . len ()) as u32) , w) }), - input: Enumerate( - Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ids [i % ids . len ()] , w) }), + input: Enumerate { + is_static: true, + input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < & str , std :: string :: String > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | s | s . to_string () }), input: Source { source: Iter( @@ -91,7 +92,7 @@ expression: built.ir() ), }, }, - ), + }, }, }, }, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap index a45a39badd06..329c9c167fe9 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap @@ -4,8 +4,8 @@ expression: ir.surface_syntax_string() --- 1v1 = source_iter ({ use crate :: __staged :: cluster :: map_reduce :: * ; vec ! ["abc" , "abc" , "xyz" , "abc"] }); 2v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < & str , std :: string :: String > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | s | s . to_string () })); -3v1 = enumerate (); -4v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use crate :: __staged :: cluster :: map_reduce :: * ; let all_ids_vec = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ClusterId :: from_raw ((i % all_ids_vec . len ()) as u32) , w) })); +3v1 = enumerate :: < 'static > (); +4v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ids [i % ids . len ()] , w) })); 5v1 = map (| (id , data) : (hydroflow_plus :: ClusterId < _ > , std :: string :: String) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < std :: string :: String > (& data) . unwrap () . into ()) }); 6v1 = dest_sink ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env = FAKE ; let p1_port = "port_0" ; { env . port (p1_port) . connect_local_blocking :: < ConnectedDemux < ConnectedDirect > > () . into_sink () } }); 7v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env = FAKE ; let p2_port = "port_1" ; { env . port (p2_port) . connect_local_blocking :: < ConnectedTagged < ConnectedDirect > > () . into_source () } }); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 4bd55606df93..5a9d311e2063 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -558,8 +558,9 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index , value : Some (payload) } }), input: CrossSingleton( CrossSingleton( - Enumerate( - Map { + Enumerate { + is_static: false, + input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -601,7 +602,7 @@ expression: built.ir() }, }, }, - ), + }, Tee { inner: , }, From 971c5f1b6c68f131662495791cb063541f672c74 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 15 Nov 2024 17:25:00 -0800 Subject: [PATCH 57/74] docs(hydroflow_plus): rewrite adding distribution page (#1564) --- .../hydroflow_plus/quickstart/distributed.mdx | 67 ++++++++++++++----- .../quickstart/first-dataflow.mdx | 2 +- .../src/first_ten_distributed.rs | 5 +- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/docs/docs/hydroflow_plus/quickstart/distributed.mdx b/docs/docs/hydroflow_plus/quickstart/distributed.mdx index a8ae364060bd..b09f06ac10c6 100644 --- a/docs/docs/hydroflow_plus/quickstart/distributed.mdx +++ b/docs/docs/hydroflow_plus/quickstart/distributed.mdx @@ -7,29 +7,66 @@ import firstTenDistExample from '!!raw-loader!../../../../template/hydroflow_plu import { getLines, extractOutput } from '../../../src/util'; # Adding Distribution -Continuing from our previous example, we will now look at how to deploy our program to run on multiple processes. +Continuing from our previous example, we will now look at how to deploy our program to run on multiple processes. First, we need to extend our dataflow program to use multiple processes with a network between them. -We achieve this by using [Hydro Deploy](../../deploy/index.md). Hydroflow+ integrates with Hydro Deploy to automatically construct the topology based on the flow graph. We can create a new file `examples/first_ten_distributed.rs` with the following contents: +We'll start by updating our function signature to take two processes. At this point, we'll need to add a lifetime parameter `'a` which represents the lifetime of data referenced by our dataflow logic. This lifetime needs to be the same across all the processes, so it can't be elided. + +```rust title="src/first_ten_distributed.rs" +use hydroflow_plus::*; + +pub fn first_ten_distributed<'a>(p1: &Process<'a>, p2: &Process<'a>) +``` + +Now, we'll use a new API, `send_bincode` to establish a network between our processes. Given a stream on process `p1`, we can send the data to `p2` by calling `.send_bincode(p2)`, which returns a stream on `p2`. So to make our program distributed, it only takes a single line change. + +```rust title="src/first_ten_distributed.rs" +pub fn first_ten_distributed<'a>(p1: &Process<'a>, p2: &Process<'a>) { + p1.source_iter(q!(0..10)) + // highlight-next-line + .send_bincode(p2) + .for_each(q!(|n| println!("{}", n))); +} +``` + +Then, we can update our deployment script to launch both processes on localhost. Hydro Deploy will automatically handle service discovery and networking, since it knows the full network topology (on UNIX systems, this will use a UNIX socket for networking). {firstTenDistExample} -Most importantly, we specify a `DeployProcessSpec`, which constructs a Hydro Deploy service for each process in the flow graph. In our case, we use the `TrybuildHost` service type, which compiles and deploys a Hydroflow+ graph. +We can then launch the program: +```bash +#shell-command-next-line +cargo run --example first_ten_distributed +[() (process 1)] 0 +[() (process 1)] 1 +[() (process 1)] 2 +[() (process 1)] 3 +[() (process 1)] 4 +[() (process 1)] 5 +[() (process 1)] 6 +[() (process 1)] 7 +[() (process 1)] 8 +[() (process 1)] 9 +``` + +You'll notice that our logs are not particularly descriptive, just showing `()` as an identifier. Furthermore, our processes have the same Rust type, which could lead to accidentally mixing up streams across the machines (this will throw an exception, but it would be nice to have a compile error). + +To fix this, we can use the optional type parameter on `Process`, which lets us add a "type tag" that acts as an identifier. We'll define two structs to act as these tags and use them in the function signature: -We can then run our distributed dataflow with: +{getLines(firstTenDistSrc, 3, 10)} -<>{/* TODO(mingwei): grab this output from a tested snapshot file */} +If you are using an IDE extension like [Rust Analyzer](https://rust-analyzer.github.io/), you'll see these types attached to each stream. And if we launch the program again, we'll see much better logs: ```bash #shell-command-next-line cargo run --example first_ten_distributed -[service/1] 0 -[service/1] 1 -[service/1] 2 -[service/1] 3 -[service/1] 4 -[service/1] 5 -[service/1] 6 -[service/1] 7 -[service/1] 8 -[service/1] 9 +[first_ten_distributed::P2 (process 1)] 0 +[first_ten_distributed::P2 (process 1)] 1 +[first_ten_distributed::P2 (process 1)] 2 +[first_ten_distributed::P2 (process 1)] 3 +[first_ten_distributed::P2 (process 1)] 4 +[first_ten_distributed::P2 (process 1)] 5 +[first_ten_distributed::P2 (process 1)] 6 +[first_ten_distributed::P2 (process 1)] 7 +[first_ten_distributed::P2 (process 1)] 8 +[first_ten_distributed::P2 (process 1)] 9 ``` diff --git a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx index 8a3c3ef6eff7..f9c54d8985c5 100644 --- a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx +++ b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx @@ -58,7 +58,7 @@ cargo run --example first_ten [() (process 0)] 6 [() (process 0)] 7 [() (process 0)] 8 -[() (process 0)] 9 +[() (process 0)] 9 ``` In the next section, we will look at how to distribute this program across multiple processes. diff --git a/template/hydroflow_plus/src/first_ten_distributed.rs b/template/hydroflow_plus/src/first_ten_distributed.rs index c325b6973191..6445b392b769 100644 --- a/template/hydroflow_plus/src/first_ten_distributed.rs +++ b/template/hydroflow_plus/src/first_ten_distributed.rs @@ -4,8 +4,9 @@ pub struct P1 {} pub struct P2 {} pub fn first_ten_distributed<'a>(p1: &Process<'a, P1>, p2: &Process<'a, P2>) { - let numbers = p1.source_iter(q!(0..10)); - numbers.send_bincode(p2).for_each(q!(|n| println!("{}", n))); + p1.source_iter(q!(0..10)) // : Stream, ...> + .send_bincode(p2) // : Stream, ...> + .for_each(q!(|n| println!("{}", n))); } #[cfg(test)] From 531bf81f8be531d2f3c3312cac4b037869c7cd3d Mon Sep 17 00:00:00 2001 From: Joe Hellerstein Date: Sun, 17 Nov 2024 12:09:45 -0800 Subject: [PATCH 58/74] fix(docs): update hydro stack diagram and discussion (#1539) - Remove TODO.md (contents filed as an issue, #1538) - Update stack diagram - Update discussion of stack diagram --- docs/docs/hydroflow/ecosystem.md | 20 +++-- docs/docs/hydroflow/img/hydro_stack.png | Bin 213274 -> 199804 bytes docs/docs/hydroflow/todo.md | 95 ------------------------ 3 files changed, 9 insertions(+), 106 deletions(-) delete mode 100644 docs/docs/hydroflow/todo.md diff --git a/docs/docs/hydroflow/ecosystem.md b/docs/docs/hydroflow/ecosystem.md index 62e10157ec9d..44199e74523b 100644 --- a/docs/docs/hydroflow/ecosystem.md +++ b/docs/docs/hydroflow/ecosystem.md @@ -4,24 +4,22 @@ sidebar_position: 4 # The Hydro Ecosystem The Hydro Project is an evolving stack of libraries and languages for distributed programming. -A rough picture of the envisioned Hydro stack is below: +A rough picture of the Hydro stack is below: ![Hydro Stack](./img/hydro_stack.png) -The core of the Hydro stack is shown in in the grey box; components that have not been implemented are in orange. +Working down from the top: -Working up from the bottom: +- [*Hydroflow+*](../hydroflow_plus) is an end-user-facing high-level [choreographic](https://en.wikipedia.org/wiki/Choreographic_programming) [dataflow](https://en.wikipedia.org/wiki/Dataflow_programming) language. Hydroflow+ is a *global* language for programming a fleet of transducers. Programmers author dataflow pipelines that start with streams of events and data, and span boundaries across multiple `process` and (scalable) `cluster` specifications. -- [Hydroplane](https://github.com/hydro-project/hydroplane) is a service for launching and monitoring Hydroflow transducers. It works both on local developer machines with Docker, and in cloud services like AWS EKS. Over time we expect to add autoscaling features to Hydroplane, to allow users to configure the cluster to grow and shrink based on the monitored behavior and a pluggable policy. +- *Hydrolysis* is a compiler that translates a global Hydroflow+ spec to multiple single-threaded Hydroflow IR programs, which collectively implement the global spec. +This compilation phase is currently a part of the Hydroflow+ codebase, but will evolve into a standalone optimizing compiler inspired by database query optimizers and [e-graphs](https://en.wikipedia.org/wiki/E-graph). -- [Hydroflow](https://github.com/hydro-project/hydroplane) is the subject of this book; a library for defining individual transducers in a distributed system. It uses the Rust compiler to generate binaries for deployment. +- [Hydroflow IR and its compiler/runtime](https://github.com/hydro-project/hydroflow/tree/main/hydroflow) are the subject of this book. +Where Hydroflow+ is a *global* language for programming a fleet of processes, Hydroflow is a *local* language for programming a single process that participates in a distributed system. More specifically, Hydroflow is an internal representation (IR) language and runtime library that generates the low-level Rust code for an individual transducer. As a low-level IR, Hydroflow is not intended for the general-purpose programmer. For most users it is intended as a readable compiler target from Hydroflow+; advanced developers can also use it to manually program individual transducers. -- *Hydrolysis* is a compiler we envision translating from Hydrologic to Hydroflow. +- [HydroDeploy](../deploy) is a service for launching Hydroflow transducers on a variety of platforms. -- *Hydrologic* is a high-level domain-specific language that we envision for distributed programming. Unlike Hydroflow, we expect Hydrologic to abstract away many notions of distributed computing. In particular, Hydrologic will be insensitive to the specific deployment of the code—the partitioning of functionality and data across transducers, the number of replicas of each transducer, etc. Instead, programmers will provide specifications for desired properties like the number of failures to tolerate, the consistency desired at a given endpoint, the latency of a given endpoint, etc. The Hydrolysis compiler will then generate Hydroflow transducers that can be deployed by Hydroplane to meet those specifications. - -- [Metalift](https://github.com/metalift/metalift) is a framework for "lifting" code from one language to a (typically higher-level) language. We envision that Metalift will be used to translate from one of many distributed programming models/languages into our common Internal Representation, Hydrologic. +- Hydro also supports *Deterministic Simulation Testing* to aid in debugging distributed programs. Documentation on this feature is forthcoming. The Hydro stack is inspired by previous language stacks including [LLVM](https://llvm.org) and [Halide](https://halide-lang.org), which similarly expose multiple human-programmable Internal Representation langauges. - -An early paper on the Hydro vision appeared in CIDR 2021, under the title [New Directions in Cloud Programming](https://www.cidrdb.org/cidr2021/papers/cidr2021_paper16.pdf). \ No newline at end of file diff --git a/docs/docs/hydroflow/img/hydro_stack.png b/docs/docs/hydroflow/img/hydro_stack.png index d6fa04edcb0ed6977faf2c863cfef9a58db0f8e8..a5d87b7b1ad310d9fe93dcf603db9a4439a79a27 100644 GIT binary patch literal 199804 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%0D|<$ZbX-a-OaHa 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
J}J)q8DN2-|-Hzwz;-f@XZ_ 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�wj_4_G|9A@7+HHKRE^EzL> z5LfLN!NaGZID+I@J&2hl^u^vvd*yXI-z)^2xdD&NRKRoLoVExUXHsEoli5D_IA#0_ zXIqJtSvUz}zt6xVj~^q{7EV=AwACPtTpLcb6}}!x zHI=5Ll-jW!a`M|dP+Vv{HSXyUI(gktnEOrsgaoI${&*PWYT!NB$Yn;p3H3oxZq!U{ zS>^mE2`ZPuwHOhH;r?+kXXoWr_Pw`8UK+k+rftwfqAcavzF%P@;0&abc%7+Kwj0~h z=$L$A{317nqA4bedh=ZDUY*YQYgj81kR^9H*=6!Dtg{9fFj}L%m?igbqE}d`4)buq zmA{53&&;?suT@0aaTk7#K%+5Gt<>1Zu4hzMW4r%t9Mz9)C6B=dQ`bLD&~stdr|xE{ z`C3GK!|iOpY$#4%KG4eP{dMVB^E9Pet6#>R`v?&uxr+&=4bcZ�+3yAox)a^@Bl) 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%0D7{hJcD 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 W;bHGT9z6tppbF}@if)?z{(k`fu8bT2 literal 213274 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�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%0DMt1Lh!gIQ%Zu zEDqTJxsasuP$;ru?=)dFKrD=n}cvb^*p~1aL zj%jv=pP*JNfza_LY&Td`Iv|=i&G@#HDIiThV=+Kaob8Vb{j1Yb+<-B>9{mW)WJQx) zuQMmYpiyQh@+ZQ_kA_7zG&H<*>()KCH)6G!a$kORrbsr#-I)+K@HG?;W$+=;^_YZr zUJQBsZZfwG$~GL&<0=bCg&@147L;}w--ki83uZFt_q@mct-8AO&1`$A<#K_+fMDe3 z_359A_Q25hfS+&Ne_W(kXqd0pv$eAFb~?f`kVb`?9+%7BcCg5#aM(J{AMBuEv9%zt z*?)_W_eCf}N^~^$D?STgRV^0#tKCp>;CyEExeJ2){qJC6zMW}}p;9bZ=*#814_

    !%l!oI+n?5*YU=Oh#RQ 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ŽY8GOth)0Ubp} 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 sZDFaO5#-l1TTpTfO8zgQ, variables, indexing multi-input/output operators - - running Hydroflow via `run_available` and `run_async` - - Recursion via cyclic dataflow - - Fixpoints and Strata - - Template structure: `clap`, message types - - `source_stdin` - - Messages and `demux` - - broadcast pattern - - the `persist` operator to store and replay dataflow - - the `defer_signal` operator to gate a dataflow - - bootstrapping pipelines: `initialize` - -- Operators covered - - cross_join - - defer_signal - - demux - - dest_sink_serde - - difference - - filter - - filter_map - - flatten - - flat_map - - for_each - - initialize - - join - - map - - persist - - union - - source_iter - - source_stdin - - source_stream - - source_stream_serde - - tee - - union - - unique From f8f970e430bd481aa21bde1443cdce7bc37997a0 Mon Sep 17 00:00:00 2001 From: Joe Hellerstein Date: Sun, 17 Nov 2024 22:27:23 -0800 Subject: [PATCH 59/74] fix(docs): improve initial doc reading (#1570) - Change `README.md` to reflect the project as a whole, not just hydroflow. - Copy Rust getting-started info from Hydroflow docs to HF+ docs --- README.md | 105 ++++++------------ docs/docs/hydroflow_plus/quickstart/index.mdx | 29 +++++ docs/static/img/hydro-stack.png | Bin 0 -> 461873 bytes 3 files changed, 64 insertions(+), 70 deletions(-) create mode 100644 docs/static/img/hydro-stack.png diff --git a/README.md b/README.md index da04f8a3587f..34d0e833e1c2 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,58 @@

    - "hf" - Hydroflow
    + "hf"

    Crates.io Docs.rs

    -Hydroflow is a low-latency dataflow runtime written in Rust. The goal of the [Hydro Project](https://hydro.run) -is to empower developers to harness the full potential of the cloud by making distributed programs easy to specify and automatic to scale. Hydroflow is the lowest level in the [Hydro stack](https://hydro.run/docs/hydroflow/ecosystem/), -serving as a single-node low-latency runtime with explicit networking. This allows us to support -not just data processing pipelines, but distributed protocols (e.g. Paxos) and real-world -long-running applications as well. +Hydro is a novel distributed programming library for standard Rust. Hydro allows developers to build distributed systems that are efficient, scalable, and correct. -Take a look at the [Hydroflow Book](https://hydro.run/docs/hydroflow/). +Hydro integrates naturally into standard Rust constructs and IDEs, providing types and programming constructs for ensuring distributed correctness. Under the covers it provides a metaprogrammed compiler that optimizes for cross-node issues of scaling and data movement while leveraging Rust and LLVM for per-node performance. -## The Hydroflow Surface Syntax +We often describe Hydro via a metaphor: *LLVM for the cloud*. Like LLVM, Hydro is a layered compilation framework with a low-level Internal Representation language. In contrast to LLVM, Hydro focuses on distributed aspects of modern software. + +
    + Image description +
    -Hydroflow comes with a custom "surface syntax"—a domain-specific language which serves as a very -simple, readable IR for specifying single-node Hydroflow programs. These programs are intended to be stitched together -by the Hydro stack to create larger autoscaling distributed systems. -Here's a simple example of the surface syntax. Check out the [Hydroflow Playground](https://hydro.run/playground) -for an interactive demo. -```rust -source_iter(0..10) - -> map(|n| n * n) - -> filter(|n| *n > 10) - -> foo; +## The Language (and the Low-Level IR) +Hydro provides a high-level language for the majority of developers called [Hydroflow+](https://hydro.run/docs/hydroflow_plus). Hydroflow+ allows you to program an entire fleet of processes from a single program, and then launch your fleet locally or in the cloud via [Hydro Deploy](https://hydro.run/docs/deploy). Get started with Hydroflow+ via the Hydroflow+ [documentation](https://hydro.run/docs/hydroflow_plus) and [examples](https://github.com/hydro-project/hydroflow/tree/main/hydroflow_plus_test/examples). -foo = map(|n| (n..=n+1)) - -> flatten() - -> for_each(|n| println!("Howdy {}", n)); -``` +> Internally, the Hydro stack compiles Hydroflow+ programs into a low-level Internal Representation (IR) language called [Hydroflow](https://hydro.run/docs/hydroflow); each process corresponds to a separate Hydroflow program. In rare cases you may want to compose one or more processes in Hydroflow by hand; see the Hydroflow [documentation](https://hydro.run/docs/hydroflow) or [examples](https://github.com/hydro-project/hydroflow/tree/main/hydroflow/examples) for details. -For more, check out the [surface syntax section of the Hydroflow book](https://hydro.run/docs/hydroflow/syntax/). +## Development Setup -## Start with a Template Program -We provide a `cargo-generate` template for you to get started from a simple working example. +See the [quickstart section of the Hydroflow+ book](https://hydro.run/docs/hydroflow_plus/quickstart/) for instructions on installing Rust and getting started with the Hydroflow+ template. -To install `cargo-generate`, run the following: -```bash,ignore -cargo install cargo-generate -``` +# A New Approach to Distributed Programming +There have been many frameworks and platforms for distributed programming over the years, with significant tradeoffs. These include: -Then run: -```bash,ignore -cargo generate gh:hydro-project/hydroflow template/hydroflow -``` +**Higher level frameworks** have been designed to serve specialized distributed use cases. These including *Client-Server (Monolith)* frameworks (e.g. Ruby on Rails + DBMS), parallel *Bulk Dataflow* frameworks (e.g. Spark, Flink, etc.), and step-wise *Workflows / Pipelines / Serverless / μservice Orchestration* frameworks (e.g. Kafka, Airflow). All of these frameworks offer limited expressibility and are inefficient outside their sweet spot. Each one ties developers' hands in different ways. -`cd` into the generated folder, ensure the correct nightly version of rust is installed, and test the generated project: -```bash -#shell-command-next-line -cd -#shell-command-next-line -rustup update -#shell-command-next-line -cargo test -``` +**Lower level asynchronous APIs** provide general-purpose distributed interfaces for sequential programming, including + *RPCs*, *Async/Await* frameworks and *Actor* frameworks (e.g. Akka, Ray, Unison, Orleans, gRPC). These interfaces allow developers to build distributed systems *one async sequential process* at a time. While they offer low-level control of individual processes, they provide minimal help for global correctness of the fleet. -And you will get a well-formed Hydroflow/Rust project to use as a starting point. It provides a simple Echo Server and Client, and advice -for adapting it to other uses. +## Towards a more comprehensive approach +What's wanted, we believe, is a proper language stack addressing distributed concerns: -### Enable IDE Support for Ligatures -Since flow edges `->` appear frequently in flows described using the Hydroflow surface syntax, enabling ligature support -in your IDE may improve your code reading experience. This has no impact on code functionality or performance. +- **Broad Expressivity**: The stack should support a spectrum of performance regimes from lightweight, low-latency async event handling to high-throughput dataflow. It should also support a full range of architectural configurations, from SIMD to more heterogeneous architectures. +- **Familiarity**: The distributed aspects of the language should be integrated into a familiar mature programming language and environment, including libraries, IDEs and other tooling. A mature compiler should optimize local code to be fast and lean. (Hydro embraces Rust and LLVM for these attributes.) +- **Performance control**: The ability to program a fleet of machines "globally" should not prevent software engineers from optimizing the code that executes locally at each node. +- **Distributed Typechecking**: The type system of the language should enforce distributed correctness in the compiler, in ways that are visible in an IDE at time of authoring. For example, the types of data items should include their abstract locations, so that two items materialized at different nodes cannot be referenced together without an intervening construct for (async) communication. +- **Distributed Optimizations**: The compiler should be able to correctly optimize (transform) programs for distributed concerns: removing bottlenecks by flexibly assigning compute and data to different processes or clusters, while preserving program semantics. +- **Modularity**: The standard modularity of traditional programming—e.g. function calling abstractions—should work for distriuted logic. For example, it should be possible to wrap a common cross-node construct like "heartbeats and timeouts", and invoke it as simply as one invokes a sequential function. +- **Native Testing Tools** for correctness. In today's standard practice, formal specification languages for testing (e.g. TLA+) are separate from languages of implementation. We believe it should be possible to perform many kinds of formal testing (e.g. model checking) on the same code that is used in deployment. -Instructions to enable this for the `Fira Code` font: -- [VSCode](https://github.com/tonsky/FiraCode/wiki/VS-Code-Instructions) -- [IntelliJ](https://github.com/tonsky/FiraCode/wiki/IntelliJ-products-instructions) +[Hydro](https://hydro.run) is a Rust library for distributed programming that is designed to address these goals. -More font options are available [here](https://github.com/tonsky/FiraCode?tab=readme-ov-file#alternatives). +# Learning More +The Hydro project's main website is at [https://hydro.run](https://hydro.run). -## Dev Setup +- **Docs**: There are docs for the [high-level Hydroflow+ language](https://hydro.run/docs/hydroflow_plus/) and the [low-level Hydroflow IR](https://hydro.run/docs/hydroflow), as well as the [HydroDeploy](https://hydro.run/docs/deploy) framework for launching Hydroflow+ programs. -See the [setup section of the book](https://hydro.run/docs/hydroflow/quickstart/setup). - -### The Examples Container - -The `hydroflow/examples` subdirectory of this repository includes a number of examples. -To make running these examples in the cloud easier, we've created a Docker image that contains compiled versions of those examples. The image is defined in the `Dockerfile` in the same directory as this README. - -If you want to build the examples container locally, you can run -``` -docker build -t hydroflow-examples . -``` - -This will build an image suitable for your architecture. - -The `scripts/multiplatform-docker-build.sh ` script will build both `arm64` and `amd64` versions of the image and push them to the image name specified. By default, this will push the image to DockerHub; if you want to push the image to another repository, you can pass an image URL as the argument to `multiplatform-docker-build.sh` instead. - -Example binaries are located in `/usr/src/myapp`. +- **Research Papers**: Our [research publications](https://hydro.run/research) are available on the project website. Some notable selections: + - The original Hydro vision paper from CIDR 2021: [New Directions in Cloud Programming](https://hydro.run/papers/new-directions.pdf) + - The first paper on optimizations from SIGMOD 2024: [Optimizing Distributed Protocols with Query Rewrites](https://hydro.run/papers/david-sigmod-2024.pdf) + - The first paper on Hydro's formal semantics to appear in POPL 2025: [Flo: a Semantic Foundation for Progressive Stream Processing](https://arxiv.org/abs/2411.08274) diff --git a/docs/docs/hydroflow_plus/quickstart/index.mdx b/docs/docs/hydroflow_plus/quickstart/index.mdx index 29868a1c7503..d5e8a071ffaa 100644 --- a/docs/docs/hydroflow_plus/quickstart/index.mdx +++ b/docs/docs/hydroflow_plus/quickstart/index.mdx @@ -1,6 +1,35 @@ # Quickstart In this tutorial, we'll walk through the basics of Hydroflow+ by building a simple dataflow that prints out the first 10 natural numbers. We'll start with a single process, then pipeline the computation, and finally distribute it across a cluster. +## Installing Rust + +First you will need to install Rust. We recommend the conventional installation +method, `rustup`, which allows you to easily manage and update Rust versions. + +[**Install Rust**](https://www.rust-lang.org/tools/install) + +The link in the previous line will take you to the Rust website that shows you how to +install `rustup` and the Rust package manager `cargo` (and the +internally-used `rustc` compiler). `cargo` is Rust's main development tool, +used for building, running, and testing Rust code. + +The following `cargo` commands will come in handy: +* `cargo check --all-targets` - Checks the workspace for any compile-time + errors. +* `cargo build --all-targets` - Builds all projects/tests/benchmarks/examples + in the workspace. +* `cargo clean` - Cleans the build cache, sometimes needed if the build is + acting up. +* `cargo test` - Runs tests in the workspace. +* `cargo run -p hydroflow --example ` - Run an example program in + `hydroflow/examples`. + +## VS Code Setup + +We recommend using VS Code with the `rust-analyzer` extension (and NOT the +`Rust` extension). + +## Getting Started with Hydroflow+ To get started with a new project, we'll use the Hydroflow+ template. The template comes with a simple distributed program. ```bash diff --git a/docs/static/img/hydro-stack.png b/docs/static/img/hydro-stack.png new file mode 100644 index 0000000000000000000000000000000000000000..5b7cf22da9a37036898b9ec9890fe72b0499dff8 GIT binary patch literal 461873 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�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

    mxSWGVIH<{r_RqE=j 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 z zN(cM)9Gy6tg@iw(RfVYfDW@gnM3XktKee77FQK~$u;G8vxDPsV51qV$IJs?T-X$SyLkbT-?vQLr&d|15&xC)4v{`+ z*L%&d3|pLMhWXXdmjzDm)6Z3kDV+;;;*Jw4cVS#GIQUP?Vn$5RoOs z*vL>tTIE90iNUEBD+BI=-Q|`Xf0(kV&@iq4dRG;kW1-ZANZ%55(i%XpWIebCvu0iR zpY5|r_=Hz2Yt4noDLT1491Js?>%u@N7dBInJXeE+%Py*E5Sm+LTV_^v{G5PoG|di~ zMNlDQ{!smLNy{xniVn)n^5jE=Ope&mUbyeBB=PP_5-U?|@4l!+NpLU{zix zih#qV)9;qOA%3G1nrO{S6T=Z*($q^0G)j1}mQ9n^LLR?H`saiNf&{3)`XDrT;GWuY z2xmEIV7IEK79)HrZ#=O)Y#%uhsKaBGkv_%JW^I5y)=QbAP|p>(MNaIxI>g2 zTtoDdG;y6S)MoGjyvN@{#RxWi$`b(365jW0lSt41x9LC!+;=}_*74v~LltdqD!NJk zzu0@{=g6YAZFGVOCdtH_SQFc}CYg9*+qP}nwlR|qI<{@ww!hBve5c;?{tf5+)K$By zR_(pkjcZ-E(7Wxi{Wz;NQ3@&7ER->wy~A8&$B}CFL=M6iiez+)d33W$GD}f&We3*Y zaw~pal$cY0st_&+3A}MaZp5=eI^%%b+O0C{@}+#$O62yjY*3!{usY<})f{kjWnRdg zZ^_i;x8%9X|H2J9OmN@q<~=au0@H4QwDKRfBSP5We>on=_O;-%>5nS+O+r}NY?3gE6 z3>?Soic`s}xw~hLN>rX_2^)<*ea!byAM@QpY6xjD0Os=cjNg~M#-*VXqRoe6=8^?AuaoIl;u#7DM0O?(>B=0EDR zN(fS?=B_mbR>Mc0udDSKKAJ2|CE0;Ey0y=Fb4azyaeC=(=L5h~23b7XB+iENMv!fbN#D2uJ z5Gg8(>6=x}zmC{=Rw=3C@lmRrI%qk3U(KFv-lR34EB^WQDFU4=haHfn&f!FtKmlLq zgYoRF<*XOsY)8ixh!rER>9PcHUix1~X`ePSi+HkC$?1+O!!0M-#>MU8)^f${Q;je` z{b%)a{VE9Ad=efYk^20Rb^y5v*_&^T+aNRj|9XC^^$8GKk4Nl3f!_c8!!U--;LkTk zy%=utzn<5+fVRWyqMUxD>VFph6kC>0=ANZSmpc1@KVJ_5iE~>;I}PA}7XN&!na^A8 z4A8&I^?yJA|3BdWp4tBwPk=CWf3M+FLOT_O2{X_t8O+v{5&YVa{YqA{VJAv9u3xA< z43=moKqA9{0J6h;2vk$yAPl4kY40pV@>_6~DG{aM);Q zYOTjNICNWccR%k&QC=+u6J4Sq=OWzkuPK72Gd&aCB$5BtA4v5Mi`bS_to!o0@WdxE zkW=MO-9%K2;HMQIFUhi-c8MrDV~1>7g>z(xvK|BM5X!Y~vReAfD!fTaBrtO2f>*oI ze@*Bnb;G={`jcQ}MMtpp=h?jWo9yyj6ggr}hh6$?6EmhG`P%9$%L^OirpWAuy4L-g>@j(sp+IW`U zdp&#Yr}*Nq8#BLxYMq72mWNmGd7b3%B*jMVJWS#2$!PHx&N_6^jRO?z0tf9=4!8tH zFVdnoQs*G;{`8zr#NW^sX=37Y*Y9E~9PencN7Lz&Y<+aiN1$}?DQh6F!t96FH%V0_ zVO?^VTea)L#uUJQbb2GjzZd5h)swFov)iq8T;DFLp|y}ydybl0DBM*7({0%;F90b- zDe+Pjm_8SwpY#K0H>E(9?gHfpH;B=6fC;T~OgU47YQ@8}3+xX{Lu+!GMtx&4rPp!P zesAxX$O&CyB*bivCvr-Fy#3O!VG`+UVFoXDF21XtzUbE8?Tvh%>HYxWq|yTE@lwUZ zhFSDtFOMwS@#h5c$`#P$_`H*M<8^QKX(BtTK^C*!!txY^5he%;g-%Tw$((mp$u^3? z4!1T{#cXztUQtR&1oGFyb9|CLg<+`}#a*wRx_|IvQZ>F_!`v?4G-BnBw>|KwXZ4P^ z>h(^}-Cyqy0W2;(ois`;>X#n^rTBdxo5uh3fi@Wgxne+uwIn7jOAkg`9C9~V#xeik zHkOGCrNK+><<{T@Le_@cgg-jW#w8y){p?HKSV`0uD~TLnmGf-|^NpwY`h81W_s-ho z9-WiR01Q;X#qXU1RcCLlCK*G1b~gshdHJVzFn|IDQWu)T^X$%lKw5AoD$!)?S6+E6 zZsY&jaBPi>S*GjVyhbU<)-iJa4Iv(WBmtvrcDaRcGuLs3Y$LOH4*jZV z)Ajg0yAD-}nLy*RPEAO>n$(O%*tqM7asBZJqMK#$*=M`1P{|D3NpV9l%-@7hcvAXj1)u@WK3iG(s&zFl2P=D$; zOojKjVAADT;Vh>>pAcLGHR$|TalU|DqU@JwJSNC{B1i`8`cP5J0z7Mk`_si{)4z^w zi%mp?^|=R@Z)MA=tx&(0_7g}EdKhF-RFOp}#Fy$y=_0mFH0Z8-JdQoM?f;d;|8oSP zAyfWlGH#tCu`^SLk^~G*54z{R?<|?Yr%v0X!G(%<-i)>gvd?J$ru}OOz2oE{jp>n# zIn?M)_vNih^X!A?-BSYCR^!g`T_lJ;fj$VOl#zh&P?>=d5hx)f(wCx7MzYQ{DiAEz zO!Zp}CX6%kHgTAh%Ta=Q-F?}7k2OpkAtacn<%Ov+c$0id06OYU(Idj_&l1tV^F6(i zsaur4I>MWhNV$2xcBd_5yx`c+5`v5euOPL@afsKEo3l8p&T}Ovbg%_Sl0w}J6*@}E zmn|yV7F!Ef_ks$ZwjG?B5S&LKYv4);mb4^ZKrvC!8%lx{@x2g9?TfJPGf6Ek_kyzv za)Hb=VMTMO0?Sh+ z;a{xECnj6b((aQdR~#W|hJnZ^KtaUCh)$|Wq0)s= zd^@TQcR9)5rZX!`z1Bn#{agO|Q4|YS{Di$xkZKdie-kMynu3qI#p9~_(-p&MOSuWRNW z19aGL7weQb4!*>&CH4$`N`};5OGB>QlaBi6K|^P;t;UQA34=QmGwGSu?Ix`kLw@83 z=B>JKV`tq5FXScbh)*L`mY5$iV?O*-T3L^dI~U?j8Oqt#-i>e@6LT}PHtPfS2=YJA zJ!jsGBYk3@gV>VSvQu``!roybTsE z?hmtn(Y!D4HR{MoN9rs|m5Q)Pe=Y2Lxi0Lty{}iBq=}IGi!bh}1hVM{7^fN%By zq{HppBa;Qdhk-=}G5@2Y{)AV(z!-NJ1E@jNS!a0j}omTWv|`oj}K_8dp@9hvTf!ALr-^^jq|x|_MuJO zw^fXDfrHL<#c&LzHw|mu=nN|K_d||~_Wp4Ac%6)*_Gd|@5cTos21NX0$}Um+CV2SL zSD2M7MBwB63t0N1aNM^mk`9iUlutMK_V*?tF@ZSaF|n4D>0+bv0L zqq!-Lxj_}uJCU3%@?LTh8c~9%HYq>mL>ELgQFWSN_*5fdw2UHwE{P_RxIoJWDz>=N zpW%HR(LJS*X@%Scp44N7o0!YmRB7_*f!@}L&R<6cvkb8~S-wLJlgj#^$>5?1Z|jo7 zCqfQztJq~FyrH5Ei-YQhXDWl(y&`S z3e;;}9_NapQTuCoq({$lKDwYS$8#-F_uAzrgf*>f81Znldw;T$SL(U1=J2#5O+m9A4-GzN}Noe&m zLCbJ&y;wbmSTmR(IN$PuPzi}z26jE{S7l6=u@P|q5o(j;9b)H2=$});Mw$|uRB(QVh zf-MJ|Z#|8Zv(`g|gPDrR-6#TUM?>-ckqetgFAt-cBKbr_F}E)>sxVjYNysWEmZrj@A z?js_Am7TC6oA4{H9iD;cV-U>7^^<(aq9bid`VZmu!U1jzU6K>z%4Hs3K@Xa*sh{D) z&```LgOX>2gvkiZMCehtMZSo$7B zr<$~S^@3jTb3JywxGf>DJaXTKGGS_ z3jNqb5jY#HZI8G?Y0n$|C0lW6_w7-@xvAgdD4XZ#I)mBArr7UU*{7tShIO+)7jD^n4sqI7T9_*GuST5`C9#+t_JwqS#UZ)g3S z%?yxrJ8h(1=#uM`x32A;l{FLmH!}W*#2i>HH=9Z=oz43X0lWDq%#RqG8ciy27TYjc zxl+2kh)6+}n=D@`xdF9hN<@$4RgDpE@=*G)WK(-IehmIVNk39A3tv`^cHk zjYdbqR=EW*j*$;sZEVpJOr=jDXIT~z2tHYyO0rb|@R_*Ph zz_&5%M5KGdaJXdTRjH7J3n}4d*`)N5=}oc2L4zE^jhZQ(zVF!`7%cuQj1U@ziR*(Tf((&2@3gez z3-E;smYNRRB%%sJku0jEsozjkRvScT^c`lXTmX;GOYl*)Z^@3}4Q{2EdJ%-yVphe=ZSMfMK4R!+>n$^OW(MI8w{^I(IFX> zlRMLyvvr6p>u4O_afmKJ`Drbmhda2cw|CeNRVF-76?&GZ)Hxh!2S$gNG+64Jf&`n? zyBJxa;-nra8MeYxol%H4bF>%y8rRg5)D#8y9=01>4|5nwK|AvYbZB;>LUJGMS|N-g zKQ9LM(cWo@bS(XF&}H9UpKrKZ5M=s>C2P5x(@ao-jc70)!U`o_n3DPP7s)Jz za-$Zcc--c5U+w_rEXi!$vnF-Az8zG(yxPao9{R_@9QOueS2FnL_61Xz{J1Gg!`rSu z72ckGi1St2TGowk${E=M9iEQ`7w^3T7{Kj4?F#I~5lE$%$&-~d&7VKJyT}CnMT>Gw zBwu9hhK-z0jlt#!O=?}Ub$2RJCBi9$R0)2luw+8ujvjX@z3E%XB`t>QWRsrK0BxZx(j8F7euVQr+1Fes^wW&p~k@ck~NG{X!7otz&apf+5 z&y@}khljEL_`c!D`-M$PW&%MlQbYet!OpPO=+9R*|Cx$y$Ve&K)C32s))vRBU-{r9 zDi>0&$eG$-sIG&VWNAYex^p3tLhCvxpJ-7(Xf6Z)-JV3>mE8{|4tSZwWpO$@7 z_^L0fBPr(iENv+LuX_dBP4VhTrPf7Xy4bG1JhG*f1Hvhxf@2x=pG<`OvFkT~_YkaJ zEx+06M5dzYnr|hdZxqT7m9AF;9a0wwTsWVu35#Ae<|29UA*|G*J_jIQV{xh=OCC zvU=M+HCdrtJ^?>0O~y*@Ddkh@kTXw3SOv9M_***nOB|qY7~F@N%+u)&^^1a21bb*JnO3`ito)Z!FKnB|Yy^_XcvR*b^L4}= ziop+2;)uLK3P2waZIWZDg|SXpoBNW-X2h7@%B{+-NYvFn^j}8)9;2`%tYS5XR2@<- z4k0!x=FyfN6?O7Yx=aZdAH$MP^?k0W`zg({ zBBn$s_BmEKJ|$>9tFu&b8c7=kAbKuYwUh*+TLtu4yT&{2i~b98AJSFeRGWwb8`cLi2dKAPXOpo@E;9h!gi z7HWJsQ7VJCtNn@v%g1qn&%8Rok7MJeeM9T98#7!sLc51!Ee**ge=I6JbzK92XT$5^ z<8=UGq{agMwL#nCz1hYMFW8aZ!bJ@5`l9ptp`pUdz)S<^KNgfLUpQ$n9gU0hb^b^Nrvz_!?AH`nRO~ML=<{7NQC~@>t6jqcMmQY)_^uS@? zncR;icsqQy@c#Q;&7FaYTBLxD!*IJ1Lp6}?xE(Rhbr+k`-c+lbeLIQ9CDNy*+gqr# z@1LhAq*>?odPDGWbH)0AfrA>l&8n@e;L=Tk<~J;McNLb#91LJP&hj&7;&}Ayc-vSP zm@Cj>VZ3r7csd+>MzGu0y1tTC!VGeR)!Z(pYvC(z#I?T*5;-Y*$QsFthUXkZG%!8Exdo$Qmq7in!V`+xb3&s1| zGe5I>-(JOZe8fZ)pcXA}=DhZ7VD69S$qE+%wi{;;RL71czF;Rm)?H1!FAW@@5a8HI z15aY!O}B>Vl^dp-e#g1~2wMJ1f<*B$AGf#he2>_Ev!-0)aQt-eF`Pk{jG6?*E~ng& z_$3U9_WKQc*q`JF0@HooER|H7R`SnzX<)s8Ky zF924OLvF!gXKe5nLU4vyx|}I>%rAA=UaNH9AFyJ$*Wq?c@$?LJjEAC%)+DYOYzOq? zh2R*aM<#+-rMS<@y>Z3qdG;4=K3;Fmrlx8zAA4!eNRhM z*VL(G6;Il$imCj7lJvyHA`G||D)M_Z-HNcyoviBC^lB5jvR|al}Q>$pdGz93_j@Eo!y|lfhxc4*^ z0)hjXv=wBmL=x_<^VT#yF4o38F5okqOd0oe9%)!uBdN}m`Ko7D6lhq0GOg+|!6BKR zFv{Wh1+3ST1r`gHU17Q{PQ5EObk^X7$U(6PTH%n>^RP!G;8Em|+bg1#3tB$@2lDhFad#Cm9 zIIET$@7&Q^oR_E*G(GJ@Xg^PH>qet!8T!%#V8T2L5;rW6^5JoUa^;_BvLWerm`p z6I&adR4bOx9dqXg#EYp7?(yFv@UM=6y(eUJR12XI@~y*ABx>@iU-cpku<7AlbC{cU z3o%)_aw^@iA!G^E{{7+wW1>vey1xI+Ao+~I3=+8DvV)>9vHy+2bmD_~!1a8S86MdmzW8(B7W7MNO(L+=ysN&?mS1y(Ov7n6ks~}<}E0axw;lk;D}!biI@TK+|hY2 zVv(m$p|){f(IAGQ$d)z4#~*;vGXJ~3nP=l^IS+h7g+Za(ySlnMjvOu?(=gw&U!*LK zx_{LmT9D4>8KM=pE9Xo{by`eU z5!Fq5vtr`HuH2VRzbEB8>I46ozh2Aw7~h5C&_7>>@Fb!)960N9N?Won7ug5K;=KUa z+5Ack`cgWs?zOh1W#%K5Xq=h9;o_-l8|u>p8))F((LTYLsYg_Au9@bHn<8;&0n zK38eb1Z|Iijn>Cg=C+`l3{-=yP9zc?)_%ZdjzhoNM*CfKo`TyL0z2+yH>PV+Xrmx> zG*P$dJPG{DnHXc(!n)@T&FSPLj*ZE~>#6#Miw~wZy#Nb%vNn{POk6hF<-dBnk>w;8 zmGrUY%gH)%Tp}TOxhG=ciC}kK;x#l`=?q%(CGbDW*TKJC?;p%qPIPC&-V_FFmjy^r zR!1;?kZWca<3iHD7S9~It&zm!$cTt2X0oE@+`tz}Xq=7VupM?Qe5KH3mofzAw7vg_ zfA;f22YY}yTM~bCa-k+Ud@*;ZwZ{QhMBdCMd;>@tiuz+Tm=w=PypD$# z@s8&|>-ldm5ZvOB^!vTg#=&+tSuWGrj0TTuDV~!7_kX797~au@ZQ?R3wc20n>3yE+ zl3du>t(#s<7QA0--WM!BPDvbz{FfO-;fLi{yFc*X>u@Ak-%oaORPP!rNWzH1Bfj%L zF7RK9uTQ@j9VQ*F@rQxMjB&KyRDZnH#aOEW<_VUbT~ai!8y`A8QWx5)LkID_!;n*| zAx6=z95XlhfMF(`_IoaTuO;hSAs%z-m`q%XHWaf<0-M^x3g8zs5^ZBRut#_Q3Njtb z(^JS3eu&}9YgGZMZf8hJP6M)BYO!6uB~hS2^#6X`r%eG(b6NA;OEPuJtq93+eO=jj zXk7<;nHjql@}X=?7rSB2vb>3+et((C(Q^5KxauFvd7j~Kbu}D;)A5maJI9TW!a58a zC#S;mc@5*&XR&Rl+di(CaDQ3hv4K)PVR{4G3?+U&8w5yau6p$#KvXqN7egy!nK*Wl z#JDpXA5Zl?PWc+o$B%zy_KvS?H7MFJO@7O)d z!2k@EW#+ac@d8-FBukepzoW`*JEe!Hh-fBGI>G9)ky{VI3mR;|9c`xT>-3Gz+w#XnJfipW3_Zt9 z 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

    CX{ZD(*% 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%0Dfa>seFAzbm?`@=_y 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

    9U;3b==`WgDL}YkkVFNP0D`hzz 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 Date: Sun, 17 Nov 2024 22:29:19 -0800 Subject: [PATCH 60/74] docs(hydroflow_plus): rewrite quickstart clusters page (#1567) --- .../hydroflow_plus/quickstart/clusters.mdx | 110 +++++++----------- .../hydroflow_plus/quickstart/distributed.mdx | 4 +- .../examples/first_ten_cluster.rs | 18 +++ .../hydroflow_plus/src/first_ten_cluster.rs | 55 +++++++++ template/hydroflow_plus/src/lib.rs | 1 + 5 files changed, 121 insertions(+), 67 deletions(-) create mode 100644 template/hydroflow_plus/examples/first_ten_cluster.rs create mode 100644 template/hydroflow_plus/src/first_ten_cluster.rs diff --git a/docs/docs/hydroflow_plus/quickstart/clusters.mdx b/docs/docs/hydroflow_plus/quickstart/clusters.mdx index cfdecec5e346..1d234d5b43cb 100644 --- a/docs/docs/hydroflow_plus/quickstart/clusters.mdx +++ b/docs/docs/hydroflow_plus/quickstart/clusters.mdx @@ -1,90 +1,70 @@ --- sidebar_position: 3 --- +import CodeBlock from '@theme/CodeBlock'; +import firstTenClusterSrc from '!!raw-loader!../../../../template/hydroflow_plus/src/first_ten_cluster.rs'; +import firstTenClusterExample from '!!raw-loader!../../../../template/hydroflow_plus/examples/first_ten_cluster.rs'; +import { getLines, extractOutput } from '../../../src/util'; # Scaling with Clusters So far, we have looked at distributed systems where there is a single process running each piece of the compute graph -- **compute parallelism** (like pipelining). However, we can also use Hydroflow+ to run the same computation on multiple processes -- achieving **data parallelism** (like replication and partitioning). This is done by creating a **cluster** of processes that all run the same subgraph. -## Creating Clusters -Just like we use `ProcessSpec` to create processes, we use `ClusterSpec` to create clusters. We can then use the `flow.cluster(spec)` method to instantiate a cluster in our graph. Let's create a simple application where a leader process broadcasts data to a cluster of workers. +## Dataflow with Clusters +Just like we use the `Process` type to represent a virtual handle to a single node, we can use the `Cluster` type to represent a handle to a **set of nodes** (with size unknown at compile-time). -We start with the standard architecture, with a flow graph and a runtime entrypoint, but now take a cluster spec in addition to a process spec. +A `Stream` materialized on a `Cluster` can be thought of as SIMD-style programming, where the stream represents many independent streams on each member of the cluster, and each transformation of the stream performs the transformation on each cluster member. -:::tip +To start, we set up a new module in `first_ten_cluster.rs` with a dataflow program that takes in a `Process` for a leader and `Cluster` for a set of workers. -If you have been following along with the Hydroflow+ template, you'll now need to declare a new module for this example. Create a new file at `src/broadcast.rs` and add the following to `src/lib.rs`: - -```rust title="src/lib.rs" -pub mod broadcast; -``` - -::: +{getLines(firstTenClusterSrc, 1, 6)} +We start by materializing a stream of numbers on the `leader`, as before. But rather than sending the stream to a single process, we will instead _distribute_ the data to each member of the cluster using `round_robin_bincode`. This API sends data to a `cluster` in a round-robin fashion by using the order of elements to determine which cluster member the element is sent to. -```rust title="src/broadcast.rs" -use hydroflow_plus::*; +:::info -pub struct Leader {} -pub struct Workers {} - -pub fn broadcast( - flow: &FlowBuilder, -) -> (Process, Cluster) { - let leader = flow.process(); - let workers = flow.cluster(); - - // ... - - (leader, workers) -} -``` +There are a variety of APIs for sending data to and reciving data from clusters. For example, we can `broadcast_bincode` to send copies to all members, or use the existing `send_bincode` if we have a custom algorithm to determine which cluster member should receive a piece of data. -## Broadcasting Data -When sending data between individual processes, we used the `send_bincode` operator. When sending data from a process to a cluster, we can use the `broadcast_bincode` operator instead. - -```rust -let data = leader.source_iter(q!(0..10)); -data - .broadcast_bincode(&workers) - .for_each(q!(|n| println!("{}", n))); -``` - -The `Stream` returned by `broadcast_bincode` represents the data received on _each_ process in the cluster. Because all processes in a cluster run the exact same computation, we can then use the `for_each` operator directly on that stream to print the data on each process. - -## Deploying Graphs with Clusters -To deploy this application, we must set up the Hydro Deploy configuration as before. Our deployment script (`examples/broadcast.rs`) instantiates multiple services for the leader process and the workers. Since this script defines the physical deployment, we explicitly instantiate multiple services for the cluster spec, returning a `Vec` of services. We also set a display name for each service so that we can tell them apart in the logs. +::: -```rust title="examples/broadcast.rs" -use std::cell::RefCell; +{getLines(firstTenClusterSrc, 7, 9)} -use hydro_deploy::{Deployment, HydroflowCrate}; -use hydroflow_plus::deploy::TrybuildHost; +On each cluster member, we will then do some work to transform the data (using `map`) and log out the transformed values locally (using `inspect`, which is useful for debugging logic). -#[tokio::main] -async fn main() { - let mut deployment = Deployment::new(); +{getLines(firstTenClusterSrc, 10, 11)} - let builder = hydroflow_plus::FlowBuilder::new(); - let (leader, workers) = flow::broadcast::broadcast(&builder); +Finally, we will send the data back to the leader. We achieve this using a variant of the APIs from before: `send_bincode_interleaved`. This is similar to `send_bincode` in that the elements are sent to the leader process, but the elements from different cluster members are mixed together into a single stream with the same element type as the sender side (regular `send_bincode` would result in a stream of (cluster ID, data) tuples). - flow.with_process(&leader, deployment.Localhost()) - .with_cluster(&workers, (0..2) - .map(|idx| deployment.Localhost()) - ) - .deploy(&mut deployment); +{getLines(firstTenClusterSrc, 12, 14)} - deployment.run_ctrl_c().await.unwrap(); -} -``` +## Deploying Clusters +Deployment scripts are similar to before, except that when provisioning a cluster we provide a list of deployment hosts rather than a single one. In our example, we'll launch 4 nodes for the cluster. -If we run this script, we should see the following output: +{firstTenClusterExample} +We can then launch the program: ```bash #shell-command-next-line -cargo run --example broadcast -[worker/0] 0 -[worker/1] 0 -[worker/0] 1 -[worker/1] 1 -... +cargo run --example first_ten_cluster +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 0] 0 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 2] 4 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 2] 12 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 0] 8 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 3] 6 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 1] 2 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 1] 10 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 1] 18 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 0 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 0] 16 +[hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 3] 14 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 8 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 16 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 2 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 10 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 18 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 4 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 12 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 6 +[hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 14 ``` + +You'll notice the round-robin distribution in action here, as each cluster log is tagged with the ID of the member (e.g. `/ 0`). In our deployment, we are sending data round-robin across 4 members of the cluster, numbered `0` through `3`. Hence cluster member `0` receives values `0`, `4`, `8`, member `1` receives values `1`, `5`, `9`, and so on. diff --git a/docs/docs/hydroflow_plus/quickstart/distributed.mdx b/docs/docs/hydroflow_plus/quickstart/distributed.mdx index b09f06ac10c6..d6c5dd27c7f7 100644 --- a/docs/docs/hydroflow_plus/quickstart/distributed.mdx +++ b/docs/docs/hydroflow_plus/quickstart/distributed.mdx @@ -17,7 +17,7 @@ use hydroflow_plus::*; pub fn first_ten_distributed<'a>(p1: &Process<'a>, p2: &Process<'a>) ``` -Now, we'll use a new API, `send_bincode` to establish a network between our processes. Given a stream on process `p1`, we can send the data to `p2` by calling `.send_bincode(p2)`, which returns a stream on `p2`. So to make our program distributed, it only takes a single line change. +Now, we'll use a new API, `send_bincode` to establish a network between our processes (`bincode` is the serialization format we are using). Given a stream on process `p1`, we can send the data to `p2` by calling `.send_bincode(p2)`, which returns a stream on `p2`. So to make our program distributed, it only takes a single line change. ```rust title="src/first_ten_distributed.rs" pub fn first_ten_distributed<'a>(p1: &Process<'a>, p2: &Process<'a>) { @@ -45,7 +45,7 @@ cargo run --example first_ten_distributed [() (process 1)] 6 [() (process 1)] 7 [() (process 1)] 8 -[() (process 1)] 9 +[() (process 1)] 9 ``` You'll notice that our logs are not particularly descriptive, just showing `()` as an identifier. Furthermore, our processes have the same Rust type, which could lead to accidentally mixing up streams across the machines (this will throw an exception, but it would be nice to have a compile error). diff --git a/template/hydroflow_plus/examples/first_ten_cluster.rs b/template/hydroflow_plus/examples/first_ten_cluster.rs new file mode 100644 index 000000000000..c421570fad88 --- /dev/null +++ b/template/hydroflow_plus/examples/first_ten_cluster.rs @@ -0,0 +1,18 @@ +use hydro_deploy::Deployment; + +#[tokio::main] +async fn main() { + let mut deployment = Deployment::new(); + + let flow = hydroflow_plus::FlowBuilder::new(); + let leader = flow.process(); + let workers = flow.cluster(); + hydroflow_plus_template::first_ten_cluster::first_ten_cluster(&leader, &workers); + + let _nodes = flow + .with_process(&leader, deployment.Localhost()) + .with_cluster(&workers, vec![deployment.Localhost(); 4]) + .deploy(&mut deployment); + + deployment.run_ctrl_c().await.unwrap(); +} diff --git a/template/hydroflow_plus/src/first_ten_cluster.rs b/template/hydroflow_plus/src/first_ten_cluster.rs new file mode 100644 index 000000000000..f328d3b860e8 --- /dev/null +++ b/template/hydroflow_plus/src/first_ten_cluster.rs @@ -0,0 +1,55 @@ +use hydroflow_plus::*; + +pub struct Leader {} +pub struct Worker {} + +pub fn first_ten_cluster<'a>(leader: &Process<'a, Leader>, workers: &Cluster<'a, Worker>) { + leader + .source_iter(q!(0..10)) // : Stream, ...> + .round_robin_bincode(workers) // : Stream, ...> + .map(q!(|n| n * 2)) // : Stream, ...> + .inspect(q!(|n| println!("{}", n))) // : Stream, ...> + .send_bincode_interleaved(leader) // : Stream, ...> + .for_each(q!(|n| println!("{}", n))); +} + +#[cfg(test)] +mod tests { + use hydro_deploy::Deployment; + use hydroflow_plus::deploy::DeployCrateWrapper; + use hydroflow_plus::hydroflow::futures::StreamExt; + use tokio_stream::wrappers::UnboundedReceiverStream; + + #[tokio::test] + async fn first_ten_cluster() { + let mut deployment = Deployment::new(); + let localhost = deployment.Localhost(); + + let flow = hydroflow_plus::FlowBuilder::new(); + let leader = flow.process(); + let workers = flow.cluster(); + super::first_ten_cluster(&leader, &workers); + + let nodes = flow + .with_process(&leader, localhost.clone()) + .with_cluster(&workers, vec![localhost.clone(); 4]) + .deploy(&mut deployment); + + deployment.deploy().await.unwrap(); + + let leader_stdout = nodes.get_process(&leader).stdout().await; + + deployment.start().await.unwrap(); + + let mut out = UnboundedReceiverStream::new(leader_stdout) + .take(10) + .collect::>() + .await; + out.sort(); + + let mut expected = vec!["0", "2", "4", "6", "8", "10", "12", "14", "16", "18"]; + expected.sort(); + + assert_eq!(out, expected); + } +} diff --git a/template/hydroflow_plus/src/lib.rs b/template/hydroflow_plus/src/lib.rs index 37c22f9c1c6b..4e3abdc4f4cf 100644 --- a/template/hydroflow_plus/src/lib.rs +++ b/template/hydroflow_plus/src/lib.rs @@ -1,4 +1,5 @@ stageleft::stageleft_no_entry_crate!(); pub mod first_ten; +pub mod first_ten_cluster; pub mod first_ten_distributed; From 64578f39d6f87e3a96fe4c3a8599b92606e1510e Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 20 Nov 2024 15:19:21 -0800 Subject: [PATCH 61/74] fix(hydroflow): avoid pulling in `clap` as a dependency (#1578) We only need it for examples, so this reduces compilation burden for `trybuild`. --- hydroflow/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hydroflow/Cargo.toml b/hydroflow/Cargo.toml index 96d2616a05ab..056d96b1c3d3 100644 --- a/hydroflow/Cargo.toml +++ b/hydroflow/Cargo.toml @@ -44,7 +44,7 @@ bytes = "1.1.0" futures = "0.3.0" hydroflow_deploy_integration = { optional = true, path = "../hydro_deploy/hydroflow_deploy_integration", version = "^0.10.0" } hydroflow_datalog = { optional = true, path = "../hydroflow_datalog", version = "^0.10.0" } -hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0", features = [ "clap-derive" ] } +hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0" } hydroflow_macro = { optional = true, path = "../hydroflow_macro", version = "^0.10.0" } itertools = "0.10.0" lattices = { path = "../lattices", version = "^0.5.8", features = [ "serde" ] } @@ -81,6 +81,8 @@ tokio-util = { version = "0.7.5", features = [ "codec" ] } getrandom = { version = "0.2.6", features = [ "js" ] } [dev-dependencies] +hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0", features = [ "clap-derive" ] } + chrono = { version = "0.4.20", features = [ "serde", "clock" ], default-features = false } clap = { version = "4.5.4", features = [ "derive" ] } colored = "2.0" From 8d550b94ae2c08486e1c2222d37e3ca8b5f018b7 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 20 Nov 2024 15:19:31 -0800 Subject: [PATCH 62/74] feat(hydro_deploy): use regular println when no tasks are active (#1577) Significantly improves the appearance of Hydroflow+ logs when the terminal causes wrapping. --- hydro_deploy/core/src/progress.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hydro_deploy/core/src/progress.rs b/hydro_deploy/core/src/progress.rs index e4f8bdd6b4f6..545773db67d8 100644 --- a/hydro_deploy/core/src/progress.rs +++ b/hydro_deploy/core/src/progress.rs @@ -370,7 +370,9 @@ impl ProgressTracker { .lock() .unwrap(); - if progress_bar.multi_progress.println(msg.as_ref()).is_err() { + if progress_bar.current_count == 0 + || progress_bar.multi_progress.println(msg.as_ref()).is_err() + { println!("{}", msg.as_ref()); } } From 7c992945d30b991ca11f7991133561dd333d9766 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 20 Nov 2024 17:15:27 -0800 Subject: [PATCH 63/74] docs: add recent papers (#1573) --- .../papers/auto-compartmentalization-src.png | Bin 265230 -> 265230 bytes .../src/pages/img/papers/conor-papoc-2024.png | Bin 387718 -> 387718 bytes .../src/pages/img/papers/david-papoc-2024.png | Bin 446215 -> 446215 bytes .../pages/img/papers/david-sigmod-2024.png | Bin 150389 -> 150389 bytes docs/src/pages/img/papers/flo.png | Bin 0 -> 198877 bytes .../src/pages/img/papers/hydroflow-thesis.png | Bin 247627 -> 248833 bytes .../src/pages/img/papers/joe-applied-2023.png | Bin 384270 -> 384270 bytes docs/src/pages/img/papers/katara.png | Bin 187076 -> 187076 bytes .../img/papers/keep-calm-and-crdt-on.png | Bin 302813 -> 302951 bytes docs/src/pages/img/papers/new-directions.png | Bin 361869 -> 361869 bytes docs/src/pages/img/papers/suki.png | Bin 0 -> 200916 bytes docs/src/pages/img/papers/tiemo-cidr-2024.png | Bin 424847 -> 424847 bytes .../pages/img/papers/tiemo-sigmod-2024.png | Bin 305469 -> 305469 bytes docs/src/pages/research.js | 40 ++++++++++++++++-- docs/static/papers/flo.pdf | Bin 0 -> 858798 bytes docs/static/papers/suki.pdf | Bin 0 -> 444370 bytes 16 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 docs/src/pages/img/papers/flo.png create mode 100644 docs/src/pages/img/papers/suki.png create mode 100644 docs/static/papers/flo.pdf create mode 100644 docs/static/papers/suki.pdf diff --git a/docs/src/pages/img/papers/auto-compartmentalization-src.png b/docs/src/pages/img/papers/auto-compartmentalization-src.png index 8785f09babac7ae9aa1eb128d630879999c09ed2..0426162663369e0099cac3fe2ab9ef2933cf293e 100644 GIT binary patch delta 173 zcmeC1A<#ENU_%@uC%2G@CL1s38jI$1#`bhZ5M}~m=I!Z>ESEX!4Gnb-EkcY8tV|58 z49v6*46FHTeU0=P+ff_X68cH&A YQ;SOya|>|mdHW!11p~wMo!l&;084`~(*OVf delta 173 zcmeC1A<#ENU_%@uCp(Y0AWQQLCh6vM#`bhZ5M}~m=I!Z>ESEX!4a{{7jY5o!tqjer z3{12Q46F|m+5W}Rn1NyXPHvV^0AHgpX#fBK diff --git a/docs/src/pages/img/papers/conor-papoc-2024.png b/docs/src/pages/img/papers/conor-papoc-2024.png index f0c3ce84da1e8028c7364b2543be15799feed4a5..83b1fb2a0998afff378c9a1f696ea5de49c2728f 100644 GIT binary patch delta 153 zcmZp>D&BTgyrG4$g{g(Pg=GtC_ltT%LtR6Q5F-OC6GJNlb8Q0yD+7bv`#4v1yQLaySgspaIuVl9`)YT#}eufLo9E^h6m3hV5K0 HS!);p>*Xt- delta 153 zcmZp>D&BTgyrG4$g{g(Pg=GtC_ltT1b6rED5F=wNLvt$wQ*8qSD+2@b)0dwxFfgc= zxJHzuB$lLF<>sekrd2W+85o)9V$cndoBE@amL-7Epr*Tti7_ZfbE!Vr~I$J$vs9eq~_T K-hGy>h7ka-cndoBE;5IEg0;oX)uAw9|H?_DVF}DD>9>Hd>b_Ry+ J-DlZq7y-0UE0h2L diff --git a/docs/src/pages/img/papers/david-sigmod-2024.png b/docs/src/pages/img/papers/david-sigmod-2024.png index f6751f75f5052566b40d6a1ca8e56c188c8e4fd7..ec1a62174cfbf363750a262e44aa0fe936976111 100644 GIT binary patch delta 142 zcmex5jq~d?&JB5toZLbpnp`aV9|$*>Gq#sAGHx$tWRh_4G&IyTvL8JKAs z7+4t?uqmIcWME)WEpd$~Nl7e8waU#;$xN$cFfuSQ(Z!~rysq%*^c${BG7{Ki6|w`Q Lw)40#WwHVQ0qG@z diff --git a/docs/src/pages/img/papers/flo.png b/docs/src/pages/img/papers/flo.png new file mode 100644 index 0000000000000000000000000000000000000000..0bfb73dd8dedc34a406d739a37a30f7be8539c61 GIT binary patch literal 198877 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ⅈ;(^nVEu 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 !ndPT{{|k%T8Z(5@K{t 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ⅈ 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%0A~;j$N~R8DKL~K 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 literal 0 HcmV?d00001 diff --git a/docs/src/pages/img/papers/hydroflow-thesis.png b/docs/src/pages/img/papers/hydroflow-thesis.png index 40515491a2c3346faa546a82b581183a3a617f71..4a11caf971052d0448799cfd0092b1e62e19c4c7 100644 GIT binary patch delta 155608 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 delta 154506 zcmb5WcU+U{)-~+tIF1DwMP#IiMhT)60qM;Gq7*@TN18~D1R+2Ow+%5MVCXf73P_h4 zLW@X{(3K7eL_;vtgdzzLl6;vnGv}Q5Jmr1A{1fi`7eaFFYp=cb+Uw#`hG0*E;KSOV zPTvRaqhR)@u5=a7SQq9@-ApFj%#!z+y%CbdWHJ$!GFSwI$bT5MI0xF=#->sOvQ(?8 zH61;^z}+W~UU{6fJf3#LclD?-=!!*kc{IKmxF6Q9eeKY%_kX%fVR0hYx|Ab+QTFZ^ z-*xHkcWwA`t3m_Ti7Gmv6F6pBotTv!Kmv{@SNg>s9w#|vY61P`q$8`dAQ{`rq*17tS% z->xs;9!!`qiMo(+q;|9^)k2Q$UOJ<`2(HmN1w2#U{;C-TI-TY^Pl8YY#E&2>{X#uZ z_K~;Vv1m}yR5yVC_UtXe+D7hDP_>pUw1;lzM==-?28qOVn2K+ty58Rokcp)w<_ck5 zxfDNe#o?B-&`N<8H;Qy#1EJoJF6Q3SS&zF*DS&lVi@=-^nMksItp)X?)CM>(R`xdy zM6_JMZ@La(=zt{Jk3F%KNpN=X0@&*u80j1|e<88hB!`3;o^? z=9zmmTeRY;pHADXqAqMe&K+w(tU959Dv*BAB&VoU3r!%Trn~r1&_Mkg5DXd^gB*}Z zAvQPT2JtPWfx_61(xtFwIgqjQa47$6O2rr#SMwF349Z}$yiGBoxFjW`V!J{1mm`)I zjf0}0Q(?_#@0SaRsovZBS1dA64QUR|89uxJEjGub0{?61fPcN<^6m4vJK$ZXFkJUV zSLCc7mx9YypI;idCnkV;ZpR((%r@B>o_0fBaI_1cw0XewE7n%73dLCA2ohS`5@zBX zb*JvW$uPPGzObmBxmgDq0|GzeZOcRb-3JQXTg-IVi@*uj>qw5hUFT?!2Rm~vLW?3c zK&+E{$2zzoj0-|(MKR@s0I&NPg2A(WOOJ8vX5MdW`0eMAmKktFd6~W^{p4D+|8zM> z?MK@M6Q`4!=b)*37}Y`H1xqn4^^(72WgArdh#o@gp7;&>=nSXcPec<$)A?a1D-YGe zt-a7`MH`w#wn?qoL0n-4$i&t!^xfVk(EDy|Yo@RG@utcXDLj zxnht>5MVtEX!=F)xzl)X*uXoCem~Iz?l~(|kWvIO-qBzSa%gCk)z??-xgK8%Dkl(I zM)#LA*tN*qnt0Hdb|-V$$F=E}nxs-tVr8>FlJ-zEHPWrA91Ysmmnoq1G3^-VT*1)k zB=PB^M~!3(q89I(^``*>NGUI5Vy$+8O~j%ZLAA4cIVim=N%-6Kro{I=QuuaTULzLK zx?EYq0gY*N)-)Fp#|)?U!ntQ!Ewq{>*B%7goisD3lru=0L$RaurIxDDX;SvzW$S2E zL>=P@7Om^J`>ocfJQSujRsPR~ck?LxYu*3c|NnL2|07jyYTY^>GNAazryO>nz=70Z z(-|~6+hqDdAMdWX+E(W|uOx({1!|S?<`QACV>+WuXCVydtua+olh=-qzd(y6HL?)h zcro2j9HIQ4Z_Z~$arOJtc!gGAMIci=^3&6z2-n|=llR!}3#{3GtGfDF*m~L)xzU*8 zDK;!n|I5~u6jXT5^ z>gyiU$k$8h?k!C=vP!G@?Ke^It?_KIE_1sSg!y41Mh@EW0>KQ5B_si4(s`6)EAx62 z;~CLFzV>2`2QWL^V8@+c=4Ed7340p^esOBz6&NqAHV1##ta4csfZvjbq!7n(yjo<> zE%a~LQTzpPYl-9c@0TW`8s<_Dpu`%hr}6QaX9gAZ(-n?feH?*f&Au&Un-^wDMO!bU zTVG@}-0WfNDfq}L1FK$jc}F#bfb`%7cg|MLms*s2=NE}Iz zH^bPAyZv_r_eT2_cf@7>pQVTLcToTFD)W?c1Gn`vEPko!K(MBybsIwIq|7gi^lOn34-;KBRlv{>4hI@jI(@D}wa5U!;GO_tbB?z|e?Alv) zCbHlcO|1Z#L>=$E!;urF-D$^=B;2R;y3XGOy1I9JB|S_ z>xeE_07P%KCtwh@Lq>$xP24^`?gwSvNL(be%rHyt>;Eb z(oooU62mYbxt}ekH&jeRI(ZB;n=oc679~3%M`f>X2I(1L&dc%cEv=#h8aD}R%HQy> zknCI+0HtN=)f3SS*@9D5b+Wa^;Z{`cn+*dYR~A|@j`U1G?A*Xlf33Rm&tn#GBFd(J z2Z8_m@HO64=-;kioeLWJ`QNV385L{=?%L(Kh=%K)OT(?rBET+n-O~q0k0i!zL_wFN z0dOm8f&YhMcr>0LWzRmF=cMju8sLNkAJel9`4W>)t2`t?8*LYEWo$Cg>Sv%$p`2u- z-lVw#G=CdL-0SM`y%c8RB#KjRaQ^a>a4qT{q26AlEJkG~*lP*J+` z7Ma_kH1=DNN#v7Y8|rZzS9i+ZYts4veT=t)S*q&asJ#Ft3Pa=-gFM6RsME#eia`Tv zyem{)X1bVvHp>q+q^6E8xP^)<6ccoTGoN?VW6|rvEjCop5lGY6bEmCA2O%E6$nz-q zl5rbGRy9bmR1$2krmtztdQ)_}2rT-KIKLYt9^F!6gs10%sv)V9ul`>1%m0G5f86@7$hNHBPsKbwamBC2QD%e!$k%Ho)X0+qc7rKj^PfOifxDDU8TFO5h2(rBes ztuAkaifZ3{=atly@hMIY^4gRLlC<{DxwSg*2G!*p?h_so(&FjveLvrhQJpmdMvtkS zg6^42ll^rJua|Za0=0(`ovH)6lI9lJZ$WCEuYxQIk3_js+$*fs= ze(%!Jaju;#otHkGJMV`9NI%c+AVlIOoZh4%rXvX7(+-_Apf5cWGhNc7d0}EqmvR6(7rsGPn1J1&eiW_)kKW27j8s;ru3iwV zLpm8kE0B6NuuMW~naiBL@1c{mf351n$DitkDIF&NI~=9Z&Vfv1o=i}NKfm1ky1fcu zxP5quKfrX9VzJt2My~I|dl*ZPA3fBiYj1SM%KpT7+LHhs@X6ssq?11;mx7Dj>I1Q! z;I{LTxcV6#5X-zt4s{>$rWv!muX6I}?zmEgBiNFmA`>Uv#fHA=wY~Ado`}Pc{R!5& zT_46eW&}6uC;eaF&}Wv*3RfBJ1=dpA%mqTH_U!AA8}7~*v8+%_oBEz{R9s;)#}Y&= zgFGkBBy6=xd(@{~Z`MyMd#+Cx^uO5{wUT!AOMB4P)u71g#O6+_973s(^a2Cna8ljm z(^8}^cp4mWy@|S1aPx5j+@z`V#Xd;W<8A4V#ohJa^eRO@HJR?r_E*M|Qh{{mK*}e< z@*~(nKl+=q`~F(Y-rY{daxT69`;Poi==bRi0^@{xe;xkT<{v~xu#kckz!ckBC{ORA zKLtL``f0ThF7ND5iE|7*g4JSFFUprx0Kbx0kM__y*5U>@rO3r(=oG)afm@p_0@vr$ zTOpJ;om7sWar(GGh#bFjme+F$pmck=q5R5f0xm4-dQs3%?M$Q3FD<5x5dwxWBp=F9 zmjlYtyu>s|dqN5tFhy2bg|)6%6LRTUV&i$G3o-tV^c1!whiS)7g2Y-G0<;rCm2yBk zvtT{Rj?tTsF^!}T3xi-C&J3m1TwJvcwM4|g8hm&;%1KslBU!{`XQ#>rr3I`PfLm#e zv!Jn8iza0u)?c~XA7|Ys?C*5;8!O;^$Fw#Cs-`>8TnC4cR03x$>#mS%@Nyl<_8UHc zq`-6eeViCb=}aI5bAhGIV6c44y()r=exUN|>Knb^uoph|{?@>+t-EgOU$IKI%~H35 zR}tjae|$O8sm%T8P$p9BUyaCo`2?q+c!y#c(%lK~_jrWeTWx>d3EPX8D!E2IELm=K z<@J|$Hvi0xw*MhF{)s=kcAYc&J2^hOqGv9+PJ2_{*66K-9Mmq@r>kig#Z0|40qCgk zzyXg(iz?T>eB%7Ht$+O(JQPXWOxVz`D+0=%tQ(_Bb&HiJo;p}U7y=~tx#Gv0W{E62 z4h?wMm)1Df-8ynkHGHSvbXUFDW577;_C*QK8aJey+C8z%L+v9 zLP`N5iXBR%7vba#>5{rX_87tbTJ6gT|HAjbZt)lQzlZm~*Z<+`w-TM1J2n^3`~%;M z^H1~JidR-<^Gv~<)E>PZB2FoYv1o=QwaLR76OYwsU7_;zVkJ#C0)yv%)PSSbt6@k| ziSP&*Z7c68f?m}4q0`p+iq@yLnlzgpOUj(>G>yDMIgiqEs2;!@rw_;G3@P8E*j)wa zj8urWKjoek^V&vysi{+CmcG18^%%a>tZYRhdg`WC?r)j4iO&ASk}D>$FZa;bfsa?iQ{N@nD@V7|oGpz!m2Lj$v1zTE}fi<$lA8z*w1K-{K2E zwxRsK0M$f4itS!|ixfORu_{dg62~`@-%=2-Q;f81(pic{DxUoFmf@JLMCJX05ksD|QVdm8>0e1c`f5qr`k^cvJ zdmH`c`Ne`mcjKPK9F7&ND6QPdKqmJ`390FWC?Ur3O&{@jyh@%m)kSyEgQ8}peF5sV zk7yG4;FcA2nqKF1d>ei%mLx3a1=g))z(KILfIk+gpasJcIFkYOwO2SFuYtx8pVL%- zuTYnR3ZHhI25D&Dp>z||ksH(MZapE9D|`54Qz>*DFTn`_jZJ&?82^w+^8#&e2dF%v zlGOC+QuW$PBYmL?ITue zn*hTJsl=-SO147``RcJ)Zm zY4Gd((QaCzxEXSi>oKv5nX<6MA853Py>LfYXbOL%_M z&D(qyaF3?&J(d(NMps(h!`m5i_?wWf#s2?jw>$4&mvkcUd>X^AN$YMof&-moin?*@ z3CD$4I7Bfa7s<-r9H!KOY+YSkSj4!!>U_Plj1GZAL;mh~sm4j-H&{{?ASma__Pt~AA95Kpn zT!Eb*KnT2+dmQWP;@kNlWX$CbB?~$sD2=X`uJoP%2^FUoMZlLMs-1(=CYDLR@#Wz+ z1qJ3BGR1$D2-Z`$FS2+<=m?8yV+5b5_R1uY+W9pPcO~IT@0GoiPNuW9Yv3g zb8G&UCffgfngIW+LR@@_EU@e0{MxG7PNQ8b({^bMUAYra;7~t^fzgtB=b<>}>Tw~x z6?~$81+{k(9JSQv$5+OO_JF717iGjSuklY&<9y*1U|tc@!!DcY2eu6wn>txqf}ot6 z0W(|7-+^u|D$}}w{M1-CvgJnIq3fnjBTZ(lCa~=~7c&}%@cG?FaFPPxSY~S-+KUfxP3s&AS9hbh&>9FM z@)+9MV2PT3g+}C?14pf`>v6sdo{qi&dGo+$AC$3;M(AlUW})RbGb!5$TbB>hX|F^`rJP zMBiZ{HgnnjMf#F(@pZEt@igL#ia@0x@1kZ*dBCRR^2d{|mRSLF0p31ONZgWJj^{l) zgA-$&Zf48neUVHZ*+aLhnXFeFJ`2q)_18*v-7-Wi-oto_HzSHuERGCR!4n;23X;Z% zYv6sY@;$P^^QaZ^u(Om41{4jwFZc94-K%oZdxd2e9Dj(jANkoLNgIX%`;(Jr_oK8T zWKTAHxknfp6M$w{*^!!Fx@kSZNE&bBSROL%tM&GicZ?r~bq*7BC#}HG`nx<@s=^a! z=1+(xSSgaoL+pNRQ6{Q20<}=W-xe3LZJy^OH;)5&g!39HS`e1C$>9BfgIJXT<=`l{ z=%pBVNsme7gp<9}ECYZ(UK9VT-k4whf0(TNnKvd6z%zF%xX;ee^aI(}1KD}CZ#|oug-;p@HlK&WQ@q)VSk8)5Q*Nu9y=K0W z&jkTI!s{B^+AMpsPzRcwyYBLyXa&uN$zg}^rMk}2(9#q+psO0rcFk0u7?mDfw>fgc zasp(!b?o%CZ2B4WpwK{%YMzrzASDJTJ%F8@nJ^4SLbB09X%(^tlc5o+BC7$XPY6Wi z&g~;_CWKE4r%E)G429A&k|gK5bHh@uYt8n!w=J76Ct3w0BR|!LM^{pfd($G3hJsu5 z?ik6qEu-ha+Y^G*P&EuEk0%55T20K_^yaOMfmkD*QEn8zvf()@^EZV)RrG(!f&Uyu zp6`S0*>y^&^y&rE$S>ZsIMNDltJ>R#_q3kOV-|n|WltQ%0`-GXp*(VpA@zqgF-^yN zR<%-MNo7^2Ac4T?w@%Ltvm9a|ow{xENLyI)O2T3E6z01jT}_v z((v)*z+*8!66Eje&uFMEc0T#}WE0~}A6vhq&EAa&BrZKSAiuk`+Lo_o5IF0M$=IyW z6634w@B0C~>-I;LTe#)p}+3m6_^YrB`BL#H8&36%m&=SI{1R$LFH*#sAQL z|D)we3KjQWb8LI@s&2YG#WzUAQKHixNN1@2lquFf*S;|P(s|jCoL-*GGs|$cwz0=ht zH&^6tS6rCEv$65$cfHY+dY8vI-=`SKXW1XY@(L{$Wj2`M`k^mwsDtT}_1LKJveTVR z*M`74f?S!aXqdKi0x@V~m>lU$J-i{1p>7XH5?V0aW@M#ZNHU4+19veyEO89m8tAy- z{$ZWL5w3P_UtbPCtfO-Z3JhR~(BrdSaRzkCaYx;PdiqtAqqDDZ^;v~9PI|%+#P5Es zU`QNkP1Di$nxOa*Y^P++?B%2To8q4ly}}a;#m0W2JI46C9%iH!SaZ2%IByE4=y049 zMM(ohJO zss-lu=br(w#*@to0G}(p`oK0BIHfdJR;C(MTj)SdEA;{&R!RLOz#>&eHAB7ZsC!+z za{zc6h^XREhGUkmS_MX3NslKVZjptyBW(A!_7f`pS(0*$W0hp?BL&;kTZ*aLYZXw;FF4 z6d+5_`x*4hFzN#6{5i5^NU_!uKC?1zJowMVzO*;N+;g>cU$*l*(TqkNjraKF7Hs)&KeBBjC zIgvB9m3=^cjfL%Vr)GRY;y0nSb+-YvsJoO72KFYkK9E!Wcm03(_`lO{Qg225Eq0od zYDaGuSVdQxNuMvUx*FeGKEAAe3)V{fwfC;W;_(8jayIU^;%uv+MCVPDd+ob+-5H(X zw@k_(fy8>n2Kav6!+S5AQxVPRJI7m*aw6`lCTsa#<)c@pIK67vOboje= zzsG9x%f7DbX5pIiR*jlcA+0aGBf~ncZ|u!Wx)1^Wt<)mZ5i!}sx#C*i{_F|2@j^X` zkfBlji;mn%i~mZkbj6RB{vm}gFN^kQMfyq2faT4)p)w<8_1T+Wl&jqkb&$->irMPO zSm4c4l^!QawrOZ9LymnEuuq5~Scht*%zW^FRo8Fej0;GZ@$b4rmKkdwJ~ztk?+Bu; zBQ9kNFJ|NxtGi|xIV_eN757a8!Q#?I0K7@WXRL`=`n&hkQ;QPoc$1ChGnkgWy}AyI z2fWCD+=((tOQz0v7K2%IPm+wQ2MY6jBd}ojx=O!*P{o&n&BVG< zXNSd&E#1R+3I`n^Rz@K*1@)ze0yFL#j1b~&n!*M7^j+bf&F#ZOSY@Xrn@rbtrUf=K z8!tyAn%K444-fg2V@grkQsDJzMMhhsq~NYy1U969Q z_G)c?`70~ByC0tT)bgQG@zhkwb>D-ZQY`il;4lrmxWcf$>nhOIGzRSV9f99v`{4I$ ze_X#>tXOB}I80#6j`SWCs~hbO$}!#2Ro%7gr5+zPUR@T56OCeVze5UNa9q?Q@c8Y% zWYF09cFu<}VB?vK@@gV^CzH_9T-<}#3!9e)!8*UyfulA%^^Rc!9C@~1xuUlHDystd znkYXmUJgtzb(VvA_0W}qe0;7_lQ^m13iUc7{t5DY)%2o>i#x@*0(-T-r;2Af!A@F= zDFQ;f@y?uq=T2{#7D9%M*ym2xwQYD!t24w%E+7LhOXRot=I-MaBK+ldQJq(&f7 zoSq^v<;I@#G|0Xo=hSk=>$Tg&(W=OAu;puq%vhmFlJY_kxV6zlcjLf7=4-F@?k;c%BZx zd~~Lj46@)HnsfLXHaW^K38})WW0HIbM|{|d?Vv!%ZkugU5`0mmQ(KBcuC2IUTtP~{ zg;9s`a$V8hC+Xd!9%c&@`CObVjN5WNkTDy2+f_~4cZ7;|H*lLl{Jcx~Mpj%MF~vel zymNnJQ27V~Q7$SY@RfW$ZZtwm1PJ)nMt&&Mpq9Kz)>AAPZtNc=CJYT_JgK#WhSv3$J1RrDUB z`)7YVMW&}q9LKgl37xp@hRvURFP%br3u_p_14ZZ`Db$s^MXOZUAp-TP*-p3o)~SJF z{&fDDRJH$&CTZQ4%jhxI)8pBj6NAB^v7rE>lairs@8>yW;^E38*amN2yiv)IGRJUC zk$-#{;q~Fv3FyUs1L&c}qUFswb2EZSROd61Gy-dgLi`AhS{tzh!4S}5ol!0VnvDi% zD(CBDQTgda#BgPUF*TH4i+e(f=;43^&m~J)gfiZ1y?deBg@7AF@sEMA7mQR|WTJFn zVA^8E1&)A_=nS2~O_R~MDg%6L_j>`EibxZ~bWx9c) ztwg-XXzYwfyJvx?-WFb$NMiCkjF~z8dcNK5uI`_|WqyftQ1Z5y-*e3Qf=21Blq^6} zn67(W;r-cRf?%~}W8Ter#gZk1OdD9%N->FB%X$Pqbla;4@HshU=pHNYDO~=Lb<@9E zrPcC9T%d{)xbVS_FjUg>eWK4+4q>*H#4%m;HhI=*d&xu9H7{mpOZR4w$s6+Nt<6Xl z8UL9-R8P@H($#O3j%pTCqbJjl{ujm=!LU5;ddlkWD^o9$?z^hZGUPC|-S@OoMZ+aA z$Ggd%SS4eib)T;^4yovn8p*TvY3XE4)eeFxtNh*ODlBLV(e$|aD ziv95WKO*B3ko48btiB*&BT3J35ld$HrCfIRtaq=!M2)4Jrk>#XI77Ib2{-tlIpynV z#PwMu9@|kfHQ8WwALi-Sj(#zbM`LI1yfMUgVbsT5WTvGK_3mYnkLbq3NQ*}(i{ z_F9VyQnmgaYT(Vi4}LYG#>t38@3rkNu0V!6=kdRk%3ThhdV%ey`d|GNd1GxK^RC?{kqQ0O=6heyhkb|1!c zW%gzsk6z2M=|QsP)IOPd*Tp~pWJc!K{l!o0AY~B`?L5Yt2kbEi zi1kE^S*UtKjZ8Zt(CUncnG*Ir%G_vbQ8zqyC>vZ@BS_+Wr>>t9`FocnqXq`redw$W zdXQJdky>k+kV+%P(axrqIRY&ADIk!nr#8#>`Fb;w5!mw}?Zy7)G0TZd+t{Sp&71E~ zRay0UMepMIr3Im#kML?pl)?IZRJd0rvd#LS?^Dhm9v^s~gOe;;XH4JRk0Fk@nw2qYvz)4 zNs6!K#F(Vyl-aF?H1F`$Q^Wu>KgkAKwW;a-MyLw(AUP1GwGz~My`GUAPfw)I1i(wX z>7aYjq8heCN;d0X_R*QaYvC!2h%Kg8K%U}H?DO4<*?)ffcwg#c3)N+#5asp-|_cNk>7&NCJxp48O5xh~4Ke~78r(K?jf2vh?g$D%ha3yhu zMON{}DCwsuP5swZm@#GX4vYGYMR08qx${cE_`uwy1un;b%mK5F-rnGzaw-MuwNwGP zHyePV!nR4+!kEv}o=Pi`!Rtj8%S`dkd$}`K?B^vYN&RpK<2AEKdJ)sj9%Tjv=<|93 zK%>YNOU|zn!5F>9!~+Am*S#JnU(IJdA_{73X?Jh5MiwqBgQMLgYqqwvbNZtafB6BB z=Yl#?B(8B8M*!2!`L7IIB>AG^wk`{E!5SDjPxN+KoZ3)E%XQA`;oh*tmXp6!%}Va! z;)HerOgGRLbR5LWQp?7WHLGyuVnp)wDbH?@>7$z*0lS~cd&+&6!Ya&-bf_}9pEbW6 zb(rb;LT;I;m3E~$7SD5BGMG{QZUDJpd-^1&pUE7VV`T!7kYNW#R)wO#u9ttg2bFu? zZ{CmC{3e;TX~t!%b!188@=zP2yg6mp-+G!@96a86pX>g}DZZIdxJS|ew>c?rm4(&e9g%ysjg?s}Ju+=u-7wNaf?ybhh z3dMiqkmqE7Exs8%nLwfKLnpQk`v|*l zhVK1-_@uTM(mExB!d%Re1IueT{wywQHvQu}o3Zdm3-?+1I zgNmX`GTvE20%U9HH!!9bHH+gr!}h9WZmuOpT2XzC0r~2H`D9v=8IjB0{3MY=`|u*& z-DosWC0gX;ASWPe`BI$>`hghifOYxCTx}UwvTot=Lf1C8RW-oUXMyyKc6556@3x}l z^j&kD<`h5;xP4}b0!PrTaG2(Xm7ax7p^vR@cQADcaqK(!!0{pl{dtQk%CFk^+qCT4^eR#a|D;8nQ-vIBb7dZ_J52ISX~w;J(OA^#}#6 z?S#)QC(9u6evVA!lTHsclso5u3N+c^tI)Eg6S|$;T4uCvqEfzb&3DP5?gJOpt)`zo`NxxG&CnLQJi20UYhLvz*rl^OWa(BCuE=tyt@|tX#NqAyD{y zl^bZX5#ARfeya0dGW7@jw-MWN3{u=RuH=VL&%jT7TSywhl^!Y4(? zU-M!tCmBDH&-g%$fcuHCVa{b(jsX$AIKd*ULBR_b(|hV&~vr zT~fEfr}S30DE<0hN=VW>H38oFoMqQjG{<<%fXuc_#$XiC??&FztHcyExqTdSonEt) z6WS5~d6%b53gaKXfDgxvHlE(Tt{2<5xCN|C{%+19>P5q0$IZmH^YA72f#&R6Cs@~x zL%;0fiXEuNHY6_?cG=&qjEwcVq)H0eS&W}IV{w$e%gmV+3ZHEGU2};2;f!sbA$EG~ zDf@oxu|*)Gk45|TpxJDPe5T68JLtZ`a}nZ$SuQL5)69U50&APAF)}VWkap;k^Jn!g zD&(n^hYB(KpySZg#c%V;uee0@X}^P3w<84`*zYVS$Um$XnO|%Qtg=TNjs03i5Y>7@ z(M$}`Isp5`&Y`kCKKKz_u^o7oH}#Fn`G&g!A_4=xyFUDU{~yBbL;E&FBZ2+I0@mnL zv}Y4|C=q<+bcdyhQ9K7FZ~j5hZ76(5AC%4X;uqJ8-yGJ$D{ksqdOEgiCQCLu4v!b< z)tnIHlfZ3eCTX7r`wzU|ZL;NU(ufdV&FG`izNN6(o;X7%>`ob>^pYCOGcs_B0l4t6 zUEi-df!ec=Sj6{@nxK;otE2rDwC`%m7rrt(a216RJl7d?G+~BRn&NA5h6V@a1@V)0 zHzIp90@j!ldFV0=klRr0@1D&AI2>D(odP|sAu5jB*b-BwxP24lci73?m%HODRN2D8 z#gaVCxYx4I)bav8kB!MIwXcquI8~RJQ9tS!o8~y3IHJMdR5x6!KJ-Vxq6T!Urnw@^ zfbl}N3JB&l2X3mI+m9+ObM|u;N#p5kclQJdNi zN@GJ#R%IHAh2|ucMr$sAKThD;*oSqWY}Y!n#hz(7?Wh#O3g*c|-JHEdu5N&AtC~sp zkXbiWur}v6$+BbEdxSJ8XP48iEJh-y97$fs^1Wnrs9CV?f{(3IvkAQk)6Tq>=N~-s z^$j=>Cc2v_Y~0QFA9mHpm^e)<^pwa$?C!ZctDa?HUUvls$Lv5_`};-wvyJ)l4qGmH z3*73zr2;-_2YqA*xsoY?S#9%0#mQGMi`q%|P$CTb$-FHy13EU}MJWAQg#N9L{>+tc zm?r&_Z{s0+S)#DkhJ9?6M@ZWcdPg7rtSE&G*wI@k0Jd+-5{stlH|w=>&3bO#l%Pfa zX!G=qxw}feqdUt+q%3Q!C%!R3m&BRg(fsDby{IjLcx6S`O@y{oS{2SHgKvCEn9v4J z-ZI1`%D@$;YiDJ)J<~@uv(tM$i(6sR(4iZpNgle{2QkT8j$TR(l)t?8U7ZArA2-WK z8{MBx0g?75{IQ|`>1=H;iPz`ZVn-c@34KCWyqy2oT#elBAHWs ze5p9(O*@yBz5AzEFaJuMiH|&w5q+RpVtodyz;Nx~j&W!0GwJ*mY0LoagaA&?Mz|JJt3RoFgt~*-;dbvh6~lyvT>4Sl=nrJq zDWNh5xg>Ik>{srnBxCpn%67?nhkWry5%K!^HXNJ^$P_XjrH~Z;`!nG+c5(1SZ+Lp> za)(8`*tp4PCJfR#!182f-jd(eNJr3kQ6=VQ)M);7wzjQMoHMd3_P=KhC3tTD?I+&}y#VMIjgeu&l@f6>#ow4d)O z-{7jXHF;HZf4^E{`TduGuyLW%9xp7aq{kBD@l;Ns>NVhxB+Q-ZT>5xqtHknV>N6c( z2zFnAXIIR^*kfE$!u2OScEN4ppb&TMXm?G+{a#33sehBEr)vbUj(%OEHENUyp_KL< ze@^oIqERm=@dP9ykUBmNQ_th|vP~kPd9qVgYesDH!iKU7eR|asGG>M?_~?i&m8jmUn~~5#&z1pS_oG z-rk{Iqp6b_ToB;xX`Ythv(kK|(X2aoMU8(lCC`;p$9FF%;F#4&w(9g5=)vuDV(4af z$^$qzJG7wk?B~y^Or07A01;Af;%xP8@vJ3)jNBbPBh!)S*K_JU9p@Afuj*VFbU4UT z;fxyC5*M!8HA1^x!BP?HBPePe)DBSL1IDTMOqyKx^ljE`${er z?B0I+Sst7i!*~?bAo3P=t|0VYx!(xkkha*!IOE|T`j2b))2oXy6A`V!RlJ;yTu0!l zZ_EMovdjV^mj;e~VHCD&SK-M&o$r?;glA+$-sCVJ!GrFXc1KO#=@-76?Udj%t+)62 zKzq(QIp^Z6$OcLJD`)n!142q!9U#QmYtTu9s*eAV7X^!}l+9}2p~PhGhUmS0$(ZS7 zaf_Ol`GWqS1H-~7z;*_J0EyH3RrY>U<=4AkEM?r00spfa?s|1+?@mT;|2@-1XQSeZ zNQ~q}aNe%HcV9Tg+v^IhyH7&0%+HA23tzJAW(Dm6E-QZh)4NtPqS_f$Pi;Dq zpu5LO@vi}((?=>nX}WiiGO^IPrz#PRlg}p z_FngDi`4U(X>rV-)HaW8m-78V8{k7R@ z-ZNYMK4>ox+_6xuM2zvY1>q^e!Y zZum5D2HF#`3~@IsX~tm*xZ)nOXQnx2KvJhIF7nY7?3`?JNWy+rDaPRZQLR9Ci8O!~ zV{_>jrnkqS?o!H<&d~;!+K6&H-KdWz7w2QG!GXW0%-(e+Z9PTiNhS%}SPw^RO9eSm z&$CNSn``vZPs9Qn#w%=tqZuL0@eQSLZQd8=b0l0%`z$nZxn`NWlg@LZ>V7!}>|aW^ z>qRGjs#X$;soUu*4gIDmgvm=Ist0YoM5YT=2w~BN%WQd+x(u{(czo8f3l^7yk?SgX z&(BB~U&=y>E~>PFBTpRYy!D(E%!>Ss7jaqPA59sP6TVO9M^Zc7+{5BJ8Fi zmHRzT^I}niwujVtz+YDw19%j?ZFHbM8{mD!i!{@4=XPlvvBYA>yD!l+=Jv2U^#Ose zPc3}oD*G=TFfKJh{B%*Q=361s3GqkWsJY#iIAOmXu5LXO>cF1&hgRN^&@6Tb`UtS)&m(qY)SykX7C9L?zUJ76lWaP>HQ??6}9eveSqsFT~B%lZ(fu8s}` zUhSM%k#Fd30M;1F+goil^q!4_Nk_N^ndl8v7o|%>U3L&j8Lz7alLKs3W52=g3KW`7BYAxNlb))t8rIxm2$I77DOulJwJts#vls;d}rz=Q$Z^i?~)S1aAZ z*D`k4)4OB$t`|@E^38!H%+6yABgFS1oEIeq*lGVT#@TWmC(EJX3t}J^Cl7B*n#eq+ z0hx#|oT~$gAP?=ME1bkg9Y7OVS%i7xB}4y^iAYp7&!!2fx0MZK6-radYw-&jLKrqT z8}3#oCw3oi>2eAX^-Xeg8vkyeM4VG!(_dMPXeOpe9dJKwmYaBmcN)JZv9l?W>FM>g z%FuQBSfofBuz>sZ^~m} zv4s_7LY(0Ml>}ku8elNYETWOBP zUOAO_ewbkJ*`IrI6^ARVH6umk0rV<|L*zZhy2|w>sG>FLUL8H|L0VN%+B)a&jqbED zb)Bq9v^q|P@QcP^MDM_j=&(I!clDnC^L)-7oBMf<8?N^Vp$m?@j!OO5gP^A2@q;!K zOP#ntf6>B6=s@Ls!MhG!$@XnJiCoEqH+;2s-i5~9)#4}&T+W@BS%~=qPag2`1odL# zFA^QfH%SD!_1*RFu@&%-GF*W3_OwYyv%I#ZeLCx97^40idsi zMfzE~nR7x5L}e$)M}E3~(7DcI-{BnVO?dAdpX>9Hmuqegu=*+MI?J4#_SSVt!+K?z zg6%`uK!6(dr^{{M{;YvQRnuxx^C>%!1Cw=q7*di?-yPQOOy0f~MKe{6gyb8mjJg@; zJHf0f`*=jRGTAJ|OU{rVvns`z`TD8Q-ls)X2R0Cw?1{tU>zLbo(WGvAj&piEw>qrU zqQtcCMclvy^T}8WpL6^0ziWQt#Kblbp0Q9&jQP~`b@_V{=W1;_{97Mz?OL>jFJ@G; zbbe}zb+?2G+28nbf!oA;?POfY4rTZ9GCyaTW~QHbYa>&qGNld;E)I;C{Kkz>HY%CW z9qhj(w6dk?%wL65Bi^4vF;-V?yzEH>YxT!ETjV;TnfwtCFq+E{X9mk}%LCRLnf~0n zH#V?}VtSQ7y?SMi-or=Q5BkF;yPJQVQSUzt^R;0Jn!eF{8)&?7t$}T|7|Pdh>oaE5 z9d|Qpq&iai--wKUa{962RJOu9HKx;_Qd2ycJ#wsiNNOMAa|tz^^sX+UV6pNNt=e3e z%Dz`E-Z?+&0J-g+??_SAF_^YOTIrMu?&D#3s+^EQO#yEc}bp2^S zF*+g?a`eWAXPEqC)0vJ#BEtOJi8d-w%l3*_ar8i*duXB&DvJ6@3h-%~bT%g?JflZu5E8<1eNz7(F zU)GAU0rnhkXFYU0@0~2lwr{bIkCdnL^DM22!=bjq_;k|Sq@-H58xhd`|55hdVNI-G zyRc=eKS5<{M5HT9Z_+`Ug`!lYBUP%DfP_wfv24Xq4IM&JkS-uKgs!yERXPboDFQ+g zdVrAR4C;Q*`@P>E=bZTm8L#9TX69MXTKBr|dr7@+YoW;yqb!4)eZLH17QqeSLCxq% zesx!#uMf&Oy_3(Pq4XBv2SfPJ#}1|P{i-O!XjbcJq+@B9bjTE2%WGhQu)}8OcSkH+ z9X;9pd|?1#t)e#8j{N=*8KrSWK32oz8iUNnduAXH<6Wnk-nY2(b%P!lfwz=(>fP>) z2W>o*0thE0f1btgCwb_Ht}F&h(KR%;~CezV7s*S~mlbuFudY7sVDMkT^qT$By7YB$ah*#FqJ z-W1xEnO)61N#FLdA7gfCx{aI_2qz6@Log-e`u=tWlWst5GI0Hk&7!g}3=ux4R1OMl z*JE!&lppT8n)a`AG|*Cb&UqJ(m=jRj=7EP2YVS_)YHCBxuBdxnf7|JH;RQ&JIJ1V>? zrHoQ%F^<6%a$xEb@2$f>1Gy86el#Uf^%=zhfxwKN!N4eL3sHF5JGbk+Y+0={?Bmxa z`^NsjMz8>trx2`~*dnKxy~W*~#7y3e1FqBFtr^FsQIFk|;8~rRrMpGQ4S(TD~M-DMa24LEw57J^9_z!TC5EjBvI zDzZcM+H?R+%XD~yaE<**Gtc2ENph+qh`NY<=~$6MMV83ZXde6r!uZ&=QEg!=ok6CuP@?#?4CaqO*XjamB0^|bVGe>jq?TZVEVH)2Af5*+`(CI-6t_&^#;!JKdNXA-hS_0VAJ&@ zSgQ9epi6(#yVYTr=P_3S4s0}wZ1@t#iW4X2!joNnXTMg{iRw?oQ*qJ(o>gn zn3t5)74l)Ked;+3h!QQfY7dmdi?e*_;V~?%G0CHPF0Wr_vmIFOW&cD%;_)l?zY9@!Uu}IOJ||m3KP73 zYW%S2bASMIkMk0uP1Q!Atk6OTR^K`O+0IrJSCcdSBf}{LTcl=HnNyN$_pV1Id7}A! z%7S&obM+$`&$o910NlVo%VCB>dBb?t+F=8~%nbgD`?!j)B-x$q*JXNlAK&FH4{Fp9 zBTrOywfFbKrcQJ6h!rwFX!TrBLn=KmpH!EFp#hFyUl99CNP#qC=hg03+fF>_wk^#H zbE3_@gWvSZjW@i-Ka8y|JGK!>^V@o=Or!-3l^28>-ScR&$qRC5{Ol$1WIi^K0Ri8~ zHsb)y-T~|#X(Q3K;>l3)D-a?Sc8164{e;V&Ao9>ioV4t~fG1z1uSm(QH)Zx~w)z27 z&-+&#FDD2RtwzK`A6R-28F6=fo_?VOb{D2{1jns#6r67hqL%riiJA2+Rxuf5Va|et zUCs}w%_s1V+^Pn7b4rFn@GB^xsB7I}1~>veVez?T;K6=4G!;ndGeDXC;16+`ca^_p z{1|i{0O9b1^y2*wx={UXeUdPct+y?+=Q!!-3Evc6cqVTX_RYY%K|f|cbt&@#d~-&_ z<8xf37i~ASl8v)|*2%LyA`6vID<>p%?g=i3xh}qHT1#kKstY!Y#e-{=Y3( zs!WAPI3BNsZ}Rz|IoKh`{Ih(=wTE`mmgw}IXx@nx*H7RkKSX%~2i_}%@fs}bq-*f@ z)gMHQERfLQ?STJ|Ijm7MtgwB4%T@l+9OoY|1g5Nou0BL#z$opBb)kwO0#3xPciAzH zJqlOstWHBXCJ){Yn*TYYddMEeMAYIu^yFWOx_$B0;qkABr$pMKs!6hQ_X+BQk(L7Y zK3oK<4hq|QbD&`G#>ZD3GXmk?o}K{TU{$!DwR)h}H~IQA&F4~euY05FKuZh^Cu%Ic z&aXol8P)i@nz?!rz-@63^L!iltS(RG+{*T{Tn?yu5WQ)QF6Yp6%tn5Ct%@9l7v$^Jw{9UBAaR(D zlG+zp6$Ob#4<rT-jzLOJ{(W>@|RmyqP6A z;WE$8Y5sEhic!H&qEg&TC-p9s*@qp%yUnY$zAMWry-BQm3U7E6)+!=+*f2Y`%4U8O zI!NC#WqYjjiE*T30oJLCDIRi#&_SfZjS zVvB5MBA~a$(^kM`D)NZqg@bR6ya>KhLKjtm$>ga=aovP>22DFT2<7~Tgx-h{;wMQb zuegQW$_aDZoO-+gSVs|Ko`P$u)N!3Re7F82TlVXDnKKasC{Wi-1_e5$8+r_K$+^Ez zztHE>-P3`++DiZWo=q1s9faF--@$UP`wK!FitW+_VJXcIb;HGINg+sTscDq{KA_B4 z=Y2{Xp8m@16CBQ$B`ucyej~;7KeZ3r3iLy}h~}^G5W~r!)eQU(xst{_j?takpO%zQ z5_Vomg0UxB1{hYQfj;F07yoHtiVWmDUE-9VJJb#;c>gkCJ~O#C${9Z|7EjAci@Ys* zgbMRqI%8;+(>%LoauM@=hFRn1<36BS8z_!V4HBxj_a0pbMIw4RCC_8hSD#GT=Ge{k zR8NJkSMVU0C98Zo-pV6=%_ayv)D+%x7-|yt(J+xJ3dkj{dTK?o&ougZS#Dc+pDE8o z_c+SURA)-rHux`lk!vmo8qzm)zvB*wB?+TdZYFR9KT}6VVL?6;Z~=7E@>yfYwEupP z|7GA|--BU$jeb;$7S037YXw^#BdJP5CSVoTUp)_f!!8;Yui9HmNw7W7j1Y0g~r4NsXR73omF`nG$G3XJdC_<3+HVn zjim*37qMxS?J`;Yd}C-M00Xzt_I6j*dBsGdpFbsVwQU8UNH%ve`T65&vD0f$IX$~0 zl}Xm1Nc+1M?nmT11UzA#Ubk0#Ea2_F9Fn;oqwkzUX?JwMRJvP!tG{l-ma3ao?JQF7 z=!0Ay+rC_?(E@(Ux*-6xz}>V#xDwyk_cL`d}3CaZL(`tg`Bj~oh=R~pTz*2YN5{&{)!tdG6tmDg{_XG zjH@sf-rCvi;@tph6w3SDvJJdQeEGiJ194czJ9M>HJxshv$Lz%XGh>!cJNdO;-=or$ zZ_ocrD$I2JKt=ywtoDPocH{p4bmju1?ZJP5$1nZKd48f|+89SypB6XIc~Bn_n9lMp zCVLl-)V)qE@UuH;_jpBX>4#meMn>GM(OF$xXSy|l;yO#OG zJGXivS3^v_VIf-uiZPz!*;!}-Mwx!!h(q_T{cpy`pHqj<=Y8$={(Yweh}a8OeD?Z5D}xNPI%3(_s&4mdYVB_6xA@u#IL|x7P}890 z%h$h=uh3j~>#qxqve=eh=uNTn8iNLY-Lf1Kgv_5L88WmolXMr2QHBqPX_ z7C}{hrQ{^jS0;AapW3z0okZeF18`eCpf}bM`V4BPc+!p)6IjoZzVk89% zgAuH3u`&##<#C&L&Ia*0su32T^53ciKJZq`g*Y1P{fbHE&2et^efd%{sMv!|=_1RY zRd54TiNt`-PY^gG4>gf5ptDx~OXLB7+#%nNfKvYy`0R}@-3!4-jV@+#yHtVUl+rXI z-3>i>`mKq7@UcUGJ9(%D6xU1_Yk4K7_PrQ+AP0LI&@g&Efm6aIp)o_@((M|ZqA3#f zms)i__BRjkiDhrtjBTI%H^qVaP_&1@N9Sq_NaH+&9MM8_Q4 zUK$oeCm9v5)N}qxo4i2Rz7s!^M|or`nl-}riQylUmBYWQvmvYl*Ml2Yk7D%60q(K9 z=K+j&M(|YoiHHQgz|GT~<%}Ic4N@4e=>^HAewE>)q}TU(`F=M-mNZCs;$Q{xKBimR z&ZxGJ^N$RJ!f)HyHtWoZEU%EQubT+MOx0x=H5eGY_Yj3eb~YQuVDXub&G7JeyA7+Se5v3GzPL9wpcpgld{XXw*Jb^?LPa z`_>oO1iCrA+r*@_=TTF_-Ca(KW9eYp&Xz)pH?Mo?5q(JopO#lVxI!;sy-1c*qHj z`f|aE@5*n}W8v@A>%8F^5P{gTop(LMFYYUwWhn~{4%#=4wVOV0ZPW)}h7!{RROy@4 zyq{=s5D-o;T?I1G)Niy9hQwAb44g;F`Zu!D9`tOi2!Mo{b#0h zis0N`-H%#G`m89>+c(jOMR@lgDyZ&Q)@uy5XX;du(bGGWEoa=;blCxHW|&8E=7M0i z=M80nN+|f{hf>adXn5 z?Dvnym^Iuxij0NP-w6@w=9BioV0ZDzdL4O7tjl5BA011)$jPYXD!9F{vSaqv{Xhze zp70^r%?k{vocO`5jeI3GX0)+JN#EJ1_%>F=)^Zg#NUp93+%I*D&%2{`uEyEp43*Q& zk<4ndIoY6D44b!w6eusmJP1ERLA~%;9m!dl1H)& z7>y^-5Q2)ivE?TTr+%3b4?X?u&e?aJq&Z&5QIu4cXasgs#+|YrNID(v!l}mOozLm9 z1%kZ%buYiOY8i!Q2mUNb#dQ|dU9;VUsUFWyWeB|V0`WinN4jdUv(@gUjykbTCsjvC zvD8AN;!#?`KOF2`hzXV04XN5nT6A~LRPZj3&}c;h^9wpn0Y`PrM>B(0e6i!VYRTnP zX-x}h=%C47hXHp|61=HWy$6V!Tqzs++3bRk;#%6C|ACJ68gn$R9in@biY6L`H3QS% z6$kQtY|WQRfMew5;#z=cUH}F%Le|$Xz`uRgVRthAI;2IK^19sSs6gzgawI2{>2cDPWKWs3spWl^s$d}dbmPlw zou4t6At?h%82*7wfl+c*iyRk;xmQ4&dKArzIE~uDNC$gzXAz$5w|uDWSZPKLoW?@S z*0$2L%|Fq9tnHz+HApt!dsTJsBcu3<-j-hq3*;Pu@a=8&K6g2HtGC5dZm-LkCZGXj z@k#GF_Mi=CxGXNgL^*9`*X%t%kDYJmUwxaf9&~)J0)E;*DE9JIIQ>RZ*r3Mk0K#;I zLM_!v9UpWvVZnc$(j5COr6{P|7Q&MKI?MznA8gwWltb^o^=wTSNLl?=0S$CwneqD%LW z+1DS#x4zbiGAh>eP-RtrP1dOxypTjYMlfOHKZ^d2VZWR(E5{~QJ!&Q3d(+?mC2C$p zx=$5?8+y_N?{!vE0H~>xX}+X10ybg-B!T=8#$&7UnlkMAv&Y5E?;7$eOGy{U%cMnR zzH=YN<+~q`5LQXPAlJ=YIP23DR;wU0QjAm^@H2~Knzy|K$}voIN>KkA9gMxxIBh*toEdDwh_f{(UNQ{hJDHS^uWHGDG=8n`1k-oCwkdALyv$IU9$;1 zyE`=N5of`jVrIcKYn5#sf3<=YfkTvMv!s*NCAYw(;X(WSSLdSNI>mgtm$i;IlrLb> zc{>I#iD_uvQ%aZ}z>TK^&o~9v*HvHVaBC_BNYC?(-EIa9gz`F@zPtRs?jo<$J3@J+ z{%w` zSrH)&S971LrQ)ilZPXV}QW4uz8oTK1F-`&NqR!Pvyn*mZi?=Hmw|TWq^!r`)@&){- zVX{E&GbbwhYzmotxI9fU0QM>D6HvuL<*s#ZOgrN8nPOKPcFwP@z1~kDZoWdjQ#otN z%lsyw4T5;~E<^v#YQHJB{y^uHFu`+e#6W=BcjD{Ywsi$a*r#Ovy2YGc4H6M;2f{Z> z0h9Eqra{D3Y-fl*o*=FZ+{s9X7a<6bLo`T7W5z%W7~~L*9M)$r4uS7lB9+%VQ3B1o z>)+g{y=Hj6WXYD|$<7qJ%P{5;zS7-|O?Yx|U=V{nQ;8ZkeAA1+}oD|ft^rg`PpZYSk=@zt$UL|(!9wq9)22@ zDGrJbM7H$ht-fX?ym2F-qspjIVy23T8&>7aNqo-Ga04tmHi&HL>ssdNpo-P_Q`{JkFFIgQpYk3e?Q3>d`hZDsiv3VQECDuI+)Mby_!2pTYsdD?%>=!m^u zaO@G!!TL1`EJh|56z;>16+G!Q6UdfYnwy8W=B9QIEP7r^qf3sgaO$&9Uy}lin;FtS zrtKG7?hAl`PTzqRjx00$l}_0U-xr+Yot*EY%(SlN07Hjg&Ljc>?4 zhYHJW&)n4NT2b<19K*y|+79>m0g#`Dl$^JWPnxy36Czr~(`*|v2HaX`j+bVo z|8tlvO4KB4uda31dz765bi?9I7U0DaSi^!_7p>TVeElC=Lp!Q(OnhQ95ykg(h+wn6 z|69YO{7{DTReP=cXs=YE*L18$1JjN&2Y7KZTz~jZ-Ib+Xs`G5U_yQpsTc`ByVFF_| zjBf;zM(Sss_HXDCEqVQn@#%&{oWdNwkC=we=wQj9QNZ9X4SvCq~l!xQnsNar%=G3naM-RQf!d!YbJU8MVp-cu$yqvW^! znR)in?AtVjvhL|?_YcwT3lT*Me+TI^zYM^mA6zV!^$&=<8lpk$%d~@cwzKRHkqtxa zfZyQGB%ym6cm7QHTe54koCl{-=d6<7s-Qrb9TKFqKSCh53vA{lOQxX?j1xN*x!Nmh zi)X0mXMl!)5Xyz|&PRoJ>22fVSGP{U!MJgD5Fzk%y?xAee?2q;Q7T}MOWf--2+VWIsv4fpZs zO;9q9C8z>*I5kCZ)WUj*g|>>6#H7a%v5C!*io1 zW+y-(p0CDrFss$EDR?>*VmGvN7GxB>RMmw=pAMO^@GQV~@Q!=T0Io?~)HbNA9=>d0 zLSy3rVJ#?7oHE5$#h;I^?UgdcJ+44dGZXk0PD#l6ydYCn9;1lM^WUYBR;Fp?mh(?@ z5{N9*bY^2?(=7XxctcAU*kQgQK^_`g!ZVlL|I8^G-16WyUVpAM#QdT(q3#XHifIyU z6Kw6u%vX&FC_+X7!M4juMoW{5nM2_P9EOmkax?xx_+>*zHd{iGc}YzcLgwiCPvhx* zuF*!v)>ZF0Il5ZOD-_ptDO_*(8p7{=t>{2yS!3Wo+p0&WZ%{ruxuy#JLl;U6V4eJ( zWc*QeUrS)^8NxV9Okxb>^3UH(zp~gB&Io}3sxA!8XgxBi=SR*nP_pelCp^<{EK3VC z_)G75HYwDsu);XOa31oWF7;XUW0b0+gwrwA?=dGQ6nFLKtS?8GcH7p8C6b?Zry?8$ z9b;$PL2L}nwLw(*=kWLhr^xU)CsSg&B>{MP1SII`6zN+g)3JfTd5s39)r0i|9I86> z+tFS~R)%y0q}71zc`tt8oA>>X>OE8dPd4!V-VS`Rr~Q?!GthBf5hO1N_JJDn+ImA| ztiimxv*D^if=v%)u5q+p{@b&46V*~)r%yw4>iHSt8oFQbVps&YrF89v`EdT7>lpTf4}N-o_JM~JaO)Vd0G8UVD5q`uJ$TbZO&~r(AijH z_sA}4(a05pFtioxbrv(97YI04IoGviT6?vNU0nBkAMv`=j5Fx%ZT-G-T7G^P#vj~c zzatXeuENjXes5{DbYZF2!f4yqv2dvU1j`f&N3rjLf-PF*lQpOWx~wKb6C*G`bfMPSdf96`SZ@6;gBgw~-6nD9s;8{kD@ z)Q}YmYa3%pw4J-JL3y8PGHyBep{X!TR8sXJFq4~tr7X8T#7I1OueX_F)4W>{Wm}#& zdHzEgQ%?5C67mX&qiA?A${ZYiQqaiw)I-sEW@^ zgD=s$#0Le^4LcmBR}|8#-AWt4!effw4az1W8IA9*XR`{^!-@wNqJ^Q-hv`1j^K3FT zqpYOyb$YpT#@PF@2f#0%jw_C`2~pEBYkYW59;@K{8jh|;N7NjB{eAB?cZTW3(E4yH zA1&EkZUcICHpjNPK{D_Y%O}4FKRG=s8teLQaFUoUetG2B8!7*qqxlJ06!7@ zMrngwMkQJj*8=kDC$1Xq8+L0O7;cTQuYsIC?69T#@7(o%w#(`-1Q*)Q|kTB^;Qy*G5j6;5Me}*o!YVgL%iH z*H?ZUR9%7$!PZ1ahNflqyF3}5kRl1X;WnZR8aumKv_tRQGN+Wjq2Mw}4n-^9X%*wyZT>(6TFj#rnA`t)H)-25$o`9(ABK8t zSEUaM**Vw;lSpv4%vnX83$EXALg;fv(y|)(Y+KkjXbV+ss$(Z9-dqeyxQR!T{}G({ zFdcih!3Fm#Fs_#zm}>%0^u&Z}7^R_us=R+=Q0k9M5DnW~sJexb@QP{9E<2gEP+C0J zpO^juK{?$M*Et>REI?WWElLATt?g&L_qz=>NM9Vy({?`Zwm>l6z}}et{wGd`&D=oa zl>N-l5zsm<&=P~FAqUntW!os21oyR{W;T1ZwOM&=Tkhm86}SbAcRnOPGb;9OmGTL_ zBmtwsp)GwL9uQ367y-(b5snlkS5ei!)LH+SB+3V>Z)SMvGR|_6btfH5<69y$mWfBL z28yFJ%g|H_NO_@M@iR@6e4fy2JBKd|?rIB6Z);k*Y%$}SxpwMfgYfN?slaUPZpw2p zn;CZFC@wo-l*g_z-_WVKYDDcnG$X=Uq}Aw#tAKeYq{Kz znQ{w>xkudY2$8GJZ-S+u!B%qAZmn+j#7({aO)dj!<~?JXu#jSATyWT+QY>WX0X5FM ziK(vW;WHyuH`GWzhpZ-5v=WB$Nvml-~SFo3H1)iVB#T0Mlhy$MX!4=XN zT~s}Z{Qe&1xZhjfjG{pvNATVxkRg89uzVq%!4+J<*v=jtbqjI8gKim%aLJKAi5II` z8+PrJilz&;fJf%f{ERnIE=4XUEfjhsLk71il0-Wc1M)wm4&EcId+yLVH!{D`OFxKr z)Xyd@34yP$YXj5wU%6-1q2IYDR-?F}dCavbX$>vKDN(sgbq~Ao-o(XFWsmA~i&h4< zD{7{RyM#`Jr(plKN6n~s7Q;F83oa9S#T3Y-6iE!dmZN+sXYlndDy^cu>%o$m4CK&^ z3Kc+7;Rh9D(NE)vjPLYB4xGAjB}i_(q_FGcQ5#okE!Jgcr{(EQwGya%}tOa%Q3G4VFz8OQ_Wko>HlHW(Y`)s$+wo| z*h&f~zQP545kX!bA9b8z<$um+zOklWOJ&-vbz{-)aHyQq7Q0}6GbfAK9$#7ea%EZd zDNbmuaV7tU7BUL&eZqboJqMrbsP#@AKiN&lLy;;MzFGnjaRq%RQ9CBX>J}?IQKx#> z{?3Lc9C3jJGSR&F3+xH+1@3cR|Msjq8W0%+x4>rD(Sv2nUTQ%NI=<0rrx@;-BR3hoFBg3w1LQOT3f(jL|H;XIf5&@s|LsG|MM*ra{ zVV57N!UO(mEi2=X0u19dGc zakEu!lXE8g*9A6b!&;3KQ8K|mX#_)sU%*%D`V$7691Ot$!D)_%#ZUm`cLFT&;Gw;d{&(FJ-A06 zc$*jX-ir?Q9(Y4;Iq4H8IV;T#=5ohAmxcZYZhqx*VCly0lYI!-va`o!5UU3cT>6H+ z0rFB44^gjqEhA+cY8FV!Fzr#v*f$whWa+RK?u_VMal zx4Tzzj~zw;Twh4b=yZaA!2F$ouGrDZHHKtFSHrZq*5w3-e-YA>)ri*rLAd|@UozjR z-qN*2=bd3+xg6haYfn!mk^huD27V`IOjZ_nE4e zP#Y3i!|_|dg+)<)E_R-QAO^5U<4xT;s$eY%Tia_2K)J`@?KIQCS%5ir=X7l06YZdB z_kz4+ucb3xc3n)&6UQhsV74J6arf1@rx2eNm1|C^#DL?&$nUsZuc7vmWgPRrV0D{5XNLgxaJ&vM^epu zCZc(tc8Zc|JNHaUz$^MV!;3p+l4Es%{tCPPy^AfbJb^enH?13!pz&hf5AZQ&?d=JD{H#ALG6VS>`M7~bo z{KQ)$yGB!Y2jekskmaA*$QutLWG|9Z;}uSr6iV4e>l7_X8h*IF`!iiMZxTN_QwpND zcWwzAx2O~w4Ssq%JZuxBzjflHS7{BVyUiy9E;BwL!FtWPzUQX!5lRzUznflq?+Oj1 zG~%5crs1TR%i^FVxu;M0ppe zXHtkHXZXt$9-v8$;lC=l9|x^+@UZmZa*crB-?*H#$LA~$7_Pa7Ncjp(<{1%WrQYs+ z2fgvC54LM2dOh}_Q{HmEuNKQe+sd#2-qpIeNkb(W^)ywus2B%-_usq~1H5;U z!~KI}(4!vY!q)#P!L_goPhJbTFOtx|BC<)z0JBp(v*%4`W@&M1Jg8Z9mbtD@deI9C zoT#Ee0$sllnHIEfI1G2?!Y=+bf4d^ObjhgCQINUR-3jc%^@P{vfuJAih!x&(Biy{^ zQK^D*>=iFQvHzY4$Z-DVIym(=y|F3KaygoM=j01eHk&^Y`m{exT5+WREQG!WDir@w z7QIz0ExfdHPiJI)<{1(=7!T_k?H=j<&PTFOe`MQe3VXJ9{)HmQ<}N}EeyX_AW+}Hm zK#O)Wf>5LH)|Ho_S`$}LCO-~^exsfwzb@?+|ptJmDx`iN1REEKDYMtx1HwaF0BEOo5>byqgXKCcJojn?u z7#X<&n>U=4K4UuIfLtzV`rQ89lny&tk@z;dB^p~CWsa(QTdOtaS2)n#r0bc@rFww{ zzg79_y=28>{l^omlH^n)ogx&cd3xPaS%YTT6`nz-1jiuV$BQN9kY9Y&9Rg#^PJ_hU zLOCHYz0OuU*Z1)@YO4Y^7$MDT`fo<*W?AOTG>86+-Dzt{lHb-xF$AWKo>o>ejRx5d!PwO{C!JPp}Y zSBQ48Q}!wOaVax%25Dl|@6zAcukCY`A>_G{3@T~HIpzv=6X!iSZtAE zxM542!4|2hPdLTGiv(8GUlA6n>^-=+o{V`A+Fph_kxgezsI@wz!lWOifUgwEAInV4BUXDQJ9OYwUw{@Hex{K)3eEFR7HHrOAVfy&8^i>>2aCU4f zyK3HtEM%Ff2~brTe>1B+0lT#op+ldoA?oqg!Z38HWkSf6W+Ja=HAJMT!Z2&B5)(jm zk6VcVmY@o2s}FZom5^&c7I=Y}`#5co+iyd@XncPH^QBN#up$-eDtlFzy(@m&5gWQ1 zj4r=EmkvXH?CI%kU;ox>Z*MiT=I1yxBsIN}l-~lx%gsL}78fasCBp}B{&qM3fwf5u z8fbreFf}FEa=e{CWl9`o+M9wK<5m{u{QTMC{8s*9d-*S5nz#O}?+h5H<{NAdMJ8B9 z+Lv(bUTFqZARx=?B$n*}mS{Ek4*JgJsQjL*<_5;j;tInLMzx1-s1XAg>TeT)dIdtv z;q@24GAX@df2@34Z+j3y^8`#3Ymb_<^Z~JFYS&BfNe)_9xTfDHLpU|z?^O1kU#YI& z8x`%d4|Ul4c-q**9w*s}^&U~|O-Ak5+MWLAd#$V9ig?%CM6vF(h@IYB?@IEJ24em4 z(&E{d#&ic*aE@a@ZGV9QwZ3v&4+^nT@uhD@E{alt3H5p-E4k|L549feHu$B*5 zDBRL({4whp)=i!j2+RhDS79VR1qsuU|#<|(R5|Du+k;Qn4C3>lP&ZzJ0)A$1C zP0zBl1_5od%u7F0gox84d25>lFRWZ#Xg*=0$K62;WyWe1$*e zN>sB2s76RD!skabv^-)~bf{#3c+{15!GvAHvoGH$nL+BiI(>`Vy8_$I07&Wmc!KvG z_*j7R*C@~QUW0bc2xjR+>vuhDzAG9ytXmy%x$x`x`Tcj_z(eO>fE7JAo8y5ZLGO^` z?=^Aq&&Gb}okiq4RHx{A{>KxCE|vd&Jc*(QLkJEIp{ud8vhxifc4ja33$eP!4Kgx- z*7yDlGR?eV|7pW$dJy4biYBQD#CX)!`yBZwTY`gWL7LrT8Uw+*91uxbE0hnt*aL%T zw=HOras$JM+X-$dRAuFz_Xsv+&=JV|;jGhb40a^>AARflXQh0)mIT{Fddb#F(jak` zu&#Gwc#Ht{Zh4kve8kowu{JOgpbu$zW6OroEpNuRr;ON&gCMHwjVzJfm)NjX^V;;f zdeG@;FC;FtuDHHOAZI-&M`XN9{jI=UInPcOlwsxVx__Nq5A4{{uRLz5++{<|kIxdM zeC9K>kR_X3_T-Q95M|ZMc8}ZR;Zw`$%5(B6&wpxo=V49F{g3h9>&pTfC0C|IjffTf z0=DxRte9Wnqg+|EE8H4)v>vXRgjM=kjgZU&qEu`5m^(rc?^ItFt0_6IwSKBkB=TOF zYF!h|4Nyjob09;$t=Rse9v$3mrauYE!N0-d|4%`#2K9&pCKuqZQqXbRB&uQj=P!2T z$<$X=QiEaWVf>i@E3u+ zmFHVOD_Lnd^f!V%CX(!5%?7IlD*dr9JrRQzUnxptVcoweNv1(&HK?HJCt`A|qaO?O ztS9|$+E)ScWzB|S5$P&PZ$hE5DdRLs_7ERBpbQ$=;C=yvU47gW#w(&Mk-Tg^ZuIwQ zIbBT>xp`L{!uam0UXqUk$MfoiGH4(TT-GU0HQ6&kQcvcu#ET_TgWFCBbF#cH3wGmr zHOxDG>=+~F?>Ke)nQU5{4BuGM0%)3VRo12+3vf9TZud8j^)oV2;FOHzTE8v?ME&pO zsamHR1l8YS?hKh*)@N~egD*u09X4z&+}qga0oE$ZpKAD0R>)F5aq+Z~Y(-CW`FvRM z@@SaW8PLJy*Kx!4|7Hpv@MrV-{h9wU1@HIV=Fu9w!^~7E*uw|?M~mW z_go_#Vz?6uA<6=(BPwxi;D|d*=**R)VGd1?WaHQ9jdX`)yR@AsyK(8a(8T_hJ7O#c zp{^dFv!V3&Ux+hAA2&UZQxQte)WNYN4Lm{k2VpGAD+eTi<$F{A#0qB43Z8S=O(Ty} zzH1_QUs3TSINM>S)_~Sf+Ja^|ES> z;au7KsxT#dV3JB}p&p8wD8^ZVCBn!l&mc&hGv+m0TE54tjrA1w-Ln&5C$_Sa!q4 zkU+Ta7j`7Pk!YtET4`>u1gzAg=ruYmy}_2+QXfJBL)}`incnNk%R#1dTuq)$`;`gK8vdTKi2y*hnFWB! zb1wbO@CR@E?#KVb_*$?F6^cHrlyfjag(DN*^vB!y+SzOxUi}qI*$Mg*rD|HM4R7jz zSsO#?g=V@=hURq|tnpsuhw=P30RSwZN)`)=>rF$rHB2*66r zY!=*_JptLDOSse(oYvGs+>&hJjb_K!Hco6`V>rpp`&3=Zb#E%I>$Su}N_cdK(Mug+ zR>_KanPDEQ&<1gbv`AxUNSw3vbSR+Df_|Z1y4fbDu;&C+zi$W_oZ5Po zA*4U6tR5CCo-IATQIF+T4#c(ts|xBp`=|d6`>7ZHU&q!0hu#SNx4E?|Qd~P9{ntO= zhX5O`8+;T@9Nux}H)P2=*$7lmIKq#59PykMX4~{v_PC*W_D)@BSXEL3WPcqIuV7xkH{T3)e3PjnZ0U+VA57kVBO;hg4Dhd+Zi!#7GB2sCz4z$|2N$HIRXCo6K(!?{Lw7(10{b?@Ovu@bW1S?Zk7Q?eU|WvcILtD z$SwLvshT{U>V@f;+(wEpg##QP7<3Kw3^DaM$F#2F@k zd-c-OEqz?g(%n_B=1knj7Xc_|ZD+S^(?G^cFz)=h|GQoPhrsh2;{EsWMH;y^a~?W0 zaXm2KSS&kaO%p=!+t7zh{wOfFah#dU2WQdF=&r~UXjvtp<TiVVt4mQF+t< z80ln6>ZXFkOM|&{p#Ri%o$_%F(&tqs2*J$AVP>nQSBaaWsp{>JtQRDB~zuk$v!)N4eY@1vtkB z4zwIKfJFRwe-5)9D-Yr2{fXq<;>t!#P62n>>oc9&hSoFu=)mV8ZBa^y-5f4dY<@e@ zNar*T|HYA?Z=_IJuaPnXj(~K06g##5Mumqg;TO@U?#n1f>o*o}eDHMnN-7T|*dmuB zCmmVfbelk%E2GNec+VMJ<~ywE)fM=%hQ*JU861#^3w{V|Z_^iulrxgx_{TWzN+NG; z6#kP^08lOlybc@QUi2~qb_aeJ;NUIBvj5j0gkLvY^M4LD1MA5r>!=J<+~K&EFAyhXn+bAORrk74SOw4tY%)Okye~&WRO;nE!wMDMljZND*AZ=6X2|C zkvNbP#)(_7Tun(?Tz$VFs(J2oaBnCUdM3Ct-#vL==~IuFVB_QFF#UHo-&}8Y71=X>4yU+hbdIW)?a3uTJmK9Pe+! z(4WQdB%fWTGEcgufGyDIlZ#bd zYvV=j-0t&b;UP z-tV0A{?7gbE&^QF-g`aIdhT_vweHn3T6cRXA;cE|D4*OAGfKxuLT&VEwuqT=HxLXL z9fAX%3V`Isw@o%aQ73DvY7E3FXkPhE6-w}j#s({2CPpIy!eI1m18ZGe<7!7ML6Ga4 z5)zHf$mMmMt@%(CGm=MJu|eR@{rFR_wASUgUK8ZFrZ$_db?oKvrjQino=|k95I8uH;MFI%h49}sJ_qt3z!Wt=ifmKE=fd8cAzQZ?D zAGzq=r~$&Tm~-$!MjMVLy|~UTeZXJ)SmzSjg{b>5R&UH+G-PI-G`QAhT*CmhfUambe+GtZGaS%D z@rbLVyJ3`>>Fw!arA~{UB$t(Vc9a{rnG{QY*1Gq*)SR+gTW=R-Qd(Tp=URcN3fyb< z&>(S~A)XuWz5J(4%mbke9pcfom+y5cdJ;BO008>Mb zx*#0Sy+8xfT*>l~;;PWg2IrhBn@9C0Ktbk)M^@&;TI;hCOs_UhQ8 zT@G*IrHb0)XBv<4HI4bLi)DsMy{Z_P!9iQLl<6szI*95qD z8}X362Jyw}{nrtQ)Qb2UYo?{!LK`#G*j^R+#?cM(GjW%9eE5wnQ}9eS#)Mcsg4_ba zom;pHP+3z zF08O{A!b1O0{=>%k8q=N7PLN1QZUBt#0-)favB|UyyJ^LKU2oQEZNar2!6^+a+q8jX zxN`C(t!0pd84oAP&866hCKJit&n7*p%N7+iUpasfVESETw>1!02x?{c;iV-~8Oq;=F&G z6{PK#T3C9|{4cT#_{nyK5U^C8Ot2=VE(VNt#O3dS3gND<=TMrDCu`5@){du{iK>8Y zk$1%?g;3*2Cdq4@QFeRHX5WP#OS;Io&u_BNZ?0F{;AcGJxfLl*4jk?C3ZP4iAnd1a z3+?4pUJTqks|~bPJD(wfz~PU{sg$W*#X~hcKAp5GjH-wMR@6~9*zwW( z^*n$buW?tHEz;7Mj1|&L0`(kn4x%uh|2gd682%pwltypCSpgp&vk2pLkKR*&$-yyNO1M?^oJHLoJZX&XmD|`ifa-mj^ut`v5RYw#&9x`$n4at$GJO92>CiJx7k_uy z7i=?Y{oDOv6Z3_y1sXXr9u#P0}x%Y|5QxY2Ko^Pv}A7kY9)HQusAA~^S`qYW2; zbyZUW1v`<)8+bc80jrwGkDJL2MtAG#?j1*z8b4PM7U{VQOI1k?*X#EoYB?Vu=Iq?w z?w5AOM^i|%^9lK_sas}6;ICQxDG{!XwC}ckm*QJ;2f7=BoEonc45rSyEyxzxF%)De zQzVTLg0Au=qTbip^|NPd2;pmj^j_z{6l*r|XR5?j5qOlNFcZReRVX1!0 ze&H35e1xTV!)PbSQgg0^a_%c){DUhIN_(lG{`bLFSA#^hnMgrwo6TZ(vJ{BMK2>51 zXWZ}y(@vf|T=bFH=WcB?Ql2rw&J47H2o=>|a(&ZXx1v{qcZc_`ojX^Ttp@|+zfdfN zdzw3flJV+n`5r=^2oB+Av3yX#a#cum)Z@+)tDlO)Li=Ecpy|XT>RuuX?qJ%P62D^y zXfcthg`B`+?v9Oc)sS_-)ZWx61aGsT60{Wf>C@cR`CBmohf}yucbE5=zKO!eebFn{ z$tC^>D?j~d*}O%Ue|E|*IvoGSEdQhq!S3;71Bne=a4aBE2&h%L2-|2Ot8TW}do2%^ zH&%a{cIw(fXDiSMJ9I4(JH{m$WpTrYeqh7GweRlL4)U;2I z`1oU|9;*CO{7OrEr_(pSKTy6!HX|{yJ&SF?RHBTLR*8dg3nF}aFk;4339wn{J?^!h z4cX1{GesUWGjLE6SqD+Egz+lL}#O<78EfBZW6zP%i=I^bbfI^RH_M!(5EB~CZK?tEi zM^C*;XDP*U(g7?r2PN8?diIw8WIXHbVxvsEumY6Qi`ca#+~%5Y&iFs?^8d)|{J$Zp zv9ImUo3#@?oy@p1Mfgq)I9h0f%>PGaOTG;PF~2GX=2*nSb%O6@v7_KpmcWw*Tp<4m zXI$m%o@8~fOx@>B{nnkk@lKKuH+;>Lg5LTWFfr6Oq8MLx%Q>!+j%;~+Ea3#l-mdp# zlksY%`}n(0If2dYb6D3#v)(=WA`FEF>K zGmANg6Xh06n%4!XE9>JTzz=m2hyg}t$#rrkNT{t%5{w^TyUJ{~9#r-&D*O?wZ{xlA z`oj#u8k|>^1jf%%cYQOnox%R)AZwtfliVtSH-|d(zzo4q$6GTt?%|vlC|O4x#|x=$ z&w%}l!H*jeV5zbo?pzK05wQm%>=_;_Amah6aPC=ioYH#_473fdrozkfmRo#p%c-kK zw+?k;P#aM~8>>@dal6GT*}dPnQSnc{yqJ0KHs5h3NKb2l^J&9{);{LF4Hiohok~v4 zh`v|<4~4sQ_J{nlHLofU61NKZD{_SZ0(SH3I7rie$LUrFt` zkyb|#-}6xs_gvNTys0L*SEZ7C+T?UUyNDZqy`38JR9|Rl(L_V)`;)(&1I(5}QFY*3 z@(;=GwF*7poHL;|G#l7`geF|(rZx2jP*)1S&2yrlQVn4Q?4>C-mUs&eHnyy!p61Uf zOU2{KZ4Hy`5{ly37=P`^o!53JvbtX@IYSo!5zns)VBP6X-862-{PILuz|3HZZ@z{r zkgKI7I`SC3sB*5!#H$vtoU7F+2(W5?EAS5-dL%0SA0hXD&{h8#jpXz7q#v1Fzx<@a zPprUVrpp8rq0+Sv`m>E!+517TdqpwGA@$wW(6vP8%DMBLLZVH?f!2U*gquN(ARvD} z=Iwq%0z2wGxGExF*CF8d84fPudGMImZ<2cr_@~Hka98mp_ScGpg1P% zxtt;?<53LmE-r!U&246e;Znb-m~MH8#Zrp#bQ?=D)NQC2oIz_849GC%L7HF)b65jM$`L2S*AZke>UOOr0X0@t}#uA{Hg12u|)o&n7L^-d% zIVFj{x0K*EiOZ4z!9%ks-c?u}tE+aoq4k-f5Bh{aOo)ED>D1z?D$F=?NdfA4XR_uZU0KykuT_ z8|~Q9aX&zL@HSK{Ps1 zTQOGol4k+)`lNsbfWmc}QndoFh&f}#C6nQkP}hZsN^9+kM!8;uWIXYF-QqRwgz+o3 zeOcL-iBh-?^8~7xn`QPx$SoS5?|Z#ia!{QL^Ls;ZHl7=Tv>bVND>L;KN&2*Syq%n& z7n=EtQ(95oYtZJS%2tl}hULuzQBAMffA6CJ;N$;Ks(y6|)ObMoZ0Ho3wb2Pg-PQvL z{X-`YnQB!%SB-<|UJxr7i}~cF+uCOT^h=vITlpC*Q8fiwK-4`8l5U<4ImRm^`jY&r zB}HMJBfj-Op3Cz}B{ZcfX56NF+EmPi_h~cCb+K-?SHz05mE7KAx8a?*Y^L-Wd8i^p z@62h1F44DuNgsR`Q@qnyyFz_o=Sv4=2Jgd;=d|+RjEJtxq#ilX;IQn1SAvazdn$r& z{2G{34roJA6t=|VWP+0salJ@m4JH-Jb=K@HJ|N8R&)&`JU{%ATL9wyNjn$W~C<&(M zjItdIX^82U*Bf0Q%Gh=V9t+G>3-){;?%4Cuz@QD67OrN)XMcb}k@0ne7&(=Ui@e() zp=Sr;dq2oi+mQWMP#7m=YFE@1ls{~{E3^R>qq+@qNTux`)_?;W>D`o|l+Y4zoBYg} z{26-vzZ^L~8cuHU$YT@Oo%o~E_Q&32g)-usv0Z^fbyIg($vb_k-{a7G35Z4tF9&Km zJ0GzukxUSf4D|kh6b9MH`FU!=2l#m;m;lpGVU3(wvw#|0RK{q7{?B>}T>SK_g18He z7gpcLu6-tfwV1Ji*lu)<^2eG8iPuFrl_}zNx{N;0Z&p(Lac`K;)ic%i9s zr&AhAhX+i((aZv*s7(m2@{~%V)^yMrd1DONNC_4v@S5%lrXtVYhfie*&EY|iaEIg@ zye`&gC+KRq=}~RnoN#`4g$eYNU>Lp|EKiUFYp^;A*`elF7@1F2YOlK9Nlpfb*S09* zXD)h5Z5+iSDE?TFYqAZj;lvQ!mCmg=z^3QVPCpC4XDn8?0?Zld5SaA!+CEc}Y$c)b zN()x|NX?SPAW{%Dz^BAH>ad#B#uaM#m%0gPwP#0<{>$0)pT!SGUbv9wgIXxgXZRMU1Kl2z3AVpM1$(?c~`Ec z&+GUlqU{TX6E|+Dt?Px}C{wC_6DtrfCFI;^;@p4s}ERB<~)_sAc2znFv2Xmto2oge$^# zY$=f(jKvuurfo7iYG!Tkf($~pRsFuoP1QwmBAXVScqNJ_jT(uy3;v0bF8=L&^Sjg? z;!xCOhzH?_i4zKRNZ3rZ?xscZ-THo^(#G`Pq=Pt%2{-Ahg@Dk&P3i)FB2 z$(#Lxf^PjrW^=*P32k3)%&}8*4Fx0ocb5dE>)H6Ae%%E=5rw}!5Hv32o@p56@#|4_ z0rIe?1uK2j4dXi-ow~n{@7GO|a`E35PC=Hl1T($-Y@frZ!i}c7O?B$t&W_k= zX^ir6@F|TdtE5#>p17IGgz1-()}fWaD?&xkPDoY7u9n%LgSYq%IntW*$bASnD9~nW z;QLk1mK3icF?(0jYVRWLj)b{WD0u~IJywsbqrAX{47Q)&TDTBJ$|s@>*Xsm$7ru zG^z3_mTFXf(EhDTS>+wxcEjy7c2y)V2RHF`4|f=7rb?+d9rLBE&kLjsW%E3&_J*s9 z7iDJ}RmUGS5gjUNf`S5Wq1VOP5UyTw#OVOvM;2YazVa^#rc$e3wg$*-MEtg>$xTVQ zbX+wucGl(q&?=P>cYFHO5a0KCwJJ@Fbw#eI*(uU$%HKREOhUzGDw?&-47mc!P#B(( zooM#43~zM`REFf=;mBL9I!g14k#&ujAbabO5qO`S=iV0@F}Lv-pbl~cmFgkoXB2P^ zUtb%F?lfX39aX`LRN!7$+dh>Me&+AB{vj8z755?87W{u3iFghy#S}-*ACl%x;{+HT zQsIR6PDM_od9`_WRrW?j%~lPh>j`sc#fkTZ*Z23LQeRw3*j<>JVzEJepYh>zN|f)| zN@0eL>krh2ccnzLvOtzo8054$-V|YA=}v`}*q(iA?mMShNluLV)v}T(er`eOGxl0_UYQ8{41bE8{7T)~#Z(MWim<`(S&^sv|m&>u@y^_tCRw$J;rXIJo{WuF^pt#jJoPv7)~5E1$0@=?`uw zH;o+wrU=PVp)M>%W4>LhD#=8SGuwd?j(YqFCZ5bq$i>#KOy}ZNaQ%O(c6IR;doW>rot?2D#_^y^47&_rLH1HD#h2uB{p6=E37xQvFfX5ZObeLAR8 zw(*APoo*&xg1myc_J zwuR880L~P^-$eNJWb>>91;FcNdD;vOvb`k4G~O(p6eC)3hvS&7jivEp=YbUHNUG6* zrI|nqY|g}}+E1@(moaK3Av%%f%7UF`pH~+mtzEy61+xtdzAg@tDaYGw8(Nz~q~J63 z?vbZ_9F3%ubF*xX&OWQ+GOIfIdZ`nak`YTPPh69<){{@IdARy3i*wyOCA!zGG$CGvbi4i5Noiq)Y)0oOSs-p;@kkL^0anK*-}x;0k{{{)wGxv$+|xUjz)6*0LG zK>5;2TyDRgrJZNyy_?ax!R$0*qku0TOftX_A@ki8O7JaMJ&t4h=E|uR6D%(t zr?;|PAB6jJdk?HAY-*8mnQLi-?y3aPckVUL_P&YKb@40FoX8pmD8y)^OWS91s?}a! ze(IUMIT8~M!{I|$wP7;GMos+V7W1O5utp81M z7Akek*%+E0?Ln#m;mb~v7#V+XLo`K<^(Dc%R7q}cGq@#E#dJTTDi}KfHFmDKwAgPb zdL{iH`LDO4DZEPDc@8mmD3Jj%c#AFix0Xger`Z-_8X;H(a>QhAUS2!#=}`t zke@{x&wWF2Y-uh12xFp;6RKr)U~ES&YZGPUnOvR;lQXQ~uY6$r>Yv3R1* zNRzMiL1nWWD4M6xYaL_B#x--$Unpd}uHm-fNp+nN0Pc3T>cZ$94DBv{V8Nox^;Up5 zy@`!9K`Db6dgG9}u3pk@$3}HiJGS;jLiFj6zaXlD_l8wDg94yscnWLC#s)f6Mw}}b z_wRGx?B;(n)ei43CYiA@JY#Stpfa2Q1nmH9*bvx(Vg0N;{ZI-{3|@I3{&CA~9*xlq|ESOj zs)qb)fKaxYNPHGPk{8=u^C1Z_X^->Z?AAw9Nc&P_rJP-h=G~r5E)9BZ&}TZ9T_0AY8<2`wM%=mG zWni^oIXCR{XP0a1!bo87atq`^^&TJ6g;J1P;``4=_+V@EUrzG><<)@yXSWuQC5)ZI z!62LNX7n6Y3fZwV&#Oanw~6W(Dk+X}(Vo_eCY0iF#VqB69-IJ8D%$z;0x z;A;XE=JK$5;+qiVk?F&(d#{B$wt9*(F6(-b2J4DCJQMj0Ggz$LcjaSpOsj!AHdbbO zMU`cfYTJ8N&Ki=%W?>%GjA0E0@}z1ZRZgOt1l^$yPv#R-o%>frqg?Tg8r>b9UihYI zWWWp`^;LCrq>Gam?a>Xz1{Ah_G{2!NO`@qGGl(!`fVC97bV|sv~DNfo`nL!OpnwOdFDHgrBVo@iPnU*|Foyn6&(^XhLpTgu5UG zEN~@hPn$+n&ePi>SuFf!?G0ytZgAva+!}mWZ{Cl|XMQdz@S-^IE>BuKXDFta6uZmE zg2kYg<#&Izqeb2DteWfi`kIMT)G|m}yvQqlNH+*ge>;~4+zC~&|9+n{=wmZIm^%G3 zG6eK|k(i7BnAE4;Y;C$r>wo$-+mXLil>h!4v&0FB)|0=OSq|Sk&)9Yg@f!3wZKYJf z_^#W?-S+98N%uVUplb5csHpeGdU{px*3x}WZ5PyR(9IHCQVq~Wa>GMKCo;E2TvrD8 zMlz>(Q+8j_1v`cSC2>C$GF|oj)S&*Q>sK?3%iFQ}*Es{MMVCp{N_>dGr)-|_b6%<-x3MxosEJQia5VH(Kw1>4eRaBKpRfrM|T#F2z=5P8Hzo z?hT?Y$K>mq`*ml95vL4A<&;;gqJXmsmFB6oLT^_;lAR!Y8h@PD4!awtl^Hd(nk47B zg?ZWK(7Jv-9#CWXK$>ZF5o7yxRq*O%#rntyrw@(Y zIS=9`I5ytS`C{5|$Ym?Zgj&dX5Gwq<{wq@WKZqn#nsq+}E30~1quqg!)%hcQRj>l! zWkx^DVOW3i@pYZ#Od)Tepj{i2zWQczB_^CyTDa;TDPHQdz{8KHhI%&0gGw+D@by2E z+j~BDYpd0$qohjtT2RLiytnS#6{{lJun`>;Olva?WGI zVpmmUUF7dJG<=c+ri6C=9o;{cT{gHs$TwdYcb?W-Mh}=6!cVX5d3ZSASuj>wRg}y2 zq6XdAj*dio(@XJwcTLS)T{DgF1=E$`cB8H4U6Hs*8`RT~dtE|q>7GMD=o@?Ir;*5`TOMjEZIiHF{*xTEV}sU(oG&W07%UlW}Je#Gbp8*#0sT^4?*?a|DU z-JF(n+Y(HO5oJCM3G1%3G^xhjo99cJKR26JcQc6K!3hr5P%|4WCmK`(6lT~67vJBe zdAD(1+p6V1fgCZV`7hv*A`I8b#g3RerINHnLTL)Cosy~i25pBB?-UZEc>@Tp_ehcml| zabfUDKLp2T!&z+ZUnoBpN6C61Wyw_t`xh2$`xbaf5QvE@T}C^UY4dD)Z{$X@wC)?3 zg{~>VMfVo89XhsHp3+(ip=7UWznS;8ZkZnQ5Gx>XmD6gO3HNPPnx=Rp#^c6@bqje= zq_*!LuB~zrnqfR(={-Lr+s`TWZd5w04i9V3Hx?h~%`vbY-FXTUXGR11eZ!uz33Qu0 zZ8lH4J77WM<7?tMyyGF>L7Uco%JE48_>63CpCzj`#O5B!vE>Cw+sq>kFQ(PuQu_Er zK{@RaX+Py$B5z>+7k;&oz!{PiywP>IM6y)GT2&y$%K=kntFNl^r9r7ECsnV-w+LOI zC&Q;+ZxxzI0cP}DhvBx=`q&K*= zT>>6-IS=oKDTEkzS%+?5UJ^?DoIQqsr)v-K7d(8sZ1 zL*KtwMY1eqvtr#~qmYCgIr54)9b3k>rZW0C2+nH_cAz|>9OWcapFEXWNx_l|ky+?QLW z*6{lcW&NtLK`s?os_#fo{9^$r;~a2#TzU0>voM|MByG}`6pzy^e_CZ`64NvLMMkcL z+ESIY5WGUAZjAZa1E3{|DJFd&q3Y4aT^qz8t54afq=RD>7er=-RVJo4xcy}Dv`NFS zn{;SMSHBRzvj6nsK#EQ0{$h3KMpg^-+mHsMmO=! zhbJ+=IP9sc5qfz?*dG_%d|<@yuks{1-7E5!vGK28GRI_PKUlDz4!*~{6jV^BbsPC{ zkGZQL{Estz-0;|mrrh>&3jF#53+spPEg7LsF7x6;SgohFF`7qet#4s$-$XVKi6Nmc z=zGi~UV?z5PJZQ;JLKLx#}oKRfhA!4JX&yx#Gb*LPFF(aYRs zPF@q!#|*TNJp?^kYR;ne>@#B_#qTI!$Rg;Pt8HX`gM8Y`w@OL(TWO~oA5BchKYGQ* z@NoiI^miLq%WSC0xpT|QRZY)#zz6b6Zr*NRiV9gU8M~&RZ}#Rp7>qR~a618x!r{Gt8q9TW;i!)~~U$&LVZE@xuom!UsA8J*@$IScT2h zj@w8`t5L|0fa}UDkijdzB`lR=7{?d~_StL>Qt9-{ZdEC}4K}PgH9ykFb&|l}3N3p7 z$$oYbEg!7ewH}-L(evvxRy1X9N0RC^|++7BDwU_*tF7#)aX< zr+k%K^L@+XdIH;kEn_U1*Uy$I$%PX-*g`D3ke2R{Ud01|Aj35&B;jJgDDD#2|IdNSM|RL|OLn9bFBg-2##QPTO&a`DCqzdov1OCG6&tCuc^ z^en7$qPIQ(Hcq7K0@pFEWm-+^`kxhv<>mF^FJNJ*gcal#~YEoRx;!75VuB zYFKmsu~Q0XE|~vmMQAT|NA15V-+%Dl1;KV&a%Vq`ut~eR#WfbL-RnknM$Kb;vL}7c zk1fL>_bqXR20~c#IZp}utLJsfq_5s(KYY=44VFW=$#*~h_hKll;uj6{R&@X`QQBiU zA3&$vT`5*^M;d&fs@$Ip^M?fK?xgL~M$?EG&DGSTS~|Om zkVa4)vdw}yJnUCOo{U%34>u?P)QL$pw(jz8B#yl6vOyf|7DyMGs>K?;RX$T8=6YQW zvB;J}Q?*>R{C%@EiRo$VRh3@!*ttNy?#%5xgMjq?%%nj--D8Q}X?X_RJEda{tw|24p1R9bGW8fnu_XgC`b=UmY1^j#GCznCj4?Aj2NDAF4L?a1^oJ| z#?VMd_>8u-gGP8PpbOzkAvZ5_i28%%_b$@+nZ!(`_=3D?9Yo9Nu(1gtPABK)wOs^#u(OSl4gyWweBmq<>icUlh);bg!IrozXMAL7K zu(^)tG7?C&!4y?+nVwzl@V7z#T0<;Nr0+z)Sg>BQmZ6%7{DWA-)#`m z1c4rahTOI2MeuyiA<>-MUpRT+!$;q@b)Cx9Y>hL${R2`wVuAk^a6VHvwt42IriBq5 zy#KV+GQfR~A-jlp^zMd$p=rP$O|9H_fCsKz=)jM+;W|hQes>QJp?-|cP}f*J=5_nr z;(~!n5;5Uj?AGHGXX4AHzkLxWl}-Su+p6?0xC8nZjRVVl7t$H|{nYr8XMTcZ$Hw9z zW(x2sxjy3ch2j-rQ7d`nab`z%TI8zriw2em{xMM)gz#$#zlT`VrLV)%|Mk zHkO6WBy81YRNQZ!&)={b=o4;tnzGdBq2X4Wa@YozRat~zU!6f-H4IiJpSOl^fmbT=J{$l)v!ud5f_Iw-nz4TJYvgO6q z5!&-T{u0#>KOS=Xd0I1Y)IN7`BqN7dqyQ&EuY5D@LANhOdPc@)qr`v3q`hdzE#@*i ziSZdG;NDAJF8H;0Whs(=kuwUQfyjg;?IH%_I(Ub?z`^^1lc#@oTqgbM<$~v9k!2NT zPL$&zlb7c_*9aKl^@t=S!?Pt;OT?$ga=fh4HN_^D1J#52l}+PNwHHYb+8;-Til8A&j=HDn&cDE=HzjKxg_iAF?o&ea zV}{b5T1=w3W8r;==fUM-7Mu1LRr_f8cg#sCwpNO%cCt+c=~M%R|L7^kKWx1HOB3Xx zdvSlIW1K&LZ{UH~7>C-a8rVHhaEo5$a(fkF&KjU*YoPeM&u#!sy68T~34WkSt4kbP zC$K$j;Y#-8&X*8~nvWyg-q8|Ey5h;#UFAR%^?z82$Wl@Hlv_3N?FHgA0S=$HJ7L}9Xf~dw{EL!7 z$J)Pk_0Vq~)1o{>UyMVKaIyg7D^;!PH+4)B6NZ}NSf6~cVyt5>CpQuQZPe{lcBkpx z7L%9Uaq#;;-vAPP1CP1_=BW+ib+cnWW&Di3927b1UT7|c=WT1?C2`DNtdengNQV^w zq6IGflF3GE6^<0fx6NA7hRcoZD*l0gb~S{~RFnqnIX@&zk*wd2(gM4%LK&VuH_0Z| z6DFqg%@n_vgavJg5#Fw`6$E6PaLx=(hO^n=Sqmfa(Xplec*94hj^n=Vq(#Pf`|rkC znO5uC#}O+RIM|nip<#i+w6|qN0YFq)UIEc%7U^Ls$8kvg?jqnde+GRRg2wC$JUxxY z7ww!Z2HRPOI<`;}JVxt=Ifv#5P=rf7)XCfEm22p37f5TfP|D0|LsZ0Jh+sh3XD_9y zwVvc@#_#54f|r%`MJ;DCkglm<-qbPZHo0Zk3gB41$Jugo<8CyN_4?Kt z>7v&Xnj?9-Z1PFC7|?tGE1s;Rhe?|PVJ#Yf#Kq#^My_4cT5DNQG@yP^kJd<3WL?99hm+^$o3BFM0>s zLxHK)ImfV^x!+%H0bVui7~WlH(pu`Yu;R|(7ICeK(CZXGwwcOokn(AS&N<-l#|I-=6Gg#Gr%&#fz2hR3zWJlASj)}T%rFT8?22ZhdfrHp^cso&H5?zG}gV1esU zculUCIKVC`urVioiZyC>d(lqcLRspTD4a8PhwN&t(pDb<$-3AQkAAU-JNKX3YR@uv zq7i(yxU_>?x&Ijv&=yrJf%W zyDgM#D^$+b6$P^%UZwjWgNL%%Ok7KN-~%>i6r>LMhoUqaG1G&#P$bEstmnsVi*x== zY*wWm-5%sU;;(u(T|F;mEiLldLygOg7cS(%?IIx6hRj+=_9SB>;XMC=?F z3(9;mLexAk{XV=S7wQ;$^*&cM_wi$d;V}py*=jE;`zSfyKr!6ajmjar4hQC)@xPb# z@usGeFVWZ&`=3aQhx=X~?!kV&1>X5jH*b= z22=aYl0GG7mxw9jIsUr|kyb6kAyS*Yki-1Q@zx0M`pV7lepHp^O+}XW z9jv6!i^sdHzC|<7fZIs(BKfJWJwiS@o!av8Bl9X$gzVc!qJtexF)9PjHmBcnNpP!; z2iLN!+eA*6Jgu%YK+I>FMfTk%-wxjxkQd_Lbc`6A`r77o7$rr2elp~66hfBUp1vlS zKqCNuq(CUBA>-BXC-dE~ixRWRJtvT`Q4v+{R=7)Srs-2)Z>@JV#HzPDE`~Fjn73@d zLv>Hy3&IyJ^oR&y$}o_lLlG(2L2J{SVU5KkdP|mM)?hd}AUrb0(znF@wLH;CDMPZw z`)2x%9w$~O^At7?n&r4W#g)T`6=*)v-<4kX8Nw^=U*c?Wo!!iG{amT>f&0zU2dV`l z+26KD;55+8dzKbOE^ak~+1T7yXL1zSi?zcHWZtYE!|B!EF0W5>pr<7b8Xio>Mala^ zyWXyqr6KOpYNCaK-xF-w#K2VM}pw|=ez0A*9=7OT)LaH&2rw)u&43K9}g zY>y;+c779kEB&q5NDuKq6>|ADN?)xzwHYld%JwlT;&HTex+tcBplE43G2JTt?M;%& zl|RxI{96*ngKFXdk_q(J0#W#Yhlhy&VR=0>yK~-rp)Vu!G}8vwWbxv9jS9FPuK_Q{ zS^JNXsp>_>>8W2@`BT$zbm=n+CLL?c3MDj$f7#U*T4}wr!rr;l^DtGF30#vVqCYdA zW}+JQ+NfuTCz-5|PH>)J`pd=}J)Rlc?HB2ehLml1^k!;8 zhHUb~<8;UKUu7LCvT&!`q`$puN>)D#;XP^6Qrs0Rl(En8FMG?56ZK*bw^tW`kGs72 z^EPF`+ceueO5z0$g@Lmv&_=Yj3uTt9!kvQmU8{Qsjy>0~3HSn}dx=hDW&8`P+c;Nb z+tQ`YBOPwU-N9l#68So@TA$cULd3Ahuk8G`V7gc!)!VixT|c#(VTno8>=@MZdrcS= zvZ8?slv{;}hn!+(XDJdv6GU=|r+Vt(J8ZsSwI2Cluwx8mc9IWT#83{>E7H5JWC>N| z-erUH_^O^yDFTpWgX+|tnN3pW+sJi1iToT5W~EGFhZbh^ZWyB=@<3y`Xz`klZ&ydQ zjdGEpQ?z`xM^9S^x*+3}VttoddqIxEep_4*g%Iu3qy=yNJdu-Q@;(a2;~TzLT^Grs z+bnEvXguhYF9EglGn=D)2J@Y(74wGpECG)mRiS18V?FPGNgM`*W<=TtW{_q>H2zuNQXo3L!{y}jj+{qsAs0;auYZMgL{XO>nDsCtJ3ue)P zW(ttS-d7%l4EPj0cf{RKhruS!kKVxH8Xo;2$6>hq;sSh(XqX>TrXU;MyO5dl3TdA( z`Ni+MDhJXl^r$VIkgM$X>|;fT4hoGbp2R@G7y_j{R^}X z{|#ED55km~o%R#x@0wt5;?$U;#wwG`#yL+9CS%rzDqk22r7Y~TN$&nCd<}#Hjtp@h zyMn{VoQ%!(NAF54LR4~1&CP}}lWYbz_j?|YnxQ8`#)Vxk9PNFvcx(LGgrT@vQG>$V z(TS5T{Pz6d5flUHv-4@PZb)EMLUX7k7fp72l%fK2a#ljF<4(9T!`I#>ehKMzBgP-- zeK;|fcXWccb~hb3jc)(Fyc1ua^cRKt*X^_XgSpL|g1SQP!>mIwxsN`7FQyd1<3Wm` zMC^RsHqo3j%2qNPF@7u_*e>@Vr_UM`mh>e?vncb)~W@&YbyM z7}WwTcSRxK(ydsI1*X95TyoBM_S?H^=LknTk!@^xV=^8e&nHTE^K#LqF>k!y8vyga zSJs~TsxPO&>!;JMb_D~;s~o=H=u|Sccq?HI>=ggyQP+)$=~uWHobBhm?s&5<&nDn#!*7K~cE=4beE=Ko6Entr8!_8fHU~6$bcyXJ zK4e@j{ItE7gD&j>uYhUX{9F2H)zQr7q{#ZZ5HQQE4$CMZ;cY241{*VakWD_EFg(w( zg=?VJbv}ORmXwu;mpTtHfzj@2UV3a68hrFCC;s!1zu89G1ZBXzqhxY2Z**gN;3!LV zIga*4%}mwB^+7d-mB7Fi+?5fSwbg-&vZ$|AIyae zp-O3=gTxaxw^iuxto*!x89Bd*d9q&Cz~159gHH3209nLeZ|nlZhr?m^gzYycvNI@a zih`U>v}Mnuaq+tB-fcEDW*&|BcIX+48HQ;FRwf|GRJ+m{c`2@ zkhO@FS13=-ZD=0wRAFy?t8C*WWgCo<+gGI3U|jaI`uQ+V8ESq?^ngw}`<}z~TS|W% z(LOsL`>;I;WVUHhOZ{l=s%rtEH3rSyN4V6sF9E~j|LT8wZcn2DVqc*mxPA6uP((^v z#I6+4*2WFzKZjoEm#)Of+}7#J>v<68`)Py?fq6Z)eJcT2Su9usv-{5iAAyCKe-$l( zYrd+%%!wK!J-fi1hgpkA?82OK24rb>t+hgs1}6guD5UFk9F6LD;R#ZkwyO-8 zn6`9};iF7L@Jzvh_@D^z4vi^|vDpw5E{(?A2qCfJj@B1Ufz{196T;dq!%_7^OPmU3UttWq{ z>?m`kpfyz~nd#zweMG|2|3%t+M>Uza?p^nsb@K-c zNEVB|KYPFX-Ou|x&+EH%!KCCw!7+L!^68~l{iDht^5V+8?nUajD!Enw*a{mOJyuK9 zhWB<`r&W*o87|kQQa&yI+u&P@tZ76Bg0)3XEpHBvD@^47q;clSzorw7|gY8Me4_slXp^JC!U6f1HhH0(-$siQ|_ zi^Go5kYK%Cx8b2bB~3R>_ikex&pKZ-$BLU&bszU$*Z8q&``q(&K{>tlimWL6n)yZ> zWX1aj83L%v`A_B#99Nw8|{?>DFwS$LumSbz-Y?>UY- z4DMoV%s*@P=eh-7_l^Ens?M$cpTN+4irC7UQwMlE17(*BasmpCwHS2DH2te}F&9mwAhc2z8d(w8jHn^T7zpe{HCpWr> zJV3Ho3+wp=?U!XQ0ZHk6=Q_JtXIWug9ZmE^N;GQv{c(J$WcVV@OQ0|0UfZR#MmEEp z!}`%KzzYlh@do$#Z^#6E3fId2S5C-((cu2pe;|IcdwoUAZpA1{>*PoWjCmAKRSzS7 zU6?zBXX|b@Z;lcmFUdTrKld)o7`qhcz+>aLwOSa}_#I1`DlF%Q!_MpuLB@}nanE6A z=e3bcmDAiv+2q6zcOgQ~B-f)cFgohU6xCEEWTmm*yy$UjuG;oMYn&tdBNjlW1!+LJ z-^&{OK@w5}IkeG3U!-}qc1=+zwa%IhZ6X+a^{M@Ey-k?7a0r=Z=0UWcehmyk4r`8w0ES}}^qvxSQDO4R*Glb_k0!p)3Z*(K5$o@|L|TVNc_NVp%*aCA!h6z6i^ zW8_^ch6J<(gAa=Z+Zv?#iuwelQJ5KTQ|Aj?XDeQJKDiMVn*sO~_=}66UPH^6>kc~~ zBgFp)>o;E=+@uQshm`n}2x>E;De9WIc#$xi%SZyVL}0gg<`Cg0KOWr&)KKgkYCp0$ zGFASivS5XI05ItZBHQK+x?%4NDY>gnem%NF=$gn}@>q~af8}%Aq@xl@d})sGwr~x$ zubMdSy@s(>z{OgVl#)P@By?@~f)EQZ=6J$F**%p}(2cL)Qt-}Vrw_Y{cMvP(i^Z#} ztHa43yFSXW4RCRvFh)|Z9Rg*vPTKVU5b>R{pI8Ol*>OOSKdj1RMUDF-QtJqzUNXy5 zT&(_R;tXzWugq3gTulc(c=4W$-}-CX=6CGkO|w<4Am$M3h>?sRXF>{Nmq@6sa;~^3 zL5Wt_!s7oAClIIvp}ReMyf?qz;N5|8YfVsUa=b+?%DSX~0kuz4N!Vs_PHWr5VP`%5 zolvTA?M0eAhSz!yDu85!WFt#JKblBNs268>j+z4XsN?a4pu%5XaXAJ7F!)>R? zjN&*+%?u)*CE_rS+f1FH9iezhPhOeZwq?bC_CNu85{Q-nnETRrX;$Lv{;9IZXy&)) zv5nKaF~kby%6)9*ADP8kGlGdWqu7G6J2bSzj$2YFft0e&^2mG)-C8+(j6uF-5Nqvx zkCwg|6<=wk^zg!()4hJc2DzozUyrCW^vUMq@D0R3T1v)*B6s@1dSRtf%aJ@sr#Phz zOJE$n+sN!uF|m*5#~`0)`;?Aa5LaAz&Ch*zDI1j}Z|ms)w5 zEm{}edsB;<95uzDr8&h(PjO6P;$pR*9sBKq{s$}l%k2IC4KnV(`m>*h#otlr|4C!u z3Ffk!%~)@}U|41y?fOg*qhlp73AdhIrV&DpkWogB_nsj&ki|YxJPr{S3eYlJqtxmb z|KsgQZNy6zB25WCry>W-VjlCATieBJP}asZuDqemQ9w8P--`)p0) z-~o=86QKm+fhvtciul{BOHAfC7MD+n!4U|U`}nv#R$VMnfeA(5 zFgiZ{EYG~N669noBqC*$--sS0Oj)6hhh0UNW`P0K0h#_+u1&k}fVSwji?rcm&G z`eF;?h5$4N{^!qU^Yit;NyYWuyXSrhj@L*>chDOaFFVve%1XD)5P)vnflc#L;CC=? z-Nmt4lvkgxcp+GHpi9?Oa@V?Q)=#^5O_ArItQqm;2`!!fb=_=jJE)&h%^h;fSyyn@ z?ORbR*CLySR(5yHu|Hn@;3%`!#w_&l*|Oh!(ycx^G=7VB1bS)+F8Mb2it-;bN%6_V zrKE?$bf}_w&h-TOk_^9H8<~ff73|tcC!vtF9?MWIIbh`;z%yvm+$_l2jAd6wE#pS1 zW*nNc?nwelwMs$_@Xb4Dx07ncJjuL88EatU!)%qGm3C@DSl zOQXXkCkP-xvyKqPGaqs^=t>Uv!i;B}B$4aXm8GNqOgeZMy88cqdjI^)f0Idp(oMsu z{c^{gdJYIK9}wTNK``Zg)1L06#G<~htgP}AZh(aKtpXFS8!WU0;He6cm@~j7xtVnw zRYp0GI$9Q@w@?{HO>Qus%{gTpPJZTXOrAr@!BDle{qk^>tGkouHa;yrjFyQdO&E zT4_=!(AsmtPIe&l5e)t&T2_^-<(D42BD(3JS4Z`1r$}Wi8Fz=6$?j zxS(Yl8NZfwFfoaZ6Msd;cW~E<=1bufpoXtvNLL8>2M<|efvbNGg%?Z&=n^Oe?*0Gd zPLU4(JNm$%6x)lI@a-x4&22v3Y5@~@jN7bmgL1&j6Cfzxm zYK93}OGf}<#$>Nrw33`amS(Oa!=0`nK>11{ZYXXWr*qQECR3)19Op>3-6;j=c0#HctWG>o_f2$L4pRf$9iF~qQMl=`Sy1P(SB{+!}+ zAs-mq{(!D102z`G8{VA`Js8VZy^PpiK$^TH<9E3oAaK=Ri>Px7ii<*erg+SUF9=To z$gJN9{z^;U_J3IRM}+PS)g?5rAJ=@;?r7Kr0jv+DBo(qi3nS$+zR-9KY`So(j$cmA zDFzUuRg7-+*O!}jo*0snB&S>P6=6}pp>ck!VtZwDXQ&_u-H3VW=rByJ)e;asDOU2O zQiQduiB4%2b-A=51G-4O@`$634of%3Ly?3ZMp-(En)9MMTl7agitVbhhzXin>op3x z2E>I|@3`1uM6pQTh_h=uG`!QQG7S)(8_8BH%ZYOEj!R}*PhIo1!zryb_t#w zwSJ2CO9Q44loG4j9`lZc*E3kWl=^4{=*=3cJ+k5<5+A1BXmugOC4Nw~59C=}Ar{|m z`}NiEt?+Ou^B_AKA3Xw*{qB9R<99jve+wj6;vvdTc)23qdgl)vvP|hRvBKYR>FNq~ zTBwNEiXbd36z>Ea;xzN`NxXK9E<*mLEUZ0QQ&6>);_!+&muoI=ONk#0-ckh1scjQG z#{gVW0QDSa{QQagS~9uBrv_{-_ZaYRNGT1Q)BEk9-jle6P`e|~y#rz3|9-r}D)3WF zsGzEA%yRK&X5shUhtFX7--M<41GHx>L&nv&tH4YyEh*eud&X){zTiI;*!v(e zm6t^oQNp@MoM=xjdWU=0-BbQ}=gtSi?+`KhYDuJA9FRI>Rz0iHfB{AT}%$VzM*8(rCnMQ z4gVjNZ9t>TFzX-jQaJP0=es=#p)~; zQA%=ed-9FZ348rL%xjX&FK%r8Rd6ieP(=3Rzxan3b5Wp4W7 zmr8G?gqV&O5uL4`i1SAW<24_}SPh$9iZ1SM%-C>Ao15K#d0o;=$ACbK3%DR2yPxeQ z5+8v+%owG5C#jtIV9z;azP_m{5J?nEM=!mIw7-yB*=0Tk!uuOyl9E<{Q_nz+ko0pd zlW)M=)H*)jbn3lD{Z;Uxnf%qr&%DMt7kFBKi*Cw1w5R6_sAZQm?r1-5m@UG~u|x?5oc(-lnc|iv=?P3h#1G_vB>P z$vElBuu@QB$a+~}4`kb6zCX}Ls$i47UL-8O6SLQIw+?^Vcxbl$h*q|v2i%*vg+-X? zyXEdl4CB}XwUB4jqf* zA*S->URkP2TnlYD{M%k%z8J;Dw#I6ScOy`YPdhdVTn+c1$KkV3+&=>@J{m{FbVZ6@ z>^CT&$LX#Z)FDHy6Sr=uAEy${CDksXK1{fbf1BI@K+1S@aIef+k1g#Pk0>Yw!JOZq}o@OO!yMoz4s31&=h|~ zDg|=Dd)oV$=&J*v4(5FwxDONn&F}j@?y|I`ly|W#Y_?ANk%{Ib6T8y!92)T~dyoamVdM$J)r!|dY3GhY^9RN9rGNpbf3W|Kb+8Jd z6+Tp*9P;%VmX%2BXbiYLsrpEzeN|w8i|*Nwso#6ork9U_=yx&C zJh;v)v`U07QM8&>gZB$OnF1-JApg-dtW(@(RiI%uVl9z4iGndO z^=e_X6F{kFg!Sa+6Q%+R*QZpAV3}|M-n|?)*WhWca7bW?*~8G2K4T{mk_I8LShIl4 z*9HAiuV6Mo_fi-A*f|@Yyv$^F-=2d>0+pXbS_Xp98ZOfXsxgXsZ5&f+KYd$}Of&#b zZ?6K6gX@#3s(NqD*;G&T5z6B=}LV3MAm!D#*v~K0z4MpXv zo#_HO;J!abtkYE5Pp_?{GhJ@bo_)PedscW%Gq#oGU%);$sOQUZ&u*@&XiNeb?=xRi z=^XBp9$c(EIrvJM^*^NeYsKAR^PX^=bI!)T8}-<&FOpRN+Rk8ah9|W&7gC(R-mj#2*a+7?>L5wDyuwB=&2sz9*Lg8^!R5zmj)#)R+WR0WD zbB@iv&bo{Bv1rPZAI{q!lY>5mvEQG5XKL^CzB=HShrwcx!9)4AVx##)1`K|4!kp@x za~^v;IsYEOo|{N$#j#qAQag13>?x`EU2^d1k7KayF+0c^gf^CKc~7= zuwQ?YqK~_IZW>>q&$2gJ!D^MA)*RZ|>s8T+mz!{DRBKRCXvZ!xor~F>k?cux;s&(I zv;PtCSGczbC)577l}2wIol`McF+kZY>v4Gg2rx}hrQDaF-Wy$@z^QNLl>b2wt3W9{ zN*WBds_see6vO^8Q@;l-{!eN)T)3Ni-5$)my7rO%2Jr19xWk=dja!+5oH*Xnx4>f3 zPAoXMX=Rpfr-M_^^Q4IwIlf61BK0}^DVabmEU!fRNM2WdsKpg2;-DW(kU6h_PFuX{ zIwjSkJ9;U}TFO){j!e;%%GEy36ETy1{>!^v$zgL`$-1^3$8;xD<55hP`H7ktNYI@n z*w>Qy4>WN|X8a(^!c!?;^`NEPtIGNYJj1gMtB%3N{DrrOc@>OGO9(VhzE#M1KatRh z0|`D&r4}4=D!Ort%IuqKFa_XeL;5QXsm$B9TxMP2CdyfS7cQ-^rBfL`O^Iy0If4P4 z&Y?=BHz(*;oi24bW+5|in6LdoN+{fIRwHJ@MO%NX!U%g#3?ayWN?~NNq!tzx92CF3 zh*3DM{d`37X6w{k1Oc5Vw=jSFXU@BQ^%!Nw`GF#i^h!ZiV%hqBLkreDPTwe=xEz zU@H0+{NfkDl>2LuMu2xttnYiiSRDg+CKdhToWI9|J-jDn@GM^L(zAnm9Z~JcPLh|7 zu9iwSLC9WBnjF6*x+Txj{QX46132qa`O_=Mu%GAio!GMaUT|(BZsvq{PWRbZYq*pM zn#z6Znu{pi75B59f+%Vw$R!RjYPyE=A-J9TqXQ+78vd*%eNUe>dF z+Ikt4Pdst?8y1O|ONdna;`(>0JFT@dDc!=b(eYBsfq8#;e` z4GO}L(}x@aUB266?OcI0O7{0fA2Bhf;AV!;R-Rdr5?xcvUbR#!dOQwWi+krOL@-Ps z8Ar^2{`iR!5x2}ee$p4C3{|Xi&R}!1Yn`)OFD$e-gev4Gr=u~;W713Vl|n_O$)Jg1Q$S^R{1 z_dvbJCZwUTs(uIOUxz}5f~rwaZmzJCAB*%Q#)}Q1-nn}7>}6tCj*~{-93;5(uTCIx!~KP8mtaSKn}>~B4fomzU67Bl5#%v1L*cdjOu%A6%t ztR*Z|>jOf)^-ecxBf;7amRufXXS-OE-C@FCIJWr=G)>~e|1jAHdw6qp{~uv+c`zh* z1luKDE&ee3c2v8bUw@f^6pY_;#U~e?)2pw6pl&|g(Y3%*cYo=0d%u`iO%;r^>pfFP zbp@O1KG#1b5DXML|M}SzN{IS3>$v=lk>r(1iv<7}2?-OriLR{Y#(1j-w1OZY$A)p>O5^ z#IP;Pzv(TGcPx|&F=Sn~E8g2N04KNGOADQ4Sa}HR<5d*4C(vsZy&?A13-4JD;z)Ev z?G^&cvwQnsDw@Fsm8`;n84RWbZuB?VI~#_}ESgOryuuqy5|4CX&T?#A4A-V^VgZkN z9Xy{HnxR?s?q@`|{wIkC>%USD+3yRr{;Wvm1(epMyZzXgHM14I`!-(#_LnINbW5*V z=UEg$$y`~8h_xr_z7`pot(sv;(0u3|)AiflIx;*oQ7qL(^AYgGbrkNRXYt1)0bD?l zp52P&+{k|Iu(_>%6Krt#gvdv|oXU-Dob?1eY}$44>{TY$bB72RyhWWj~UlN0+FON*iek#2e{Rx~p6Hsakr)t5oUiBcZfBjW{-aamsi5S*k zsT|Os0X74uCg9}B6P@FF(l25f+li67?4m`hufcQIOB>Zfs`PtItB{ejW%CZ}U>yV5 z@}4d0uTr^PfDm|lF37G%AI@!oES`+V}sDSA)q;ncI3KTwS{y!CVocm;6Rmi2BuabRUa zq=8?q?MugX+U6s-Go^PomuAM#qIlI@e2oLsUxd92U zoWEA>Y)~}mnfW_-=R5v(`-p2FKS23%t~{%R&%&!Ga($-z`DDKh_g<>$vv@!no%NLa zV%i`u+R&eCJ-!b{`Mac_Qs+K02j5wxzaW?LcIYE!dW$dZFqlozR7oJ^cf%}JTb5;f z?DFFRePsrH(^f%wlm>*>d6@t?;-zk>=AFtlW_ze}?twgF47WNt>ut7EkVyzfl{40e`xtt(^tE)}Y?jJND*jowZx3_sf!}Sd&{m=BibMVnJbcMrpubQ+v?1AR zEeFIdJ;4g+;bpV4BKP24WEoBp!?f+ytHUM!@BP5@4wf04{-FSE2iWh_2hBLy^DQiV zeFhSEBA1AFx$<>4BoO{9@VY4>KC|2BFkQ=B(7*;XY>L^v3kwBHBbzvr%)`;kz18`5 z6v>0H+zcs~*}K`i^K$s3wZ(9)MboFZt4l30CbUO~2`URxOmt0E%tU2Wk*E(6QMcnK zR6m>hIc0m4=<^;BfS%nWQ|NrH8Hl&(%sBw4ew+NYdOc}m^?-O%lAb(1dGR?}+j};& z&FZn1`22OF}e-*~eZ3naYR;3Ul>fX1F6yZvEBRu>wLw+k9$yI0X4 zkM*x-Wlgg^*>Pw70~$zJ^>O%lUNWG{^*AxTCP>g!?)E zqx3*(Q_@T62MgB*&mfS+wF78<2GN^PUh<8whPvc^R8uw=o(^@3!L%oXNjKkOLs)ph z)}apar@^O_->pIeYm=#^M4aP2pR@0PRH@}ENFL&RpSAr%oHZ*p$38pp6Oe$TSv31< zpubsCQJ~1=m{slhk9Km~752^a*Gll<=#BBusbfS6xOMVJc2jHJ&bRJkB0S2D=~le6 z_;d%|zE4(Y+f{$y-oVd2(+s^x=i2S>X!=chkL<6qe^*Ga9eMssQ~Bg|pt=GC5flS2 z9Jyq{Qfky%8iTkm4~q*f3+#6xzymgm7f?E^t(qR&y4~bgr_n;k4i}z{PX$_F(~DO2BmaYxR#?&Lt9Ql z8)i-#CS;xkVbFS!r`*72a2)YeY&mCJM}vo9?v`C19-oF@%m|7 zDBJhhmvwoT$fe+!;UQbw+?|e{1To2wDQsE8W*xWW z13?y#JS3B-M*JgDR0^|j)BkYqTMLYLC|s*nzb&f0O}Tnykz87Xnj8rm|^=;H`=@qtoRricwl%dwsj zflPgyXe8(0Vri3=P&s@(rSaE46}jli5PYTnB_+Mws*M0yK-tXIWyi4F0B&YMB1X+b zBn1=wb6!4M-bw8*EfB6R>2VVc#=jL3TrMR?uYF5;NNyO7ki=VP$Vnc`xN?{9!Dmt@ z^(@=#W9&QpqpH8n2U@4tKd<^iq1b9L&Me}B+NR>5pgbky3>ziQ+q ze)G00wirY#gy;;xH+nX?uNx%vD1Tfy657vFGu3ru3jYM{15QV7LH5X5{zo`xSeOWR zlwXS8xUWCN5QzcN(7rifS=5dTsIqo{c>dT2+lijzS|ys<9bx~#uM(nf`DfrO%@VG; z27(sAA#WBZPdSwna!Npze~;?v0$#o)Z@_Bh>ajrDb-rH_fuUIaEq1Ezm{;#8y%oGZ?`i{Buk)An@V?nR4P;B4~K+$iwDew7n)aMuz z_-6Tbmqwjw(Zwg!b35bhGbScK>TIbnk`zY>x#$ScQb!Jk^rlz*nUMid`xeEwvJ%!! z=fi>ZZZ;wx(v7bYuABJv=lFe3>odXe8~#IdD;qTDG()x}Niz>M;S(?5te*1IPl`$J ztGj{$>hfBqmg)DTiO_luJl-6d`5qVd&Cm-dqumOJJWW_20%b2D`vroKrz0yNU3I&f z6Y%1a4>R41XC!uVKLN%IEJRCqsJ5m}eZ?pI1L|hC{+9En=afm_u^`|qljT^AcV{1@ z`STi1|G_*35^T4+n};cxsxW!Z&9bc8!@!^BXtdqNG4A`GWsaku+(y7BVXso@wNu!4 zoHX#b9Y*jBs!n8Wz76_&A%NAZCz%}O9_2CGOuwsh4j7Nkqsd#TRuGxCH=w2>@_17f zw-$ZnsyGUsPI)mHrs&&y;6GIya6(*i7Wb6RhJ2p^jG^2n!KD3w!w-4pI6XJd-GXGN z-+0s}5495n!*QxMWwtJBaX`9Jgml(dUmP`Y#>8m(ifH@fsYLiVXw=zsSz%x2)=NvX zRrL<;)v63)y{+q0)Dd;FqOxAGmiFf=(tVlcq?GeX&s$<@!tM61Dh@jQSPHl5mQVV30$G&;^g<-2bki1baKj* zj-VYu4n0X9vf1@g7*%X5;ZKCu4RO>}Dtv8`#PVFyCNN+z^+|`dpM- zB)em$h*|5J6GcNv+StxrdOQvYo%_;PmM*(OA*qN3YWU{Ks@!T28|X+Vk(kej|Bz&V zF0B{nlXW>)_V$F+C2UCKC4wJlPSf%}RO$F(xovz|m$~S*{I`qM;svVRyOUqojra@N zTsq@zowwKtSQofad&KnbU-KIc=>uz#^yo!~lFy%?OT;g53aeLPVw9E3?J+$TmQSYG zn>wW(OCk#>bJ`)E;XwQn>T2BN7`aL;>M>9a;J4Tb?r&EKL1m-j(T73H+V&T+`En20 zA`WoQr=-u2v`wr>JY9>IDJU2?cA}!R+1GZu-)(Phs2y8ZV1w-owY?#s5aGO#ppls$ zI74~cgBX6h=`qNJKQHCTS~+HIe(K*WNy!0^)7q_LJU6M_rY%Ne(A7u0m|q6(18-wz zxR0_%vyosnQkvomx{|51Z$t&hL0x0w$1V7Nx6r#w?@&xI3<{%m)BUBNGutNRHk07+ zU30jwm%`7&6lmvC2v8iwdFKoCL|s|#d%rN+{&C%ozQAhhzIHJ`;87;&z1We^3!oM$ za<^y+vQPKnLVBk89k658odVz^$^6b^lI?pWCvrcQ%L+M1Z{amVoMKe(-mv)tLB-W- z>KW`skx^8EEt0I-z8i5mp`Ie^;SU-qE5|t$SDqySga+PdIL53=*J_*&(WYm$_+7}5 z;t|$za2s;GatsmSU;mC~iJy8;W1eM~i}OczJ|!kDeOqv>0>JQ4c=44@qM4TT_}1Rp zK79)Vx^&~LeO5R6uLrs&lKD9%lh>}rDK0n+(ACMx&V?h`r_AV_b6y(eJrsNR5@Pt$ z@=OjLzYo6!@x~UdzG-!79P3ZgZ&2y`K4G|9Vf3SJQku3t@>FplnFxAMnDf?&n1iy< zFT5|sk@GCKHFuvy#tbJ* zgnoPuXZc36O>12eQB1=gNF?PB@r)J0RRCrc!*7*e;+Us?MQ zf`6#XyL5GehWZG;$7U`oGUfs_d=;wJzJ1w-0m;_@!hpUi=;UH>C3f1Cv-@G zMcj4{95^l~Gva#|cJhP9Q9&jyAz=_JUVXi#VH)x_@!OhN`G;UT;jL5H`M<;RHw4cM z3opz<>u6F&e(ZRCq`$`62XC_6yl+kZS=}?pwMQUx51cn-)FOlrHRdg*K5tebv90Y^ z_|F^9w`JfNFDk|dB{vK%07q`%45n)D&$fD|a5lAMB!=-{`(I4%-L#AYbs&J{Yt>Mf zU+(X_|Ff>=!rDcxtbN659VWC7Z#}hjf8n)_pj^s8!R$buc%nX8S=l|AP2tCiBr*i`ux+jPdO1`u=nG0$gZT zLM(kh8M!>41^#i(74DO7(wKgx_hlq?=>~Q9^vw8OVl+^>VL;LK-a>fQlFYr5LEAHL z_e(8`!RIqoLu9-f!FFX+Xx#;%I~(0fFy zCn{rR{^z#>2i{4{6(`vNua_(Taw#^{O0fINgnTLY(*Y($W+c_LRzj*MNrSQ-cMEo9 z$3m@O3KLIMzgZ=cK<-S9(_g$)x18>4dRa?NxZ09Om{VJrN6#Ve>vK19y|7IMR`@?| z*OW|ui{7X~*yjv)=*{+N|2Q|}Mism;<=LK*Zb#IFPks!!tXHOGI)qO`B z)r|HgD`k3;Y7zJ7r0kTowE*w3VS4&IdpHo3w5|$kV}2>W#oOIfX79Hy{d|W_Dl{Pd}vqw<3PG#8)`6>28 z&noRoo;&ed!A^S7OH>c}RQe6|MK8O!SDl(pw*&Rvfkaz{^Y9JFVe{owkpqcqf-u4VMm!G zsxzP^(_GtXwxA#={!*39B`z)fRuy`|E@3^j^A1ixwt)}4Jq4SZe~ZtS55d4RK^CUI#G)* zH;O6UpeO_w*~{WL>^GmUTU8@~TsR%HMS|YZ+kiYB`3w6)<{wgNnFW|{e3CXQ8jA#E z|Nhm9e{=))V^q&5NWg*j8qdC5QW0Y}Z!Yr|7F!rIJSh!#XxH$J9(>?0+YWFM#5Y+8tY%g5Q#wX-i%+bR zb_WwVFkvA)Ia)2sDXKkyIH^l4#?;+$!!MVcUVJux@s?_bKE`UiL3v*%)3Oby)jz1Shfho62Z zd&(UDV}UBZwydhqxMESln_dS6NG})d)L!z)aPd2eq#v|0TY0G7@S$q^gM|JpQof_S zhtW&2N(6`vxRXvNdbrn?WLW|}aUNpWL+`l!a?3=xkxxxonHF1$q3R2zO%9&~?F_04VV}#CL! z&&zr{=hdE3+8h39LLtk)K>ns)g!!uNI;(6FrJGehx-d|tVfGbi91O5Y)9)-56C(T5 zpO6KU-a2 zWbAjI^l+&#&Q)B_@E$NXuw*(jKPbL!{`}1^xvubD@@=?3YQEY~=Ya;j#A#?DcEtWG zpSmV(@P4|uLBZJkL%1$8VrTuc>bDylygXa;d7f_{79u$!F-S?fX z5dZ01^Iy*W_uDX|mm+`PBOH&d3jB`Ua}Bv�To{p0>TS4iNfhWB&71R^UGm{_mgv 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 delta 110 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

    IBN-P_mHboY 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%0DyPzwd&2D79v&f&Ux9o))C|gw~8YDJJ)BG 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 X6!I%N{(t-1LD6%YRd5WQJl%f)Lsvf! diff --git a/docs/src/pages/img/papers/new-directions.png b/docs/src/pages/img/papers/new-directions.png index a6d419e779d1b295d1eaabc1dbb7220481a5f639..7e325e145346196b5b074d91c55f98d9f804f8f2 100644 GIT binary patch delta 149 zcmeC}7VGU6n^48cEhM5T@bj)@W@B?}Gh=HrQ)@GGYctE%W>&Kn2}46&LyHh211l3l zD?>AF0|P4q19qm*q0=w6uuA%1lP!z)e#yYVpi$x)QIe8al4@0wnVVW%l9*e-U}Ruq RqKi$BqF0vLbhB30Z~zySDqjEq delta 149 zcmeC}7VGU6n^48c&Lb|!l@g#Y)7aeF%-Gt@)Y{D4+RU=GnboXC!oXbD&?v;n*vin{ z%FsaDz`)ADU}fZ=yVEbWuuA%1lfA*>lE}copi$x)QIe8al4@0wnVVW%l9*e-U}Ruq RqKi#WzhLUV>1M60;Q+sEDwqHO diff --git a/docs/src/pages/img/papers/suki.png b/docs/src/pages/img/papers/suki.png new file mode 100644 index 0000000000000000000000000000000000000000..b3db39c9ead6debb5c439a56bc2e79cdd83e2a7b GIT binary patch literal 200916 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ℑ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 literal 0 HcmV?d00001 diff --git a/docs/src/pages/img/papers/tiemo-cidr-2024.png b/docs/src/pages/img/papers/tiemo-cidr-2024.png index 0c0a9378f96d2456fb8066c34c62bfae9de20069..4188c96c3c1761b497fb20e9a06fd09e76543e6b 100644 GIT binary patch delta 131 zcmeDGF4_NGvY~~sg{g(Pg{6gc3){ysPeVgpLyHh211l3lD?cndoBEaD&Aqak^DGn~VfD*?z&)ecSWO*%Vm-UYaFo diff --git a/docs/src/pages/img/papers/tiemo-sigmod-2024.png b/docs/src/pages/img/papers/tiemo-sigmod-2024.png index bbddb644965328d58108981755002ba0768c2d63..7d519860207c09c4f06c0dd3fdf3cbdfc6ae362b 100644 GIT binary patch delta 108 zcmdmcNoemSp@tU57N!>FEiBU(OBfpJ8d`)H8CaPZS{Yhu8yHv_7<}|<-ZOpOVirlP bvJK+a4Abu~W|6@vd!0$@{&wLdELyAp47MK4 delta 108 zcmdmcNoemSp@tU57N!>FEiBU(OBk5z8XAQd8Cw~eTNxT^8yHv_81UShadaj Laddad, Alvin Cheung, Joseph M. Hellerstein, Mae Milano, + description: [ + <>Existing streaming languages have a variety of semantic models and guarantees that are often incompatible. In this paper, we identify two general yet precise semantic properties: streaming progress and eager execution. We formally define these properties in the context of Flo, a parameterized streaming language that abstracts over dataflow operators and the underlying structure of streams., + <>To demonstrate the generality of our properties, we show how key ideas from representative streaming and incremental computation systems—Flink, LVars, and DBSP—have semantics that can be modeled in Flo and guarantees that map to our properties. + ], + conf: "POPL 2025", + links: <>PDF / arXiv + }, { title: "Optimizing Distributed Protocols with Query Rewrites", pdf: "pathname:///papers/david-sigmod-2024.pdf", thumb: require("./img/papers/david-sigmod-2024.png"), authors: <>David Chu, Rithvik Panchapakesan, Shadaj Laddad, Lucky Katahanas, Chris Liu, Kaushik Shivakumar, Natacha Crooks, Joseph M. Hellerstein, & Heidi Howard, description: [ - <>Distributed protocols such as 2PC and Paxos lie at the core of many systems in the cloud, but standard implementations do not scale. New scalable distributed protocols are developed through careful analysis and rewrites, but this process is ad hoc and error-prone. This paper presents an approach for scaling any distributed protocol by applying rule-driven rewrites, borrowing from query optimization. Distributed protocol rewrites entail a new burden: reasoning about spatiotemporal correctness. We leverage order-insensitivity and data dependency analysis to systematically identify correct coordination-free scaling opportunities. We apply this analysis to create preconditions and mechanisms for coordination-free decoupling and partitioning, two fundamental vertical and horizontal scaling techniques. Manual rule-driven applications of decoupling and partitioning improve the throughput of 2PC by 5x and Paxos by 3x, and match state-of-the-art throughput in recent work. These results point the way toward automated optimizers for distributed protocols based on correct-by-construction rewrite rules. + <>Distributed protocols such as 2PC and Paxos lie at the core of many systems in the cloud, but standard implementations do not scale. New scalable distributed protocols are developed through careful analysis and rewrites, but this process is ad hoc and error-prone. This paper presents an approach for scaling any distributed protocol by applying rule-driven rewrites, borrowing from query optimization., + <>Distributed protocol rewrites entail a new burden: reasoning about spatiotemporal correctness. We leverage order-insensitivity and data dependency analysis to systematically identify correct coordination-free scaling opportunities. We apply this analysis to create preconditions and mechanisms for coordination-free decoupling and partitioning, two fundamental vertical and horizontal scaling techniques. Manual rule-driven applications of decoupling and partitioning improve the throughput of 2PC by 5x and Paxos by 3x, and match state-of-the-art throughput in recent work. These results point the way toward automated optimizers for distributed protocols based on correct-by-construction rewrite rules. ], conf: "SIGMOD 2024", links: <>PDF / Tech Report / GitHub @@ -23,11 +36,24 @@ const papers = [ thumb: require("./img/papers/tiemo-sigmod-2024.png"), authors: <>Tiemo Bang, Chris Douglas, Natacha Crooks and Joseph M. Hellerstein, description: [ - <>Cloud object stores offer vastly different price points for object storage as a function of workload and geography. Poor object placement can thus lead to significant cost overheads. Prior cost-saving techniques attempt to optimize placement policies on the fly, deciding object placements for each object individually. In practice, these techniques do not scale to the size of the modern cloud. In this work, we leverage the static nature and pay-per-use pricing model of cloud environments to explore a different approach. Rather than computing object placements on the fly, we precompute a SkyPIE oracle---a lookup structure representing all possible placement policies and the workloads for which they are optimal. Internally, SkyPIE represents placement policies as a matrix of cost-hyperplanes, which we effectively precompute through pruning and convex optimization. By leveraging a fast geometric algorithm, online queries then are 1 to 8 orders of magnitude faster but as accurate as Integer-Linear-Programming. This makes exact optimization tractable for real workloads and we show >10x cost savings compared to state-of-the-art heuristic approaches. + <>Cloud object stores offer vastly different price points for object storage as a function of workload and geography. Poor object placement can thus lead to significant cost overheads. Prior cost-saving techniques attempt to optimize placement policies on the fly, deciding object placements for each object individually. In practice, these techniques do not scale to the size of the modern cloud. In this work, we leverage the static nature and pay-per-use pricing model of cloud environments to explore a different approach. Rather than computing object placements on the fly, we precompute a SkyPIE oracle---a lookup structure representing all possible placement policies and the workloads for which they are optimal., + <>Internally, SkyPIE represents placement policies as a matrix of cost-hyperplanes, which we effectively precompute through pruning and convex optimization. By leveraging a fast geometric algorithm, online queries then are 1 to 8 orders of magnitude faster but as accurate as Integer-Linear-Programming. This makes exact optimization tractable for real workloads and we show {">"}10x cost savings compared to state-of-the-art heuristic approaches. ], conf: "SIGMOD 2024", links: <>PDF / GitHub }, + { + title: "Suki: Choreographed Distributed Dataflow in Rust", + pdf: "pathname:///papers/suki.pdf", + thumb: require("./img/papers/suki.png"), + authors: <>Shadaj Laddad, Alvin Cheung, Joseph M. Hellerstein, + description: [ + <>Programming models for distributed dataflow have long focused on analytical workloads that allow the runtime to dynamically place and schedule compute logic. Meanwhile, models that enable fine-grained control over placement, such as actors, make global optimization difficult. In this extended abstract, we present Suki, an embedded Rust DSL that lets developers implement streaming dataflow with explicit placement of computation., + <>Key to this choreographic programming approach is our use of staged programming, which lets us expose a high-level Rust API while compiling local compute units into individual binaries with zero-overhead. + ], + conf: "CP 2024", + links: <>PDF / arXiv + }, { title: "Bigger, not Badder: Safely Scaling BFT Protocols", pdf: "pathname:///papers/david-papoc-2024.pdf", @@ -191,14 +217,20 @@ export default function Home() { {paper.conf} -

    +

    {paper["title"]}

    {paper["authors"]}

    -

    {paper.description[0]} {paper.description[1]}

    +

    {paper.description[0]} {paper.description[1]}

    {paper.links}

    diff --git a/docs/static/papers/flo.pdf b/docs/static/papers/flo.pdf new file mode 100644 index 0000000000000000000000000000000000000000..146dab24a52096e5d6c9609fe54987561c5d42b6 GIT binary patch literal 858798 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%0Dm)QH2OoRLoLOj28hdU41o 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

    {qNb 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 bRXr)K=Z=YNyS{!;OiaxAW`3NA9Xqizcdm?B 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 z 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

    sxa>FL01w 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%0D-y7ju(e_`=O@eJb{ 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�^4Tx<}htXz635FSS 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 literal 0 HcmV?d00001 From 8e1aed9090047f12f515ef5a9b164603bc962aee Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 20 Nov 2024 21:29:07 -0800 Subject: [PATCH 64/74] docs(hydroflow_plus): polish quickstart and drop stale pages (#1576) I've decided to leave some more challenging TODOs as comments for now, just so we can get the live site back into decent shape. Also changes the "Get Started" button on the landing to point to the HF+ quickstart. --- docs/docs/hydroflow_plus/aggregations.mdx | 40 --------- docs/docs/hydroflow_plus/clusters.mdx | 89 ------------------- docs/docs/hydroflow_plus/cycles.mdx | 32 ------- .../hydroflow_plus/dataflow-programming.mdx | 13 +++ docs/docs/hydroflow_plus/index.mdx | 4 +- docs/docs/hydroflow_plus/process_streams.mdx | 78 ---------------- .../hydroflow_plus/quickstart/_category_.json | 2 +- .../hydroflow_plus/quickstart/clusters.mdx | 27 +++--- .../hydroflow_plus/quickstart/distributed.mdx | 19 +++- .../quickstart/first-dataflow.mdx | 26 ++++-- docs/docs/hydroflow_plus/quickstart/index.mdx | 23 ++--- docs/docs/hydroflow_plus/stageleft.mdx | 4 +- docs/src/pages/index.js | 2 +- docs/src/util.ts | 10 +++ 14 files changed, 86 insertions(+), 283 deletions(-) delete mode 100644 docs/docs/hydroflow_plus/aggregations.mdx delete mode 100644 docs/docs/hydroflow_plus/clusters.mdx delete mode 100644 docs/docs/hydroflow_plus/cycles.mdx create mode 100644 docs/docs/hydroflow_plus/dataflow-programming.mdx delete mode 100644 docs/docs/hydroflow_plus/process_streams.mdx diff --git a/docs/docs/hydroflow_plus/aggregations.mdx b/docs/docs/hydroflow_plus/aggregations.mdx deleted file mode 100644 index 2951c1d097f5..000000000000 --- a/docs/docs/hydroflow_plus/aggregations.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Aggregations and Ticks -Hydroflow+ streams support aggregation operators such as `fold()`, which can be used to compute results that combine information from multiple elements along a stream. However, because Hydroflow+ streams are infinite, these operators have slightly different semantics than typical implementations. - -In particular, Hydroflow (and Hydroflow+) adhere to a _tick_ model of computation, where the stream is chunked into finite _ticks_ of data. At the beginning of each tick on each process (ticks are **not** synchronized), a batch of inputs are collected, the local dataflow graph is executed, and a batch of outputs are produced. This enables Hydroflow to support infinite streams while still being able to apply optimizations such as vectorization. - -## Specifying Windows -For most operators, such as `map` and `filter`, which operate on each element independently, the tick model is transparent. However, for operators that combine multiple elements, such as `fold`, the tick model is more visible. In particular, developers must explicitly specify whether they want aggregations such as `fold` or multi-stream operators such as `join` to operate over the latest batch of data or all data since the beginning of the stream. - -:::note - -Hydroflow+ tracks windows using the `W` type parameter on `Stream`. For streams with an unspecified window, this type will be `Async`, but with a window it will be `Windowed`. This guards against accidental aggregations since the type system will prevent you from aggregating over a stream with an `Async` window. - -::: - -To specify this **window**, Hydroflow+ offers two operators, `tick_batch()` and `all_ticks()`. The former specifies that the operator should operate over the latest batch of data, while the latter specifies that the operator should operate over all data since the beginning of the stream. - -For example, consider a pipelined aggregation across two processes. We can sum up elements on the first process in a batched manner using `tick_batch()`, then sum up the results on the second process in an unbounded manner using `all_ticks()`: - -```rust -let root_stream = process.source_stream(q!(1..=10)); -root_stream - .tick_batch() - .fold(q!(|| 0), q!(|acc, x| *acc += x)) - .send_bincode(&process2); - .all_ticks() - .fold(q!(|| 0), q!(|acc, x| acc + x)); -``` - -Note that Hydroflow+ streams are still unbounded! The `all_ticks()` operator does not wait until all elements are received, instead on each tick it will operate over all data since the beginning of the stream. - -So if we were to pass in inputs 1, 2, 3, we might get the following results on process 2: -``` -First Tick: 0 -Second Tick: (1 + 2) = 3 -Third Tick: (1 + 2) + (3) = 6 -``` diff --git a/docs/docs/hydroflow_plus/clusters.mdx b/docs/docs/hydroflow_plus/clusters.mdx deleted file mode 100644 index 5d742324c673..000000000000 --- a/docs/docs/hydroflow_plus/clusters.mdx +++ /dev/null @@ -1,89 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Clusters -A key restriction of processes in Hydroflow+ is that there can only be one instance of the computation assigned to each process across the entire distributed system. This is fine for simple applications with only pipelined computation, but for scaling out we need the ability to have _multiple instances of the same computation_ running in parallel. - -Clusters solve this by providing an nearly-identical API to processes, but representing a **set of instances** running the same computation instead of a single one. What changes when using a cluster is sending data, since we need to specify _which_ instance(s) of the computation to send the data to. - -## Computing on Clusters -Instantiating clusters is done using the `cluster` method on `FlowBuilder`, taking a `ClusterSpec`: -```rust -pub fn my_flow<'a, D: Deploy<'a>>( - flow: &FlowBuilder<'a, D>, - cluster_spec: &impl ClusterSpec<'a, D> -) { - let cluster = flow.cluster(cluster_spec); -} -``` - -This API follows the same pattern as processes, where a cluster spec represents a _template_ for a cluster, which can be instantiated multiple times to create multiple clusters. - -Instantiating streams on clusters uses the same APIs as streams: `source_iter` and `source_stream` are both available. But when using these APIs, the root streams will be instantiated on _all_ instances in the cluster. - -```rust -let stream = cluster.source_iter(q!(vec![1, 2, 3])); - -stream.for_each(q!(|x| println!("{}", x))) -// will print 1, 2, 3 on **each** instance -``` - -## Sending Data -Because clusters represent a set of instances, adding networking requires us to specify _which_ instance(s) to send data to. Clusters provide different types depending on if the source or receiver is a cluster or a process. - -Elements in a cluster are identified by a **cluster ID** (a `ClusterId` where `C` is the typetag of the cluster). To get the IDs of all instances in a cluster, use the `members` method on cluster, which returns a runtime expression of type `&Vec>` (which can only be used inside `q!()` or as an argument to `source_iter`). All IDs always are ranging from 0 through the length of the IDs vector. - -This can then be passed into `source_iter` to load the IDs into the graph. -```rust -let stream = process.source_iter(cluster.members()).cloned(); -``` - -### One-to-Many -When sending data from a process to a cluster, the source must be a stream of tuples of the form `(ClusterId<_>, T)` and sends each `T` element to the instance with the matching ID. - -This is useful for partitioning data across instances. For example, we can partition a stream of elements in a round-robin fashion by using `enumerate` to add a sequence number to each element, then using `send_bincode` to send each element to the instance with the matching sequence number: -```rust -let cluster_ids = cluster.members(); -let stream = process.source_iter(q!(vec![123, 456, 789])) - .enumerate() - .map(q!(|(i, x)| ( - ClusterId::from_raw(i % cluster_ids.len() as u32), - x - ))) - .send_bincode(&cluster); -``` - -To broadcast data to all instances in a cluster, use `broadcast_{bincode,bytes}`, which acts as a shortcut for the cross product. - -```rust -let stream = process.source_iter(q!(vec![123, 456, 789])) - .broadcast_bincode(&cluster); -``` - -### Many-to-One -In the other direction, sending data from a cluster to a process, we have a stream of elements of type `T` at the sender but on the recipient side we get a stream of tuples of the form `(u32, T)`, where the `u32` is the ID of the instance that sent the element. The elements received from different instances will be interleaved. - -This is useful for aggregating data from multiple instances into a single stream. For example, we can use `send_bincode` to send data from all instances to a single process, and then print them all out: -```rust -let stream = cluster.source_iter(q!(vec![123, 456, 789])) - .send_bincode(&process) - .for_each(q!(|(id, x)| println!("{}: {}", id, x))); -``` - -If you don't care which instance sent the data, you can use `send_{bincode,bytes}_interleaved`, where the recipient receives a stream of `T` elements, but the elements received from different instances will be interleaved. -```rust -let stream = cluster.source_iter(q!(vec![123, 456, 789])) - .send_bincode_interleaved(&process) - .for_each(q!(|x| println!("{}", x))); -``` - -### Many-to-Many -Finally, when sending data from one cluster to another (or to itself as in distributed protocols), the source emits tuples of the form `(u32, T)` and sends each `T` element to the instance with the matching `u32` ID, but the recipient also gets `(u32, T)` tuples with the ID of the sender. - -We can use the same shortcuts as before. For example, we can use `broadcast_bincode_interleaved` to send data from all instances in a cluster to all instances in another cluster, and then print them all out: -```rust -let stream = cluster1.source_iter(q!(vec![123, 456, 789])) - .broadcast_bincode_interleaved(&cluster2) - .for_each(q!(|x| println!("{}", x))); -``` diff --git a/docs/docs/hydroflow_plus/cycles.mdx b/docs/docs/hydroflow_plus/cycles.mdx deleted file mode 100644 index 21d1b78fcf52..000000000000 --- a/docs/docs/hydroflow_plus/cycles.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -sidebar_position: 5 ---- - -# Cycles -Hydroflow+ supports cyclic graphs, which are useful for iterative computations or patterns like heartbeats. - -Because streams are represented as values when constructing a Hydroflow+ graph, we can't directly create cycles since that would require a forward reference. Instead, Hydroflow+ offers an API to create a cycle by using a _placeholder_ stream, which is a stream that can be used as a placeholder for a stream that will be created later. - -We can create a cycle by using the `cycle` method on flow with a process or cluster. This returns a tuple of two values: a `HfCycle` value that can be used to complete the cycle later and the placeholder stream. - -```rust -let (complete_cycle, cycle_placeholder) = process.cycle(); -``` - -For example, consider the classic graph reachability problem, which computes the nodes reachable from a given set of roots in a directed graph. This can be modeled as an iterative fixpoint computation where we start with the roots, then repeatedly add the children of each node to the set of reachable nodes until we reach a fixpoint. - -In Hydroflow+, we can implement this using cycles: - -```rust -let roots = process.source_stream(roots); -let edges = process.source_stream(edges); - -let (complete_reached_nodes, reached_nodes) = process.cycle(); - -let reach_iteration = roots - .union(&reached_nodes) - .map(q!(|r| (r, ()))) - .join(&edges) - .map(q!(|(_from, (_, to))| to)); -complete_reached_nodes.complete(&reach_iteration); -``` diff --git a/docs/docs/hydroflow_plus/dataflow-programming.mdx b/docs/docs/hydroflow_plus/dataflow-programming.mdx new file mode 100644 index 000000000000..f88b2d389233 --- /dev/null +++ b/docs/docs/hydroflow_plus/dataflow-programming.mdx @@ -0,0 +1,13 @@ +--- +sidebar_position: 1 +--- + +# Dataflow Programming +Hydroflow+ uses a dataflow programming model, which will be familiar if you have used APIs like Rust iterators. Instead of using RPCs or async/await to describe distributed computation, Hydroflow+ instead uses **asynchronous streams**, which represent data arriving over time. Streams can represent a series of asynchronous events (e.g. inbound network requests) or a sequence of data items. + +Programs in Hydroflow+ describe how to **transform** entire collections of data using operators such as `map` (transforming elements one by one), `fold` (aggregating elements into a single value), or `join` (combining elements from multiple streams on matching keys). + +If you are familiar with Spark, Flink or Pandas, you will find Hydroflow+ syntax familiar. However, note well that the semantics for asynchronous streams in Hydroflow+ differ significantly from bulk analytics systems like those above. In particular, Hydroflow+ uses the type system to distinguish between bounded streams (originating from finite data) and unbounded streams (originated from asynchronous input). Moreover, Hydroflow+ is designed to handle asynchronous streams of small, independent events very efficiently. + + + diff --git a/docs/docs/hydroflow_plus/index.mdx b/docs/docs/hydroflow_plus/index.mdx index 77a9ee57362c..c2ade3d3f6f3 100644 --- a/docs/docs/hydroflow_plus/index.mdx +++ b/docs/docs/hydroflow_plus/index.mdx @@ -3,11 +3,11 @@ sidebar_position: 0 --- # Introduction -Hydroflow+ is a high-level distributed streaming framework for Rust powered by the [Hydroflow runtime](../hydroflow/index.mdx). Unlike traditional architectures such as actors or RPCs, Hydroflow+ offers _choreographic_ APIs, where expressions and functions can describe computation that takes place across many locations. It also integrates with [Hydro Deploy](../deploy/index.md) to make it easy to deploy and run Hydroflow+ programs to the cloud. +Hydroflow+ is a high-level distributed programming framework for Rust powered by the [Hydroflow runtime](../hydroflow/index.mdx). Unlike traditional architectures such as actors or RPCs, Hydroflow+ offers _choreographic_ APIs, where expressions and functions can describe computation that takes place across many locations. It also integrates with [Hydro Deploy](../deploy/index.md) to make it easy to deploy and run Hydroflow+ programs to the cloud. Hydroflow+ uses a two-stage compilation approach. HF+ programs are standard Rust programs, which first run on the developer's laptop to generate a _deployment plan_. This plan is then compiled to individual binaries for each machine in the distributed system (enabling zero-overhead abstractions), and are then deployed to the cloud using the generated plan along with specifications of cloud resources. -Hydroflow+ has been used to write a variety of high-performance distributed system, including implementations of classic distributed protocols such as two-phase commit and Paxos. Work is ongoing to develop a distributed systems standard library that will offer these protocols and more as reusable components. +Hydroflow+ has been used to write a variety of high-performance distributed systems, including implementations of classic distributed protocols such as two-phase commit and Paxos. Work is ongoing to develop a distributed systems standard library that will offer these protocols and more as reusable components. :::caution diff --git a/docs/docs/hydroflow_plus/process_streams.mdx b/docs/docs/hydroflow_plus/process_streams.mdx deleted file mode 100644 index 75559052b43a..000000000000 --- a/docs/docs/hydroflow_plus/process_streams.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Processes and Streams -Hydroflow+ involves two main concepts: -- **Processes**, which represent _where_ elements of a dataflow program are processed -- **Streams**, which define _what_ is being computed - -By combining the two, Hydroflow+ makes it possible to implement both low-level distributed protocols and high-level dataflow programs using the same API, all while supporting compile-time checks to guard against unexpected sources of nondeterminism. - -## Processes -Unlike most streaming systems, Hydroflow+ requires that all streams be associated with a particular **process**. A process is a logical unit of computation that can be deployed to a single machine. Processes are most closely related to _actors_ in actor-based systems, but use streaming operators rather than an imperative API. - -To create a process, we must take a `ProcessSpec` as an argument to our function. This trait abstracts over what the dataflow graph is being built for: compilation to a Rust binary or deployment. - -```rust -pub fn my_flow<'a, D: Deploy<'a>>( - flow: &FlowBuilder<'a, D>, - process_spec: &impl ProcessSpec<'a, D> -) { - ... -} -``` - -Process specs represent a _template_ for a process, which can be instantiated multiple times to create multiple processes. Multiple process specs can be useful to specify deployment characteristics for different sets of processes, such as deploying them to different cloud providers or regions. - -Instantiating a process from a process spec is done using the `process` method on `FlowBuilder`: - -```rust -let process = flow.process(process_spec); -``` - -## Streams -Streams are infinite ordered sequences of elements. They can be transformed using functional operators such as `map` and `filter`, relational operators such as `join`, and can be connected across processes using `send_to`. - -### Instantiating Streams -Root streams are created using methods available on an an instantiated process. - -#### `source_iter` -To create a stream from a Rust iterator, use `source_iter`. This is useful for loading static data into the graph. Each element of the iterator will be emitted _exactly once_ in the _first tick_ of execution (see [Aggregations and Ticks](./aggregations.mdx)). - -```rust -let stream = process.source_iter(q!(vec![1, 2, 3])); -``` - -#### `source_stream` -To create a stream from an asynchronous source, use `source_stream`. This takes any type that implements `futures::Stream` and emits each element as it is received. This is useful for loading data from external sources such as Kafka or a database. Typically, you will want to take the stream as a `RuntimeData` parameter to your function, and pass the stream in your runtime binary. - -```rust -pub fn my_flow<'a, D: Deploy<'a>>( - ..., - my_stream: RuntimeData> -) { - let stream = process.source_stream(my_stream); - ... -} -``` - -### Sending Streams between Processes -To send a stream from one process to another, use the `send_*` methods on the source stream. This takes a parameter of the process to send the data to. - -If sending a type that supports serialization using `serde`, use `send_bincode`, which uses the `bincode` crate to serialize the data. - -```rust -let process0 = flow.process(process_spec); -let process1 = flow.process(process_spec); - -let stream0 = process0.source_iter(...); -let stream1 = stream0.send_bincode(&process1); -``` - -To use custom serializers, you can use the `send_bytes` method to send a stream of `Bytes` values. - -```rust -let stream0 = process0.source_iter(...); -let stream1 = stream0.send_bytes(&process1); -``` diff --git a/docs/docs/hydroflow_plus/quickstart/_category_.json b/docs/docs/hydroflow_plus/quickstart/_category_.json index db70db116681..20b00e5064e9 100644 --- a/docs/docs/hydroflow_plus/quickstart/_category_.json +++ b/docs/docs/hydroflow_plus/quickstart/_category_.json @@ -1,6 +1,6 @@ { "label": "Quickstart", - "position": 1, + "position": 2, "link": { "type": "doc", "id": "hydroflow_plus/quickstart/index" diff --git a/docs/docs/hydroflow_plus/quickstart/clusters.mdx b/docs/docs/hydroflow_plus/quickstart/clusters.mdx index 1d234d5b43cb..85ccee8c57df 100644 --- a/docs/docs/hydroflow_plus/quickstart/clusters.mdx +++ b/docs/docs/hydroflow_plus/quickstart/clusters.mdx @@ -4,56 +4,59 @@ sidebar_position: 3 import CodeBlock from '@theme/CodeBlock'; import firstTenClusterSrc from '!!raw-loader!../../../../template/hydroflow_plus/src/first_ten_cluster.rs'; import firstTenClusterExample from '!!raw-loader!../../../../template/hydroflow_plus/examples/first_ten_cluster.rs'; -import { getLines, extractOutput } from '../../../src/util'; +import { getLines, highlightLines, extractOutput } from '../../../src/util'; # Scaling with Clusters -So far, we have looked at distributed systems where there is a single process running each piece of the compute graph -- **compute parallelism** (like pipelining). However, we can also use Hydroflow+ to run the same computation on multiple processes -- achieving **data parallelism** (like replication and partitioning). This is done by creating a **cluster** of processes that all run the same subgraph. +So far, we have looked at distributed systems where each process is running a different piece of the compute graph -- **compute parallelism**. However, we can also use Hydroflow+ to run the same computation on multiple processes -- achieving **data parallelism** (e.g. partitioning). This is done by creating a **cluster** of processes that all run the same subgraph of code. ## Dataflow with Clusters -Just like we use the `Process` type to represent a virtual handle to a single node, we can use the `Cluster` type to represent a handle to a **set of nodes** (with size unknown at compile-time). +Just like we use the `Process` type to represent a virtual handle to a single node, we can use the **`Cluster`** type to represent a handle to a **set of nodes** (with size unknown at compile-time). -A `Stream` materialized on a `Cluster` can be thought of as SIMD-style programming, where the stream represents many independent streams on each member of the cluster, and each transformation of the stream performs the transformation on each cluster member. +A `Stream` located on a `Cluster` can be thought of as SIMD-style programming, where each cluster member executes the same operators but on different pieces of data. -To start, we set up a new module in `first_ten_cluster.rs` with a dataflow program that takes in a `Process` for a leader and `Cluster` for a set of workers. +To start, we set up a new module in `src/first_ten_cluster.rs` with a dataflow program that takes in a `Process` for a leader and `Cluster` for a set of workers. {getLines(firstTenClusterSrc, 1, 6)} -We start by materializing a stream of numbers on the `leader`, as before. But rather than sending the stream to a single process, we will instead _distribute_ the data to each member of the cluster using `round_robin_bincode`. This API sends data to a `cluster` in a round-robin fashion by using the order of elements to determine which cluster member the element is sent to. +We start by materializing a stream of numbers on the `leader`, as before. But rather than sending the stream to a single process, we will instead _distribute_ the data to each member of the cluster using `round_robin_bincode`. This API places data on a `cluster` in a round-robin fashion by using the order of elements to determine which cluster member each element is sent to. :::info -There are a variety of APIs for sending data to and reciving data from clusters. For example, we can `broadcast_bincode` to send copies to all members, or use the existing `send_bincode` if we have a custom algorithm to determine which cluster member should receive a piece of data. +There are a [variety of APIs](pathname:///rustdoc/hydroflow_plus/stream/struct.Stream.html#impl-Stream%3CT,+L,+B%3E-2) for sending data to and receiving data from clusters. For example, we `broadcast_bincode` to send copies to all members (e.g. for replication), or use `send_bincode` if we have a custom partitioning algorithm. ::: {getLines(firstTenClusterSrc, 7, 9)} -On each cluster member, we will then do some work to transform the data (using `map`) and log out the transformed values locally (using `inspect`, which is useful for debugging logic). +On each cluster member, we will then do some work to transform the data (using `map`) and log the transformed values locally (using `inspect`, which is useful for debugging). {getLines(firstTenClusterSrc, 10, 11)} -Finally, we will send the data back to the leader. We achieve this using a variant of the APIs from before: `send_bincode_interleaved`. This is similar to `send_bincode` in that the elements are sent to the leader process, but the elements from different cluster members are mixed together into a single stream with the same element type as the sender side (regular `send_bincode` would result in a stream of (cluster ID, data) tuples). +Finally, we will send the data back to the leader. We achieve this using a variant of the APIs from before: `send_bincode_interleaved`. If we used `send_bincode`, we would get a stream of (cluster ID, data) tuples. Since it is a common pattern to ignore the IDs, `send_bincode_interleaved` is available as a helper. {getLines(firstTenClusterSrc, 12, 14)} ## Deploying Clusters -Deployment scripts are similar to before, except that when provisioning a cluster we provide a list of deployment hosts rather than a single one. In our example, we'll launch 4 nodes for the cluster. +Deployment scripts are similar to before, except that when provisioning a cluster we provide a list of deployment hosts rather than a single one. In our example, we'll launch 4 nodes for the cluster by creating a `Vec` of 4 localhost instances. -{firstTenClusterExample} +{highlightLines(firstTenClusterExample, [14])} We can then launch the program: ```bash #shell-command-next-line cargo run --example first_ten_cluster +#highlight-next-line [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 0] 0 [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 2] 4 [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 2] 12 +#highlight-next-line [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 0] 8 [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 3] 6 [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 1] 2 [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 1] 10 [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 1] 18 [hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 0 +#highlight-next-line [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 0] 16 [hydroflow_plus_template::first_ten_cluster::Worker (cluster 1) / 3] 14 [hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 8 @@ -67,4 +70,4 @@ cargo run --example first_ten_cluster [hydroflow_plus_template::first_ten_cluster::Leader (process 0)] 14 ``` -You'll notice the round-robin distribution in action here, as each cluster log is tagged with the ID of the member (e.g. `/ 0`). In our deployment, we are sending data round-robin across 4 members of the cluster, numbered `0` through `3`. Hence cluster member `0` receives values `0`, `4`, `8`, member `1` receives values `1`, `5`, `9`, and so on. +You'll notice the round-robin distribution in action here, as each cluster log is tagged with the ID of the member (e.g. `/ 0`). In our deployment, we are sending data round-robin across 4 members of the cluster, numbered `0` through `3`. Hence cluster member `0` receives values `0`, `4`, `8` (corresponding to the highlighted lines), member `1` receives values `1`, `5`, `9`, and so on. diff --git a/docs/docs/hydroflow_plus/quickstart/distributed.mdx b/docs/docs/hydroflow_plus/quickstart/distributed.mdx index d6c5dd27c7f7..4b958cd52c86 100644 --- a/docs/docs/hydroflow_plus/quickstart/distributed.mdx +++ b/docs/docs/hydroflow_plus/quickstart/distributed.mdx @@ -7,9 +7,10 @@ import firstTenDistExample from '!!raw-loader!../../../../template/hydroflow_plu import { getLines, extractOutput } from '../../../src/util'; # Adding Distribution -Continuing from our previous example, we will now look at how to deploy our program to run on multiple processes. First, we need to extend our dataflow program to use multiple processes with a network between them. +Continuing from our previous example, we will now look at how to deploy our program to run on two processes. + +We'll start by updating our dataflow function signature to take two processes (in a new file, `src/first_ten_distributed.rs`). At this point, we'll need to add a lifetime parameter `'a` which represents the lifetime of data referenced by our dataflow logic. This lifetime needs to be the same across all the processes, so it can't be elided. -We'll start by updating our function signature to take two processes. At this point, we'll need to add a lifetime parameter `'a` which represents the lifetime of data referenced by our dataflow logic. This lifetime needs to be the same across all the processes, so it can't be elided. ```rust title="src/first_ten_distributed.rs" use hydroflow_plus::*; @@ -17,6 +18,12 @@ use hydroflow_plus::*; pub fn first_ten_distributed<'a>(p1: &Process<'a>, p2: &Process<'a>) ``` +:::info + +The Hydroflow+ template only contains the final version of this program. In order to follow along with the tutorial, we recommend overwriting `src/first_ten_distributed.rs` according to the following snippets. + +::: + Now, we'll use a new API, `send_bincode` to establish a network between our processes (`bincode` is the serialization format we are using). Given a stream on process `p1`, we can send the data to `p2` by calling `.send_bincode(p2)`, which returns a stream on `p2`. So to make our program distributed, it only takes a single line change. ```rust title="src/first_ten_distributed.rs" @@ -28,7 +35,7 @@ pub fn first_ten_distributed<'a>(p1: &Process<'a>, p2: &Process<'a>) { } ``` -Then, we can update our deployment script to launch both processes on localhost. Hydro Deploy will automatically handle service discovery and networking, since it knows the full network topology (on UNIX systems, this will use a UNIX socket for networking). +Then, we can update our deployment script to launch both processes on localhost. Hydro Deploy will automatically handle service discovery and networking, since it knows the full network topology. {firstTenDistExample} @@ -54,6 +61,12 @@ To fix this, we can use the optional type parameter on `Process`, which lets us {getLines(firstTenDistSrc, 3, 10)} +:::info + +This is the final version of our dataflow which you will find in the Hydroflow+ template. + +::: + If you are using an IDE extension like [Rust Analyzer](https://rust-analyzer.github.io/), you'll see these types attached to each stream. And if we launch the program again, we'll see much better logs: ```bash diff --git a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx index f9c54d8985c5..75ca4a9f5552 100644 --- a/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx +++ b/docs/docs/hydroflow_plus/quickstart/first-dataflow.mdx @@ -24,23 +24,37 @@ cargo generate gh:hydro-project/hydroflow template/hydroflow_plus ::: ## Writing a Dataflow +In Hydroflow+, streams are attached to a **location**, which is either a virtual handle to a **single machine** (the **`Process`** type) or **set of machines** (the **`Cluster`** type). A single piece of Hydroflow+ code can describe a distributed program that runs across multiple processes and clusters, each with their own local state and data. -In Hydroflow+, streams are attached to a **`Location`**, which is a virtual handle to a **single machine** (the `Process` type) or **set of machines** (the `Cluster` type). To write distributed programs, a single piece of code can use multiple locations. - -Our first dataflow will run on a single machine, so we take a `&Process` parameter. We can materialize a stream on this machine using `process.source_iter` (which emits values from a provided collection), and then print out the values using `for_each`. +We'll write our first dataflow in `src/first_ten.rs`. This program will run on a single machine, so we take a single `&Process` parameter. We can materialize a stream on this machine using `process.source_iter` (which emits values from a static in-memory collection), and then print out the values using `for_each`. {firstTenSrc} -You'll notice that the arguments to `source_iter` and `for_each` are wrapped in `q!` macros. This is because Hydroflow+ uses a two-stage compilation process, where the first stage generates a deployment plan that is then compiled to individual binaries for each machine in the distributed system. The `q!` macro is used to mark Rust code that will be executed in the second stage ("runtime" code). This generally includes snippets of Rust code that are used to define static sources of data or closures that transform them. +:::caution + +You'll notice that the arguments to `source_iter` and `for_each` are wrapped in `q!` macros. The top-level Hydroflow+ program (`first_ten`) is responsible for setting up the dataflow structure, whereas the `q!` macro is used to mark the Rust code that will be executed at **runtime**. Generally, runtime code in a `q!` macro is a snippet of Rust code that defines a static source of data or a closure. + +If you forget to wrap a block in `q!` when that is required, you'll see an error like: +``` +closure is expected to take 5 arguments, but it takes X arguments +``` + +::: ## Running the Dataflow -Next, let's launch the dataflow program we just wrote. To do this, we'll need to write a bit more code in `examples/first_ten.rs` to configure our deployment (generally, we will place deployment scripts in `examples` because Hydro Deploy is a dev dependency). +To run a Hydroflow+ program, we need to write some deployment configuration in `examples/first_ten.rs`. + +:::tip + +When using Hydroflow+, we will *always* place our deployment scripts in the `examples` directory. This is required because deployment is done via [Hydro Deploy](../../deploy/index.md) which is a _dev dependency_---i.e. not part of the dependencies used for generating binaries (but available to programs in the `examples` directory). + +::: {firstTenExampleSrc} First, we initialize a new [Hydro Deploy](../../deploy/index.md) deployment with `Deployment::new()`. Then, we create a `FlowBuilder` which will store the entire dataflow program and manage its compilation. -To create a `Process`, we call `flow.process()`. After the dataflow has been created, we must map each instantiated `Process` to a deployment target using `flow.with_process` (in this case we deploy to localhost). +To create a `Process`, we call `flow.process()`. After the dataflow has been created (by invoking the `hydroflow_plus_template::first_ten::first_ten` function we created earlier), we must map each instantiated `Process` to a deployment target using `flow.with_process` (in this case we deploy to localhost). Finally, we call `flow.deploy(&mut deployment)` to provision the dataflow program on the target machine. This returns a struct with handles to the instantiated machines, which we must store in the `_nodes` variable to prevent them from being dropped. Then, we can start the dataflow program and block until `Ctrl-C` using `deployment.run_ctrl_c()`. diff --git a/docs/docs/hydroflow_plus/quickstart/index.mdx b/docs/docs/hydroflow_plus/quickstart/index.mdx index d5e8a071ffaa..c53a66903f61 100644 --- a/docs/docs/hydroflow_plus/quickstart/index.mdx +++ b/docs/docs/hydroflow_plus/quickstart/index.mdx @@ -1,34 +1,21 @@ # Quickstart In this tutorial, we'll walk through the basics of Hydroflow+ by building a simple dataflow that prints out the first 10 natural numbers. We'll start with a single process, then pipeline the computation, and finally distribute it across a cluster. -## Installing Rust +:::tip First you will need to install Rust. We recommend the conventional installation -method, `rustup`, which allows you to easily manage and update Rust versions. - -[**Install Rust**](https://www.rust-lang.org/tools/install) +method, `rustup`, which allows you to easily manage and update Rust versions: [**Install Rust**](https://www.rust-lang.org/tools/install) The link in the previous line will take you to the Rust website that shows you how to install `rustup` and the Rust package manager `cargo` (and the internally-used `rustc` compiler). `cargo` is Rust's main development tool, used for building, running, and testing Rust code. -The following `cargo` commands will come in handy: -* `cargo check --all-targets` - Checks the workspace for any compile-time - errors. -* `cargo build --all-targets` - Builds all projects/tests/benchmarks/examples - in the workspace. -* `cargo clean` - Cleans the build cache, sometimes needed if the build is - acting up. -* `cargo test` - Runs tests in the workspace. -* `cargo run -p hydroflow --example ` - Run an example program in - `hydroflow/examples`. - -## VS Code Setup - We recommend using VS Code with the `rust-analyzer` extension (and NOT the `Rust` extension). +::: + ## Getting Started with Hydroflow+ To get started with a new project, we'll use the Hydroflow+ template. The template comes with a simple distributed program. @@ -46,4 +33,4 @@ After `cd`ing into the generated folder, we can run tests for the included sampl ```bash #shell-command-next-line cargo test -``` \ No newline at end of file +``` diff --git a/docs/docs/hydroflow_plus/stageleft.mdx b/docs/docs/hydroflow_plus/stageleft.mdx index f2a4ef062ce2..e4ac5014c9e8 100644 --- a/docs/docs/hydroflow_plus/stageleft.mdx +++ b/docs/docs/hydroflow_plus/stageleft.mdx @@ -1,8 +1,10 @@ --- title: Stageleft -sidebar_position: 6 +sidebar_position: 3 --- import StageleftDocs from '../../../stageleft/README.md' +Under the hood, Hydroflow+ uses a library called Stageleft to power the `q!` macro and code generation logic. The following docs, from the Stageleft README, outline the core architecture of Stageleft. + diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js index c55173a9b8f3..7b89d377e3bb 100644 --- a/docs/src/pages/index.js +++ b/docs/src/pages/index.js @@ -26,7 +26,7 @@ export default function Home() { justifyContent: "center", flexWrap: "wrap" }}> - { + if (lines.includes(i + 1)) { + return `// highlight-next-line\n${line}`; + } + return line; + }).join('\n'); +} + /// Extract the output from the stdout snapshots created by `surface_examples.rs`. /// /// This hides the graph output. Use `extractMermaid` to extract the graph output. From 0c104c5dc5ae5c82a9ab058f4d785219b9a5be7d Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 21 Nov 2024 09:08:56 -0800 Subject: [PATCH 65/74] fix(docs): use async API required by Mermaid v10 (#1579) Docusaurus v3 broke Mermaid in the playground due to an API change. --- docs/package-lock.json | 3 ++- docs/src/pages/playground.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 315bd3fc8e6e..046e857f96dd 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -28,10 +28,11 @@ "@docusaurus/module-type-aliases": "^3.6.1" }, "engines": { - "node": ">=16.14" + "node": ">=18.0" } }, "../website_playground/pkg": { + "name": "website_playground", "version": "0.0.0" }, "node_modules/@algolia/autocomplete-core": { diff --git a/docs/src/pages/playground.js b/docs/src/pages/playground.js index e1f706d3f5a1..fdff5ddcd479 100644 --- a/docs/src/pages/playground.js +++ b/docs/src/pages/playground.js @@ -39,7 +39,7 @@ import styles from "./playground.module.css"; function MermaidGraph({ id, source }) { const [svg, setSvg] = useState({ __html: 'Loading Mermaid graph...' }); useEffect(() => { - mermaid.render(id, source, svg => { + mermaid.render(id, source).then(({ svg }) => { setSvg({ __html: svg, }); From 4c5ca31486a9cfcbcba3af03aa30084a8b8dfcce Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 21 Nov 2024 09:13:40 -0800 Subject: [PATCH 66/74] feat(hydroflow_plus)!: introduce an unordered variant of streams to strengthen determinism guarantees (#1568) Previously, sending data from a `Cluster` would return a stream assumed to have deterministic contents **and** ordering, which is false. This introduces another type parameter for `Stream` which tracks whether element ordering is expected to be deterministic, and restricts operators such as `fold` and `reduce` to commutative aggregations accordingly. --- Cargo.lock | 18 +- hydroflow_plus/Cargo.toml | 1 + hydroflow_plus/src/boundedness.rs | 7 + hydroflow_plus/src/lib.rs | 5 +- hydroflow_plus/src/location/can_send.rs | 10 + hydroflow_plus/src/location/cluster.rs | 12 - hydroflow_plus/src/singleton.rs | 3 +- hydroflow_plus/src/stream.rs | 419 +++++++++++----- hydroflow_plus_test/src/cluster/compute_pi.rs | 2 +- hydroflow_plus_test/src/cluster/map_reduce.rs | 2 +- hydroflow_plus_test/src/cluster/paxos.rs | 239 ++++----- .../src/cluster/paxos_bench.rs | 171 ++++--- hydroflow_plus_test/src/cluster/paxos_kv.rs | 7 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 473 +++++++++--------- hydroflow_plus_test/src/cluster/two_pc.rs | 2 +- .../src/local/graph_reachability.rs | 3 +- 16 files changed, 799 insertions(+), 575 deletions(-) create mode 100644 hydroflow_plus/src/boundedness.rs diff --git a/Cargo.lock b/Cargo.lock index 80f84c7cb2a3..17b2e0470e4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1493,7 +1493,7 @@ dependencies = [ "ref-cast", "regex", "rustc-hash 1.1.0", - "sealed", + "sealed 0.5.0", "serde", "serde_json", "slotmap", @@ -1601,6 +1601,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", + "sealed 0.6.0", "serde", "sha2", "stageleft", @@ -1903,7 +1904,7 @@ dependencies = [ "cc-traits", "lattices_macro", "ref-cast", - "sealed", + "sealed 0.5.0", "serde", "trybuild", "variadics", @@ -3098,6 +3099,17 @@ dependencies = [ "syn 2.0.75", ] +[[package]] +name = "sealed" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.75", +] + [[package]] name = "semver" version = "0.9.0" @@ -4094,7 +4106,7 @@ name = "variadics" version = "0.0.7" dependencies = [ "hashbrown 0.14.5", - "sealed", + "sealed 0.5.0", "trybuild", ] diff --git a/hydroflow_plus/Cargo.toml b/hydroflow_plus/Cargo.toml index b18e2d9b18cd..0023e55415f4 100644 --- a/hydroflow_plus/Cargo.toml +++ b/hydroflow_plus/Cargo.toml @@ -39,6 +39,7 @@ hydro_deploy = { path = "../hydro_deploy/core", version = "^0.10.0", optional = prettyplease = { version = "0.2.0", features = [ "verbatim" ], optional = true } toml = { version = "0.8.0", optional = true } trybuild-internals-api = { version = "1.0.99", optional = true } +sealed = "0.6.0" [build-dependencies] stageleft_tool = { path = "../stageleft_tool", version = "^0.4.0" } diff --git a/hydroflow_plus/src/boundedness.rs b/hydroflow_plus/src/boundedness.rs new file mode 100644 index 000000000000..2a033259d32f --- /dev/null +++ b/hydroflow_plus/src/boundedness.rs @@ -0,0 +1,7 @@ +/// Marks the stream as being unbounded, which means that it is not +/// guaranteed to be complete in finite time. +pub enum Unbounded {} + +/// Marks the stream as being bounded, which means that it is guaranteed +/// to be complete in finite time. +pub enum Bounded {} diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index c4aaeb977f5e..3a0161107214 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -13,8 +13,11 @@ pub mod runtime_support { pub mod runtime_context; pub use runtime_context::RuntimeContext; +pub mod boundedness; +pub use boundedness::{Bounded, Unbounded}; + pub mod stream; -pub use stream::{Bounded, Stream, Unbounded}; +pub use stream::Stream; pub mod singleton; pub use singleton::Singleton; diff --git a/hydroflow_plus/src/location/can_send.rs b/hydroflow_plus/src/location/can_send.rs index ddbec2719872..b88bbfade478 100644 --- a/hydroflow_plus/src/location/can_send.rs +++ b/hydroflow_plus/src/location/can_send.rs @@ -1,11 +1,16 @@ use stageleft::quote_type; use super::{Cluster, ClusterId, ExternalProcess, Location, Process}; +use crate::stream::NoOrder; pub trait CanSend<'a, To: Location<'a>>: Location<'a> { type In; type Out; + /// Given the ordering guarantees of the input, determines the strongest possible + /// ordering guarantees of the output. + type OutStrongestOrder; + fn is_demux() -> bool; fn tagged_type() -> Option; } @@ -13,6 +18,7 @@ pub trait CanSend<'a, To: Location<'a>>: Location<'a> { impl<'a, P1, P2> CanSend<'a, Process<'a, P2>> for Process<'a, P1> { type In = T; type Out = T; + type OutStrongestOrder = InOrder; fn is_demux() -> bool { false @@ -26,6 +32,7 @@ impl<'a, P1, P2> CanSend<'a, Process<'a, P2>> for Process<'a, P1> { impl<'a, P1, C2> CanSend<'a, Cluster<'a, C2>> for Process<'a, P1> { type In = (ClusterId, T); type Out = T; + type OutStrongestOrder = InOrder; fn is_demux() -> bool { true @@ -39,6 +46,7 @@ impl<'a, P1, C2> CanSend<'a, Cluster<'a, C2>> for Process<'a, P1> { impl<'a, C1, P2> CanSend<'a, Process<'a, P2>> for Cluster<'a, C1> { type In = T; type Out = (ClusterId, T); + type OutStrongestOrder = NoOrder; fn is_demux() -> bool { false @@ -52,6 +60,7 @@ impl<'a, C1, P2> CanSend<'a, Process<'a, P2>> for Cluster<'a, C1> { impl<'a, C1, C2> CanSend<'a, Cluster<'a, C2>> for Cluster<'a, C1> { type In = (ClusterId, T); type Out = (ClusterId, T); + type OutStrongestOrder = NoOrder; fn is_demux() -> bool { true @@ -65,6 +74,7 @@ impl<'a, C1, C2> CanSend<'a, Cluster<'a, C2>> for Cluster<'a, C1> { impl<'a, P1, E2> CanSend<'a, ExternalProcess<'a, E2>> for Process<'a, P1> { type In = T; type Out = T; + type OutStrongestOrder = InOrder; fn is_demux() -> bool { false diff --git a/hydroflow_plus/src/location/cluster.rs b/hydroflow_plus/src/location/cluster.rs index 55f1d9f14168..695bce5d875a 100644 --- a/hydroflow_plus/src/location/cluster.rs +++ b/hydroflow_plus/src/location/cluster.rs @@ -123,18 +123,6 @@ impl PartialEq for ClusterId { impl Eq for ClusterId {} -impl PartialOrd for ClusterId { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for ClusterId { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.raw_id.cmp(&other.raw_id) - } -} - impl Hash for ClusterId { fn hash(&self, state: &mut H) { self.raw_id.hash(state) diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 834eef5aea28..35dcdcb08377 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -12,8 +12,7 @@ use crate::cycle::{ }; use crate::ir::{HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::{check_matching_location, Location, LocationId, NoTick, Tick}; -use crate::stream::{Bounded, Unbounded}; -use crate::{Optional, Stream}; +use crate::{Bounded, Optional, Stream, Unbounded}; pub struct Singleton { pub(crate) location: L, diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 550afa52a37b..0ec265a48f5d 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use std::rc::Rc; use hydroflow::bytes::Bytes; -use hydroflow::futures::Sink; +use hydroflow::futures; use hydroflow_lang::parse::Pipeline; use serde::de::DeserializeOwned; use serde::Serialize; @@ -21,15 +21,41 @@ use crate::location::{ check_matching_location, CanSend, ExternalProcess, Location, LocationId, NoTick, Tick, }; use crate::staging_util::get_this_crate; -use crate::{Cluster, ClusterId, Optional, Process, Singleton}; +use crate::{Bounded, Cluster, ClusterId, Optional, Process, Singleton, Unbounded}; -/// Marks the stream as being unbounded, which means that it is not -/// guaranteed to be complete in finite time. -pub enum Unbounded {} +/// Marks the stream as being totally ordered, which means that there are +/// no sources of non-determinism (other than intentional ones) that will +/// affect the order of elements. +pub struct TotalOrder {} -/// Marks the stream as being bounded, which means that it is guaranteed -/// to be complete in finite time. -pub enum Bounded {} +/// Marks the stream as having no order, which means that the order of +/// elements may be affected by non-determinism. +/// +/// This restricts certain operators, such as `fold` and `reduce`, to only +/// be used with commutative aggregation functions. +pub struct NoOrder {} + +/// Helper trait for determining the weakest of two orderings. +#[sealed::sealed] +pub trait MinOrder { + /// The weaker of the two orderings. + type Min; +} + +#[sealed::sealed] +impl MinOrder for T { + type Min = T; +} + +#[sealed::sealed] +impl MinOrder for TotalOrder { + type Min = NoOrder; +} + +#[sealed::sealed] +impl MinOrder for NoOrder { + type Min = NoOrder; +} /// An ordered sequence stream of elements of type `T`. /// @@ -37,27 +63,31 @@ pub enum Bounded {} /// - `T`: the type of elements in the stream /// - `L`: the location where the stream is being materialized /// - `B`: the boundedness of the stream, which is either [`Bounded`] -/// or [`Unbounded`] -pub struct Stream { +/// or [`Unbounded`] +/// - `Order`: the ordering of the stream, which is either [`TotalOrder`] +/// or [`NoOrder`] (default is [`TotalOrder`]) +pub struct Stream { location: L, pub(crate) ir_node: RefCell, - _phantom: PhantomData<(T, L, B)>, + _phantom: PhantomData<(T, L, B, Order)>, } -impl<'a, T, L: Location<'a>, B> Stream { +impl<'a, T, L: Location<'a>, B, Order> Stream { fn location_kind(&self) -> LocationId { self.location.id() } } -impl<'a, T, L: Location<'a>> DeferTick for Stream, Bounded> { +impl<'a, T, L: Location<'a>, Order> DeferTick for Stream, Bounded, Order> { fn defer_tick(self) -> Self { Stream::defer_tick(self) } } -impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycleMarker> for Stream, Bounded> { +impl<'a, T, L: Location<'a>, Order> CycleCollection<'a, TickCycleMarker> + for Stream, Bounded, Order> +{ type Location = Tick; fn create_source(ident: syn::Ident, location: Tick) -> Self { @@ -72,7 +102,9 @@ impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycleMarker> for Stream> CycleComplete<'a, TickCycleMarker> for Stream, Bounded> { +impl<'a, T, L: Location<'a>, Order> CycleComplete<'a, TickCycleMarker> + for Stream, Bounded, Order> +{ fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -88,7 +120,9 @@ impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycleMarker> for Stream + NoTick, B> CycleCollection<'a, ForwardRefMarker> for Stream { +impl<'a, T, L: Location<'a> + NoTick, B, Order> CycleCollection<'a, ForwardRefMarker> + for Stream +{ type Location = L; fn create_source(ident: syn::Ident, location: L) -> Self { @@ -103,7 +137,9 @@ impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRefMarker> f } } -impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRefMarker> for Stream { +impl<'a, T, L: Location<'a> + NoTick, B, Order> CycleComplete<'a, ForwardRefMarker> + for Stream +{ fn complete(self, ident: syn::Ident) { self.location .flow_state() @@ -119,7 +155,7 @@ impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRefMarker> for } } -impl<'a, T, L: Location<'a>, B> Stream { +impl<'a, T, L: Location<'a>, B, Order> Stream { pub(crate) fn new(location: L, ir_node: HfPlusNode) -> Self { Stream { location, @@ -129,7 +165,7 @@ impl<'a, T, L: Location<'a>, B> Stream { } } -impl<'a, T: Clone, L: Location<'a>, B> Clone for Stream { +impl<'a, T: Clone, L: Location<'a>, B, Order> Clone for Stream { fn clone(&self) -> Self { if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { let orig_ir_node = self.ir_node.replace(HfPlusNode::Placeholder); @@ -153,8 +189,11 @@ impl<'a, T: Clone, L: Location<'a>, B> Clone for Stream { } } -impl<'a, T, L: Location<'a>, B> Stream { - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { +impl<'a, T, L: Location<'a>, B, Order> Stream { + pub fn map U + 'a>( + self, + f: impl IntoQuotedMut<'a, F>, + ) -> Stream { Stream::new( self.location, HfPlusNode::Map { @@ -164,7 +203,7 @@ impl<'a, T, L: Location<'a>, B> Stream { ) } - pub fn cloned(self) -> Stream + pub fn cloned(self) -> Stream where T: Clone, { @@ -174,7 +213,7 @@ impl<'a, T, L: Location<'a>, B> Stream { pub fn flat_map, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location, HfPlusNode::FlatMap { @@ -184,14 +223,17 @@ impl<'a, T, L: Location<'a>, B> Stream { ) } - pub fn flatten(self) -> Stream + pub fn flatten(self) -> Stream where T: IntoIterator, { self.flat_map(q!(|d| d)) } - pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Stream { + pub fn filter bool + 'a>( + self, + f: impl IntoQuotedMut<'a, F>, + ) -> Stream { Stream::new( self.location, HfPlusNode::Filter { @@ -204,7 +246,7 @@ impl<'a, T, L: Location<'a>, B> Stream { pub fn filter_map Option + 'a>( self, f: impl IntoQuotedMut<'a, F>, - ) -> Stream { + ) -> Stream { Stream::new( self.location, HfPlusNode::FilterMap { @@ -217,7 +259,7 @@ impl<'a, T, L: Location<'a>, B> Stream { pub fn cross_singleton( self, other: impl Into>, - ) -> Stream<(T, O), L, B> + ) -> Stream<(T, O), L, B, Order> where O: Clone, { @@ -234,17 +276,17 @@ impl<'a, T, L: Location<'a>, B> Stream { } /// Allow this stream through if the other stream has elements, otherwise the output is empty. - pub fn continue_if(self, signal: Optional) -> Stream { + pub fn continue_if(self, signal: Optional) -> Stream { self.cross_singleton(signal.map(q!(|_u| ()))) .map(q!(|(d, _signal)| d)) } /// Allow this stream through if the other stream is empty, otherwise the output is empty. - pub fn continue_unless(self, other: Optional) -> Stream { + pub fn continue_unless(self, other: Optional) -> Stream { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } - pub fn cross_product(self, other: Stream) -> Stream<(T, O), L, B> + pub fn cross_product(self, other: Stream) -> Stream<(T, O), L, B, Order> where T: Clone, O: Clone, @@ -260,27 +302,7 @@ impl<'a, T, L: Location<'a>, B> Stream { ) } - pub fn enumerate(self) -> Stream<(usize, T), L, B> { - if L::is_top_level() { - Stream::new( - self.location, - HfPlusNode::Persist(Box::new(HfPlusNode::Enumerate { - is_static: true, - input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - })), - ) - } else { - Stream::new( - self.location, - HfPlusNode::Enumerate { - is_static: false, - input: Box::new(self.ir_node.into_inner()), - }, - ) - } - } - - pub fn unique(self) -> Stream + pub fn unique(self) -> Stream where T: Eq + Hash, { @@ -290,7 +312,7 @@ impl<'a, T, L: Location<'a>, B> Stream { ) } - pub fn filter_not_in(self, other: Stream) -> Stream + pub fn filter_not_in(self, other: Stream) -> Stream where T: Eq + Hash, { @@ -305,11 +327,7 @@ impl<'a, T, L: Location<'a>, B> Stream { ) } - pub fn first(self) -> Optional { - Optional::new(self.location, self.ir_node.into_inner()) - } - - pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { + pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { if L::is_top_level() { Stream::new( self.location, @@ -329,7 +347,16 @@ impl<'a, T, L: Location<'a>, B> Stream { } } - pub fn fold A + 'a, F: Fn(&mut A, T)>( + pub fn assume_ordering(self) -> Stream { + Stream::new(self.location, self.ir_node.into_inner()) + } +} + +impl<'a, T, L: Location<'a>, B, Order> Stream +where + Order: MinOrder, +{ + pub fn fold_commutative A + 'a, F: Fn(&mut A, T)>( self, init: impl IntoQuotedMut<'a, I>, comb: impl IntoQuotedMut<'a, F>, @@ -350,7 +377,7 @@ impl<'a, T, L: Location<'a>, B> Stream { Singleton::new(self.location, core) } - pub fn reduce( + pub fn reduce_commutative( self, comb: impl IntoQuotedMut<'a, F>, ) -> Optional { @@ -370,7 +397,7 @@ impl<'a, T, L: Location<'a>, B> Stream { where T: Ord, { - self.reduce(q!(|curr, new| { + self.reduce_commutative(q!(|curr, new| { if new > *curr { *curr = new; } @@ -381,7 +408,7 @@ impl<'a, T, L: Location<'a>, B> Stream { where T: Ord, { - self.reduce(q!(|curr, new| { + self.reduce_commutative(q!(|curr, new| { if new < *curr { *curr = new; } @@ -389,12 +416,79 @@ impl<'a, T, L: Location<'a>, B> Stream { } pub fn count(self) -> Singleton { - self.fold(q!(|| 0usize), q!(|count, _| *count += 1)) + self.fold_commutative(q!(|| 0usize), q!(|count, _| *count += 1)) } } -impl<'a, T, L: Location<'a>> Stream { - pub fn sort(self) -> Stream +impl<'a, T, L: Location<'a>, B> Stream { + pub fn enumerate(self) -> Stream<(usize, T), L, B, TotalOrder> { + if L::is_top_level() { + Stream::new( + self.location, + HfPlusNode::Persist(Box::new(HfPlusNode::Enumerate { + is_static: true, + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + })), + ) + } else { + Stream::new( + self.location, + HfPlusNode::Enumerate { + is_static: false, + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + } + + pub fn first(self) -> Optional { + Optional::new(self.location, self.ir_node.into_inner()) + } + + pub fn last(self) -> Optional { + self.reduce(q!(|curr, new| *curr = new)) + } + + pub fn fold A + 'a, F: Fn(&mut A, T)>( + self, + init: impl IntoQuotedMut<'a, I>, + comb: impl IntoQuotedMut<'a, F>, + ) -> Singleton { + let mut core = HfPlusNode::Fold { + init: init.splice_fn0().into(), + acc: comb.splice_fn2_borrow_mut().into(), + input: Box::new(self.ir_node.into_inner()), + }; + + if L::is_top_level() { + // top-level (possibly unbounded) singletons are represented as + // a stream which produces all values from all ticks every tick, + // so Unpersist will always give the lastest aggregation + core = HfPlusNode::Persist(Box::new(core)); + } + + Singleton::new(self.location, core) + } + + pub fn reduce( + self, + comb: impl IntoQuotedMut<'a, F>, + ) -> Optional { + let mut core = HfPlusNode::Reduce { + f: comb.splice_fn2_borrow_mut().into(), + input: Box::new(self.ir_node.into_inner()), + }; + + if L::is_top_level() { + core = HfPlusNode::Persist(Box::new(core)); + } + + Optional::new(self.location, core) + } +} + +impl<'a, T, L: Location<'a>, Order> Stream { + pub fn sort(self) -> Stream where T: Ord, { @@ -404,7 +498,10 @@ impl<'a, T, L: Location<'a>> Stream { ) } - pub fn chain(self, other: Stream) -> Stream { + pub fn chain(self, other: Stream) -> Stream + where + Order: MinOrder, + { check_matching_location(&self.location, &other.location); Stream::new( @@ -417,8 +514,8 @@ impl<'a, T, L: Location<'a>> Stream { } } -impl<'a, K, V1, L: Location<'a>, B> Stream<(K, V1), L, B> { - pub fn join(self, n: Stream<(K, V2), L, B>) -> Stream<(K, (V1, V2)), L, B> +impl<'a, K, V1, L: Location<'a>, B, Order> Stream<(K, V1), L, B, Order> { + pub fn join(self, n: Stream<(K, V2), L, B, O2>) -> Stream<(K, (V1, V2)), L, B, NoOrder> where K: Eq + Hash, { @@ -433,7 +530,7 @@ impl<'a, K, V1, L: Location<'a>, B> Stream<(K, V1), L, B> { ) } - pub fn anti_join(self, n: Stream) -> Stream<(K, V1), L, B> + pub fn anti_join(self, n: Stream) -> Stream<(K, V1), L, B, Order> where K: Eq + Hash, { @@ -479,15 +576,48 @@ impl<'a, K: Eq + Hash, V, L: Location<'a>> Stream<(K, V), Tick, Bounded> { } } -impl<'a, T, L: Location<'a> + NoTick, B> Stream { - pub fn tick_batch(self, tick: &Tick) -> Stream, Bounded> { +impl<'a, K: Eq + Hash, V, L: Location<'a>, Order> Stream<(K, V), Tick, Bounded, Order> +where + Order: MinOrder, +{ + pub fn fold_keyed_commutative A + 'a, F: Fn(&mut A, V) + 'a>( + self, + init: impl IntoQuotedMut<'a, I>, + comb: impl IntoQuotedMut<'a, F>, + ) -> Stream<(K, A), Tick, Bounded, Order> { + Stream::new( + self.location, + HfPlusNode::FoldKeyed { + init: init.splice_fn0().into(), + acc: comb.splice_fn2_borrow_mut().into(), + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + + pub fn reduce_keyed_commutative( + self, + comb: impl IntoQuotedMut<'a, F>, + ) -> Stream<(K, V), Tick, Bounded, Order> { + Stream::new( + self.location, + HfPlusNode::ReduceKeyed { + f: comb.splice_fn2_borrow_mut().into(), + input: Box::new(self.ir_node.into_inner()), + }, + ) + } +} + +impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { + pub fn tick_batch(self, tick: &Tick) -> Stream, Bounded, Order> { Stream::new( tick.clone(), HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } - pub fn tick_prefix(self, tick: &Tick) -> Stream, Bounded> + pub fn tick_prefix(self, tick: &Tick) -> Stream, Bounded, Order> where T: Clone, { @@ -497,7 +627,7 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { pub fn sample_every( self, interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, - ) -> Stream { + ) -> Stream { let samples = self.location.source_interval(interval); let tick = self.location.tick(); self.tick_batch(&tick) @@ -518,7 +648,7 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { }); } - pub fn dest_sink + 'a>(self, sink: impl Quoted<'a, S>) { + pub fn dest_sink + 'a>(self, sink: impl Quoted<'a, S>) { self.location .flow_state() .borrow_mut() @@ -532,15 +662,15 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { } } -impl<'a, T, L: Location<'a>> Stream, Bounded> { - pub fn all_ticks(self) -> Stream { +impl<'a, T, L: Location<'a>, Order> Stream, Bounded, Order> { + pub fn all_ticks(self) -> Stream { Stream::new( self.location.outer().clone(), HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn persist(self) -> Stream, Bounded> + pub fn persist(self) -> Stream, Bounded, Order> where T: Clone, { @@ -550,14 +680,14 @@ impl<'a, T, L: Location<'a>> Stream, Bounded> { ) } - pub fn defer_tick(self) -> Stream, Bounded> { + pub fn defer_tick(self) -> Stream, Bounded, Order> { Stream::new( self.location, HfPlusNode::DeferTick(Box::new(self.ir_node.into_inner())), ) } - pub fn delta(self) -> Stream, Bounded> { + pub fn delta(self) -> Stream, Bounded, Order> { Stream::new( self.location, HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), @@ -606,14 +736,15 @@ pub(super) fn deserialize_bincode(tagged: Option } } -impl<'a, T, L: Location<'a> + NoTick, B> Stream { +impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { pub fn decouple_process( self, other: &Process<'a, P2>, - ) -> Stream, Unbounded> + ) -> Stream, Unbounded, Order> where L: CanSend<'a, Process<'a, P2>, In = T, Out = T>, T: Clone + Serialize + DeserializeOwned, + Order: MinOrder, Min = Order>, { self.send_bincode::, T>(other) } @@ -621,10 +752,11 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { pub fn decouple_cluster( self, other: &Cluster<'a, C2>, - ) -> Stream, Unbounded> + ) -> Stream, Unbounded, Order> where L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)>, T: Clone + Serialize + DeserializeOwned, + Order: MinOrder>, { let self_node_id = match self.location_kind() { LocationId::Cluster(cluster_id) => ClusterSelfId { @@ -636,15 +768,17 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { self.map(q!(move |b| (self_node_id, b.clone()))) .send_bincode_interleaved(other) + .assume_ordering() // this is safe because we are mapping clusters 1:1 } pub fn send_bincode, CoreType>( self, other: &L2, - ) -> Stream, L2, Unbounded> + ) -> Stream, L2, Unbounded, Order::Min> where L: CanSend<'a, L2, In = T>, CoreType: Serialize + DeserializeOwned, + Order: MinOrder>, { let serialize_pipeline = Some(serialize_bincode::(L::is_demux())); @@ -706,9 +840,13 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { } } - pub fn send_bytes>(self, other: &L2) -> Stream, L2, Unbounded> + pub fn send_bytes>( + self, + other: &L2, + ) -> Stream, L2, Unbounded, Order::Min> where L: CanSend<'a, L2, In = T>, + Order: MinOrder>, { let root = get_this_crate(); Stream::new( @@ -767,10 +905,11 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { pub fn send_bincode_interleaved, Tag, CoreType>( self, other: &L2, - ) -> Stream + ) -> Stream where L: CanSend<'a, L2, In = T, Out = (Tag, CoreType)>, CoreType: Serialize + DeserializeOwned, + Order: MinOrder>, { self.send_bincode::(other).map(q!(|(_, b)| b)) } @@ -778,9 +917,10 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { pub fn send_bytes_interleaved, Tag>( self, other: &L2, - ) -> Stream + ) -> Stream where L: CanSend<'a, L2, In = T, Out = (Tag, Bytes)>, + Order: MinOrder>, { self.send_bytes::(other).map(q!(|(_, b)| b)) } @@ -788,10 +928,11 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { pub fn broadcast_bincode( self, other: &Cluster<'a, C2>, - ) -> Stream, Cluster<'a, C2>, Unbounded> + ) -> Stream, Cluster<'a, C2>, Unbounded, Order::Min> where L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, T: Clone + Serialize + DeserializeOwned, + Order: MinOrder>, { let ids = other.members(); @@ -802,50 +943,26 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { .send_bincode(other) } - pub fn round_robin_bincode( - self, - other: &Cluster<'a, C2>, - ) -> Stream, Cluster<'a, C2>, Unbounded> - where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, - T: Clone + Serialize + DeserializeOwned, - { - let ids = other.members(); - - self.enumerate() - .map(q!(|(i, w)| (ids[i % ids.len()], w))) - .send_bincode(other) - } - pub fn broadcast_bincode_interleaved( self, other: &Cluster<'a, C2>, - ) -> Stream, Unbounded> + ) -> Stream, Unbounded, Order::Min> where L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, T: Clone + Serialize + DeserializeOwned, + Order: MinOrder>, { self.broadcast_bincode(other).map(q!(|(_, b)| b)) } - pub fn round_robin_bincode_interleaved( - self, - other: &Cluster<'a, C2>, - ) -> Stream, Unbounded> - where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, - T: Clone + Serialize + DeserializeOwned, - { - self.round_robin_bincode(other).map(q!(|(_, b)| b)) - } - pub fn broadcast_bytes( self, other: &Cluster<'a, C2>, - ) -> Stream, Cluster<'a, C2>, Unbounded> + ) -> Stream, Cluster<'a, C2>, Unbounded, Order::Min> where L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, T: Clone, + Order: MinOrder>, { let ids = other.members(); @@ -856,41 +973,95 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { .send_bytes(other) } - pub fn round_robin_bytes( + pub fn broadcast_bytes_interleaved( self, other: &Cluster<'a, C2>, - ) -> Stream, Cluster<'a, C2>, Unbounded> + ) -> Stream, Unbounded, Order::Min> where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + + 'a, T: Clone, + Order: MinOrder>, + { + self.broadcast_bytes(other).map(q!(|(_, b)| b)) + } +} + +#[expect(clippy::type_complexity, reason = "ordering semantics for round-robin")] +impl<'a, T, L: Location<'a> + NoTick, B> Stream { + pub fn round_robin_bincode( + self, + other: &Cluster<'a, C2>, + ) -> Stream< + L::Out, + Cluster<'a, C2>, + Unbounded, + >>::Min, + > + where + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, + T: Clone + Serialize + DeserializeOwned, + TotalOrder: MinOrder>, { let ids = other.members(); self.enumerate() .map(q!(|(i, w)| (ids[i % ids.len()], w))) - .send_bytes(other) + .send_bincode(other) } - pub fn broadcast_bytes_interleaved( + pub fn round_robin_bincode_interleaved( self, other: &Cluster<'a, C2>, - ) -> Stream, Unbounded> + ) -> Stream< + T, + Cluster<'a, C2>, + Unbounded, + >>::Min, + > where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> - + 'a, + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, + T: Clone + Serialize + DeserializeOwned, + TotalOrder: MinOrder>, + { + self.round_robin_bincode(other).map(q!(|(_, b)| b)) + } + + pub fn round_robin_bytes( + self, + other: &Cluster<'a, C2>, + ) -> Stream< + L::Out, + Cluster<'a, C2>, + Unbounded, + >>::Min, + > + where + L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, T: Clone, + TotalOrder: MinOrder>, { - self.broadcast_bytes(other).map(q!(|(_, b)| b)) + let ids = other.members(); + + self.enumerate() + .map(q!(|(i, w)| (ids[i % ids.len()], w))) + .send_bytes(other) } pub fn round_robin_bytes_interleaved( self, other: &Cluster<'a, C2>, - ) -> Stream, Unbounded> + ) -> Stream< + Bytes, + Cluster<'a, C2>, + Unbounded, + >>::Min, + > where L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + 'a, T: Clone, + TotalOrder: MinOrder>, { self.round_robin_bytes(other).map(q!(|(_, b)| b)) } diff --git a/hydroflow_plus_test/src/cluster/compute_pi.rs b/hydroflow_plus_test/src/cluster/compute_pi.rs index 6bfdbda2764c..00bf90b5348a 100644 --- a/hydroflow_plus_test/src/cluster/compute_pi.rs +++ b/hydroflow_plus_test/src/cluster/compute_pi.rs @@ -31,7 +31,7 @@ pub fn compute_pi<'a>( trials .send_bincode_interleaved(&process) - .reduce(q!(|(inside, total), (inside_batch, total_batch)| { + .reduce_commutative(q!(|(inside, total), (inside_batch, total_batch)| { *inside += inside_batch; *total += total_batch; })) diff --git a/hydroflow_plus_test/src/cluster/map_reduce.rs b/hydroflow_plus_test/src/cluster/map_reduce.rs index 97173bf45578..a46e1d0d401a 100644 --- a/hydroflow_plus_test/src/cluster/map_reduce.rs +++ b/hydroflow_plus_test/src/cluster/map_reduce.rs @@ -24,7 +24,7 @@ pub fn map_reduce<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, Leader>, Cluster<' .send_bincode_interleaved(&process) .tick_batch(&process.tick()) .persist() - .reduce_keyed(q!(|total, count| *total += count)) + .reduce_keyed_commutative(q!(|total, count| *total += count)) .all_ticks() .for_each(q!(|(string, count)| println!("{}: {}", string, count))); diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index c2aa8ce4a882..f575ee1c3f13 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -5,6 +5,7 @@ use std::time::Duration; use hydroflow_plus::*; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; +use stream::{NoOrder, TotalOrder}; use tokio::time::Instant; pub struct Proposer {} @@ -13,13 +14,26 @@ pub struct Acceptor {} pub trait PaxosPayload: Serialize + DeserializeOwned + PartialEq + Eq + Clone + Debug {} impl PaxosPayload for T {} -#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Hash)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] pub struct Ballot { - // Note: Important that num comes before id, since Ord is defined lexicographically pub num: u32, pub proposer_id: ClusterId, } +impl Ord for Ballot { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.num + .cmp(&other.num) + .then_with(|| self.proposer_id.raw_id.cmp(&other.proposer_id.raw_id)) + } +} + +impl PartialOrd for Ballot { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + #[derive(Serialize, Deserialize, Clone, Debug)] struct P1a { ballot: Ballot, @@ -61,15 +75,20 @@ struct P2b

    { pub fn paxos_core<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, - r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Cluster<'a, Acceptor>, Unbounded>, + r_to_acceptors_checkpoint: Stream< + (ClusterId, usize), + Cluster<'a, Acceptor>, + Unbounded, + NoOrder, + >, c_to_proposers: Stream, Unbounded>, f: usize, i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, ) -> ( - Stream<(), Cluster<'a, Proposer>, Unbounded>, - Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded>, + Stream, Unbounded>, + Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded, NoOrder>, ) { proposers .source_iter(q!(["Proposers say hello"])) @@ -83,12 +102,12 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( let acceptor_tick = acceptors.tick(); let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b_forward_reference) = - proposers.forward_ref::, _, _>>(); + proposers.forward_ref::, _, _, NoOrder>>(); let (a_log_complete_cycle, a_log_forward_reference) = acceptor_tick .forward_ref::, HashMap>), _, _>>(); - let (p_ballot_num, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( + let (p_ballot, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( proposers, acceptors, &proposer_tick, @@ -108,11 +127,11 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( // Tell clients that leader election has completed and they can begin sending messages let p_to_clients_new_leader_elected = just_became_leader .clone() - .map(q!(move |_| ())) // Only tell the clients once when leader election concludes + .then(p_ballot.clone()) // Only tell the clients once when leader election concludes .all_ticks(); let (p_log_to_try_commit, p_max_slot, p_log_holes) = - recommit_after_leader_election(proposers, p_relevant_p1bs, p_ballot_num.clone(), f); + recommit_after_leader_election(p_relevant_p1bs, p_ballot.clone(), f); let p_log_to_recommit = p_log_to_try_commit .chain(p_log_holes) @@ -125,7 +144,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( &acceptor_tick, c_to_proposers, r_to_acceptors_checkpoint, - p_ballot_num, + p_ballot, p_is_leader, p_max_slot, p_log_to_recommit, @@ -153,18 +172,18 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( i_am_leader_send_timeout: u64, i_am_leader_check_timeout: u64, i_am_leader_check_timeout_delay_multiplier: usize, - p_received_p2b_ballots: Stream, Unbounded>, + p_received_p2b_ballots: Stream, Unbounded, NoOrder>, a_log: Singleton>, Bounded>, ) -> ( - Singleton>, Bounded>, + Singleton>, Bounded>, Optional>, Bounded>, - Stream, Tick>, Bounded>, + Stream, Tick>, Bounded, NoOrder>, Singleton>, Bounded>, ) { let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b_forward_ref) = - proposers.forward_ref::, _, _>>(); + proposers.forward_ref::, _, _, NoOrder>>(); let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader_forward_ref) = - proposers.forward_ref::>(); + proposers.forward_ref::>(); let (p_is_leader_complete_cycle, p_is_leader_forward_ref) = proposer_tick.forward_ref::>(); // a_to_proposers_p2b.clone().for_each(q!(|(_, p2b): (u32, P2b)| println!("Proposer received P2b: {:?}", p2b))); @@ -177,7 +196,7 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( p_received_p2b_ballots, p_to_proposers_i_am_leader_forward_ref, ); - let (p_ballot_num, p_has_largest_ballot) = p_ballot_calc( + let (p_ballot, p_has_largest_ballot) = p_ballot_calc( proposers, proposer_tick, p_received_max_ballot.latest_tick(proposer_tick), @@ -187,7 +206,7 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( proposers, proposer_tick, p_is_leader_forward_ref, - p_ballot_num.clone(), + p_ballot.clone(), i_am_leader_send_timeout, i_am_leader_check_timeout, i_am_leader_check_timeout_delay_multiplier, @@ -195,36 +214,30 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( p_to_proposers_i_am_leader_complete_cycle.complete(p_to_proposers_i_am_leader); - let p_to_acceptors_p1a = p_p1a( - p_ballot_num.clone(), - p_trigger_election, - proposers, - acceptors, - ); + let p_to_acceptors_p1a = p_p1a(p_ballot.clone(), p_trigger_election, acceptors); let (a_max_ballot, a_to_proposers_p1b) = acceptor_p1(acceptor_tick, p_to_acceptors_p1a, a_log, proposers); a_to_proposers_p1b_complete_cycle.complete(a_to_proposers_p1b.clone()); let (p_is_leader, p_relevant_p1bs) = p_p1b( - proposers, proposer_tick, a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), - p_ballot_num.clone(), + p_ballot.clone(), p_has_largest_ballot, f, ); p_is_leader_complete_cycle.complete(p_is_leader.clone()); - (p_ballot_num, p_is_leader, p_relevant_p1bs, a_max_ballot) + (p_ballot, p_is_leader, p_relevant_p1bs, a_max_ballot) } // Proposer logic to calculate the largest ballot received so far. fn p_max_ballot<'a>( proposers: &Cluster<'a, Proposer>, - p_received_p1b_ballots: Stream, Unbounded>, - p_received_p2b_ballots: Stream, Unbounded>, - p_to_proposers_i_am_leader: Stream, Unbounded>, + p_received_p1b_ballots: Stream, Unbounded, NoOrder>, + p_received_p2b_ballots: Stream, Unbounded, NoOrder>, + p_to_proposers_i_am_leader: Stream, Unbounded, NoOrder>, ) -> Singleton, Unbounded> { let ballot_batcher = proposers.tick(); p_received_p1b_ballots @@ -246,8 +259,8 @@ fn p_ballot_calc<'a>( proposer_tick: &Tick>, p_received_max_ballot: Singleton>, Bounded>, ) -> ( - Singleton>, Bounded>, - Optional<(Ballot, u32), Tick>, Bounded>, + Singleton>, Bounded>, + Optional<(), Tick>, Bounded>, ) { let p_id = proposers.self_id(); let (p_ballot_num_complete_cycle, p_ballot_num) = @@ -270,28 +283,30 @@ fn p_ballot_calc<'a>( })); p_ballot_num_complete_cycle.complete_next_tick(p_new_ballot_num); + let p_ballot = p_ballot_num.clone().map(q!(move |num| Ballot { + num, + proposer_id: p_id + })); + let p_has_largest_ballot = p_received_max_ballot .clone() - .zip(p_ballot_num.clone()) + .zip(p_ballot.clone()) .filter(q!( - move |(received_max_ballot, ballot_num)| *received_max_ballot - <= Ballot { - num: *ballot_num, - proposer_id: p_id - } - )); + |(received_max_ballot, cur_ballot)| *received_max_ballot <= *cur_ballot + )) + .map(q!(|_| ())); // End stable leader election - (p_ballot_num, p_has_largest_ballot) + (p_ballot, p_has_largest_ballot) } fn p_leader_expired<'a>( proposer_tick: &Tick>, - p_to_proposers_i_am_leader: Stream, Unbounded>, + p_to_proposers_i_am_leader: Stream, Unbounded, NoOrder>, p_is_leader: Optional>, Bounded>, i_am_leader_check_timeout: u64, // How often to check if heartbeat expired ) -> Optional, Tick>, Bounded> { - let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold( + let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold_commutative( q!(|| None), q!(|latest, _| { // Note: May want to check received ballot against our own? @@ -317,24 +332,20 @@ fn p_leader_heartbeat<'a>( proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, p_is_leader: Optional>, Bounded>, - p_ballot_num: Singleton>, Bounded>, + p_ballot: Singleton>, Bounded>, i_am_leader_send_timeout: u64, // How often to heartbeat i_am_leader_check_timeout: u64, // How often to check if heartbeat expired i_am_leader_check_timeout_delay_multiplier: usize, /* Initial delay, multiplied by proposer pid, to stagger proposers checking for timeouts */ ) -> ( - Stream, Unbounded>, + Stream, Unbounded, NoOrder>, Optional, Tick>, Bounded>, ) { let p_id = proposers.self_id(); let p_to_proposers_i_am_leader = p_is_leader .clone() - .then(p_ballot_num) + .then(p_ballot) .latest() .sample_every(q!(Duration::from_secs(i_am_leader_send_timeout))) - .map(q!(move |ballot_num| Ballot { - num: ballot_num, - proposer_id: p_id - })) .broadcast_bincode_interleaved(proposers); let p_leader_expired = p_leader_expired( @@ -361,21 +372,13 @@ fn p_leader_heartbeat<'a>( // Proposer logic to send "I am leader" messages periodically to other proposers, or send p1a to acceptors if other leaders expired. fn p_p1a<'a>( - p_ballot_num: Singleton>, Bounded>, + p_ballot: Singleton>, Bounded>, p_trigger_election: Optional, Tick>, Bounded>, - proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, -) -> Stream, Unbounded> { - let p_id = proposers.self_id(); - +) -> Stream, Unbounded, NoOrder> { p_trigger_election - .then(p_ballot_num) - .map(q!(move |ballot_num| P1a { - ballot: Ballot { - num: ballot_num, - proposer_id: p_id - } - })) + .then(p_ballot) + .map(q!(|ballot| P1a { ballot })) .all_ticks() .inspect(q!(|_| println!("Proposer leader expired, sending P1a"))) .broadcast_bincode_interleaved(acceptors) @@ -384,12 +387,12 @@ fn p_p1a<'a>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( acceptor_tick: &Tick>, - p_to_acceptors_p1a: Stream, Unbounded>, + p_to_acceptors_p1a: Stream, Unbounded, NoOrder>, a_log: Singleton>, Bounded>, proposers: &Cluster<'a, Proposer>, ) -> ( Singleton>, Bounded>, - Stream, Cluster<'a, Proposer>, Unbounded>, + Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, ) { let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(acceptor_tick); let a_max_ballot = p_to_acceptors_p1a @@ -424,23 +427,21 @@ fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( // Proposer logic for processing p1bs, determining if the proposer is now the leader, which uncommitted messages to commit, what the maximum slot is in the p1bs, and which no-ops to commit to fill log holes. #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( - proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, - a_to_proposers_p1b: Stream, Cluster<'a, Proposer>, Unbounded>, - p_ballot_num: Singleton>, Bounded>, - p_has_largest_ballot: Optional<(Ballot, u32), Tick>, Bounded>, + a_to_proposers_p1b: Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, + p_ballot: Singleton>, Bounded>, + p_has_largest_ballot: Optional<(), Tick>, Bounded>, f: usize, ) -> ( Optional>, Bounded>, - Stream, Tick>, Bounded>, + Stream, Tick>, Bounded, NoOrder>, ) { - let p_id = proposers.self_id(); let p_relevant_p1bs = a_to_proposers_p1b .tick_prefix(proposer_tick) - // NOTE: because `p_ballot_num` grows monotonically across ticks, we could garbage gollect + // NOTE: because `p_ballot` grows monotonically across ticks, we could garbage gollect // but we don't do that here since leader election is a rare event - .cross_singleton(p_ballot_num.clone()) - .filter(q!(move |(p1b, ballot_num)| p1b.ballot.num == *ballot_num && p1b.ballot.proposer_id == p_id)) + .cross_singleton(p_ballot.clone()) + .filter(q!(|(p1b, ballot)| p1b.ballot == *ballot)) .map(q!(|t| t.0)); let p_received_quorum_of_p1bs = p_relevant_p1bs @@ -458,20 +459,22 @@ fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn recommit_after_leader_election<'a, P: PaxosPayload>( - proposers: &Cluster<'a, Proposer>, - p_relevant_p1bs: Stream>>, Tick>, Bounded>, - p_ballot_num: Singleton>, Bounded>, + p_relevant_p1bs: Stream< + P1b>>, + Tick>, + Bounded, + NoOrder, + >, + p_ballot: Singleton>, Bounded>, f: usize, ) -> ( - Stream, Tick>, Bounded>, + Stream, Tick>, Bounded, NoOrder>, Optional>, Bounded>, Stream, Tick>, Bounded>, ) { - let p_id = proposers.self_id(); - let p_p1b_highest_entries_and_count = p_relevant_p1bs .flat_map(q!(|p1b| p1b.accepted.into_iter())) // Convert HashMap log back to stream - .fold_keyed::<(usize, Option>), _, _>(q!(|| (0, None)), q!(|curr_entry, new_entry| { + .fold_keyed_commutative::<(usize, Option>), _, _>(q!(|| (0, None)), q!(|curr_entry, new_entry| { if let Some(curr_entry_payload) = &mut curr_entry.1 { let same_values = new_entry.value == curr_entry_payload.value; let higher_ballot = new_entry.ballot > curr_entry_payload.ballot; @@ -494,15 +497,12 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( })); let p_log_to_try_commit = p_p1b_highest_entries_and_count .clone() - .cross_singleton(p_ballot_num.clone()) - .filter_map(q!(move |((slot, (count, entry)), ballot_num)| { + .cross_singleton(p_ballot.clone()) + .filter_map(q!(move |((slot, (count, entry)), ballot)| { let entry = entry.unwrap(); if count <= f { Some(P2a { - ballot: Ballot { - num: ballot_num, - proposer_id: p_id, - }, + ballot, slot, value: entry.value, }) @@ -521,12 +521,9 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( .clone() .flat_map(q!(|max_slot| 0..max_slot)) .filter_not_in(p_proposed_slots) - .cross_singleton(p_ballot_num.clone()) - .map(q!(move |(slot, ballot_num)| P2a { - ballot: Ballot { - num: ballot_num, - proposer_id: p_id - }, + .cross_singleton(p_ballot.clone()) + .map(q!(|(slot, ballot)| P2a { + ballot, slot, value: None })); @@ -544,27 +541,31 @@ fn sequence_payload<'a, P: PaxosPayload, R>( proposer_tick: &Tick>, acceptor_tick: &Tick>, c_to_proposers: Stream, Unbounded>, - r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Cluster<'a, Acceptor>, Unbounded>, - - p_ballot_num: Singleton>, Bounded>, + r_to_acceptors_checkpoint: Stream< + (ClusterId, usize), + Cluster<'a, Acceptor>, + Unbounded, + NoOrder, + >, + + p_ballot: Singleton>, Bounded>, p_is_leader: Optional>, Bounded>, p_max_slot: Optional>, Bounded>, - p_log_to_recommit: Stream, Tick>, Bounded>, + p_log_to_recommit: Stream, Tick>, Bounded, NoOrder>, f: usize, a_max_ballot: Singleton>, Bounded>, ) -> ( - Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded>, + Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded, NoOrder>, Singleton<(Option, HashMap>), Tick>, Bounded>, - Stream, Cluster<'a, Proposer>, Unbounded>, + Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, ) { let p_to_acceptors_p2a = p_p2a( - proposers, proposer_tick, p_max_slot, c_to_proposers, - p_ballot_num.clone(), + p_ballot.clone(), p_log_to_recommit, p_is_leader.clone(), acceptors, @@ -593,18 +594,15 @@ enum CheckpointOrP2a

    { } // Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. -#[expect(clippy::too_many_arguments, reason = "internal paxos code // TODO")] fn p_p2a<'a, P: PaxosPayload>( - proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, p_max_slot: Optional>, Bounded>, c_to_proposers: Stream, Unbounded>, - p_ballot_num: Singleton>, Bounded>, - p_log_to_recommit: Stream, Tick>, Bounded>, + p_ballot: Singleton>, Bounded>, + p_log_to_recommit: Stream, Tick>, Bounded, NoOrder>, p_is_leader: Optional>, Bounded>, acceptors: &Cluster<'a, Acceptor>, -) -> Stream, Cluster<'a, Acceptor>, Unbounded> { - let p_id = proposers.self_id(); +) -> Stream, Cluster<'a, Acceptor>, Unbounded, NoOrder> { let (p_next_slot_complete_cycle, p_next_slot) = proposer_tick.cycle::>(); let p_next_slot_after_reconciling_p1bs = p_max_slot .map(q!(|max_slot| max_slot + 1)) @@ -618,10 +616,10 @@ fn p_p2a<'a, P: PaxosPayload>( .enumerate() .cross_singleton(p_next_slot.clone()) // .inspect(q!(|next| println!("{} p_indexed_payloads next slot: {}", context.current_tick(), next)))) - .cross_singleton(p_ballot_num.clone()) + .cross_singleton(p_ballot.clone()) // .inspect(q!(|ballot_num| println!("{} p_indexed_payloads ballot_num: {}", context.current_tick(), ballot_num)))) - .map(q!(move |(((index, payload), next_slot), ballot_num)| P2a { - ballot: Ballot { num: ballot_num, proposer_id: p_id }, + .map(q!(|(((index, payload), next_slot), ballot)| P2a { + ballot, slot: next_slot + index, value: Some(payload) })); @@ -652,20 +650,25 @@ fn p_p2a<'a, P: PaxosPayload>( fn acceptor_p2<'a, P: PaxosPayload, R>( acceptor_tick: &Tick>, a_max_ballot: Singleton>, Bounded>, - p_to_acceptors_p2a: Stream, Cluster<'a, Acceptor>, Unbounded>, - r_to_acceptors_checkpoint: Stream<(ClusterId, usize), Cluster<'a, Acceptor>, Unbounded>, + p_to_acceptors_p2a: Stream, Cluster<'a, Acceptor>, Unbounded, NoOrder>, + r_to_acceptors_checkpoint: Stream< + (ClusterId, usize), + Cluster<'a, Acceptor>, + Unbounded, + NoOrder, + >, proposers: &Cluster<'a, Proposer>, f: usize, ) -> ( Singleton<(Option, HashMap>), Tick>, Bounded>, - Stream, Cluster<'a, Proposer>, Unbounded>, + Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, ) { let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(acceptor_tick); // Get the latest checkpoint sequence per replica let a_checkpoint_largest_seqs = r_to_acceptors_checkpoint .tick_prefix(acceptor_tick) - .reduce_keyed(q!(|curr_seq, seq| { + .reduce_keyed_commutative(q!(|curr_seq, seq| { if seq > *curr_seq { *curr_seq = seq; } @@ -695,7 +698,8 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( } else { None } - )); + )) + .assume_ordering::(); let a_log = a_p2as_to_place_in_log .chain(a_new_checkpoint.into_stream()) .persist() @@ -749,11 +753,12 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( fn p_p2b<'a, P: PaxosPayload>( proposer_tick: &Tick>, - a_to_proposers_p2b: Stream, Cluster<'a, Proposer>, Unbounded>, + a_to_proposers_p2b: Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, f: usize, -) -> Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded> { +) -> Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded, NoOrder> { let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposer_tick.cycle(); - let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = proposer_tick.cycle(); + let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = + proposer_tick.cycle::>(); let p_p2b = a_to_proposers_p2b .tick_batch(proposer_tick) .chain(p_persisted_p2bs); @@ -765,11 +770,13 @@ fn p_p2b<'a, P: PaxosPayload>( } else { None })) - .fold_keyed( + .fold_keyed_commutative( q!(|| (0, None)), q!(|accum, value| { accum.0 += 1; accum.1 = Some(value); + // this is commutative because p2bs with the same slot and ballot + // will have identical values since they originated from the payload }), ) .map(q!(|(k, (count, v))| (k, (count, v.unwrap())))); diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 49159f00fafc..649706f48123 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -3,8 +3,9 @@ use std::rc::Rc; use std::time::{Duration, SystemTime}; use hydroflow_plus::*; +use stream::NoOrder; -use super::paxos::{Acceptor, Proposer}; +use super::paxos::{Acceptor, Ballot, Proposer}; use super::paxos_kv::{paxos_kv, KvPayload, Replica}; pub struct Client {} @@ -33,17 +34,42 @@ pub fn paxos_bench<'a>( let replicas = flow.cluster::(); let (new_leader_elected_complete, new_leader_elected) = - clients.forward_ref::>(); + clients.forward_ref::>(); + + let client_tick = clients.tick(); + let cur_leader_id = new_leader_elected + .inspect(q!(|ballot| println!( + "Client notified that leader was elected: {:?}", + ballot + ))) + .max() + .map(q!(|ballot: Ballot| ballot.proposer_id)) + .latest_tick(&client_tick); + + let leader_changed = cur_leader_id.clone().delta().map(q!(|_| ())).all_ticks(); bench_client( &clients, - new_leader_elected, + leader_changed, |c_to_proposers| { + let client_self_id = clients.self_id(); let (new_leader_elected, processed_payloads) = paxos_kv( &proposers, &acceptors, &replicas, - c_to_proposers.send_bincode_interleaved(&proposers), + c_to_proposers + .tick_batch(&client_tick) + .cross_singleton(cur_leader_id) + .all_ticks() + .map(q!(move |(key, leader_id)| (leader_id, KvPayload { + key, + // we use our ID as the value and use that so the replica only notifies us + value: client_self_id + }))) + .send_bincode_interleaved(&proposers) + // clients "own" certain keys, so interleaving elements from clients will not affect + // the order of writes to the same key + .assume_ordering(), f, i_am_leader_send_timeout, i_am_leader_check_timeout, @@ -51,115 +77,86 @@ pub fn paxos_bench<'a>( checkpoint_frequency, ); - new_leader_elected_complete.complete( - new_leader_elected - .broadcast_bincode(&clients) - .map(q!(|(leader_id, _)| leader_id)), - ); - processed_payloads + new_leader_elected_complete + .complete(new_leader_elected.broadcast_bincode_interleaved(&clients)); + + // we only mark a transaction as committed when `f + 1` replicas have committed it + let (c_pending_quorum_payloads_complete_cycle, c_pending_quorum_payloads) = + client_tick.cycle::>(); + let c_received_payloads = processed_payloads .map(q!(|payload| (payload.value, payload))) - .send_bincode(&clients) + .send_bincode_interleaved(&clients) + .tick_batch(&client_tick) + .map(q!(|replica_payload| (replica_payload.key, ()))) + .chain(c_pending_quorum_payloads); + let c_received_quorum_payloads = c_received_payloads + .clone() + .fold_keyed_commutative( + q!(|| 0), + q!(|curr_count, _sender| { + *curr_count += 1; // Assumes the same replica will only send commit once + }), + ) + .filter_map(q!(move |(key, count)| { + if count == f + 1 { + Some(key) + } else { + None + } + })); + let c_new_pending_quorum_payloads = + c_received_payloads.anti_join(c_received_quorum_payloads.clone()); + c_pending_quorum_payloads_complete_cycle + .complete_next_tick(c_new_pending_quorum_payloads); + + c_received_quorum_payloads.all_ticks() }, num_clients_per_node, median_latency_window_size, - f, ); (proposers, acceptors, clients, replicas) } -// Clients. All relations for clients will be prefixed with c. All ClientPayloads will contain the virtual client number as key and the client's machine ID (to string) as value. Expects p_to_clients_leader_elected containing Ballots whenever the leader is elected, and r_to_clients_payload_applied containing ReplicaPayloads whenever a payload is committed. Outputs (leader address, ClientPayload) when a new leader is elected or when the previous payload is committed. fn bench_client<'a>( clients: &Cluster<'a, Client>, - p_to_clients_leader_elected: Stream, Cluster<'a, Client>, Unbounded>, + trigger_restart: Stream<(), Cluster<'a, Client>, Unbounded>, transaction_cycle: impl FnOnce( - Stream< - (ClusterId, KvPayload>), - Cluster<'a, Client>, - Unbounded, - >, - ) -> Stream< - (ClusterId, KvPayload>), - Cluster<'a, Client>, - Unbounded, - >, + Stream, Unbounded>, + ) -> Stream, Unbounded, NoOrder>, num_clients_per_node: usize, median_latency_window_size: usize, - f: usize, ) { let client_tick = clients.tick(); - let c_id = clients.self_id(); // r_to_clients_payload_applied.clone().inspect(q!(|payload: &(u32, ReplicaPayload)| println!("Client received payload: {:?}", payload))); - // Only keep the latest leader - let current_leader = p_to_clients_leader_elected - .inspect(q!(|ballot| println!( - "Client notified that leader was elected: {:?}", - ballot - ))) - .max(); - let c_new_leader_ballot = current_leader.clone().latest_tick(&client_tick).delta(); - // Whenever the leader changes, make all clients send a message - let c_new_payloads_when_leader_elected = - c_new_leader_ballot - .clone() - .flat_map(q!(move |leader_ballot| (0..num_clients_per_node).map( - move |i| ( - leader_ballot, - KvPayload { - key: i as u32, - value: c_id - } - ) - ))); - let (c_to_proposers_complete_cycle, c_to_proposers) = clients.forward_ref(); - let transaction_results = transaction_cycle(c_to_proposers); + // Whenever the leader changes, make all clients send a message + let restart_this_tick = trigger_restart.tick_batch(&client_tick).last(); - // Whenever replicas confirm that a payload was committed, collected it and wait for a quorum - let (c_pending_quorum_payloads_complete_cycle, c_pending_quorum_payloads) = client_tick.cycle(); - let c_received_payloads = transaction_results - .tick_batch(&client_tick) - .map(q!(|(sender, replica_payload)| ( - replica_payload.key, - sender - ))) - .chain(c_pending_quorum_payloads); - let c_received_quorum_payloads = c_received_payloads + let c_new_payloads_when_restart = restart_this_tick .clone() - .fold_keyed( - q!(|| 0), - q!(|curr_count, _sender| { - *curr_count += 1; // Assumes the same replica will only send commit once - }), - ) - .filter_map(q!(move |(key, count)| { - if count == f + 1 { - Some(key) - } else { - None - } - })); - let c_new_pending_quorum_payloads = - c_received_payloads.anti_join(c_received_quorum_payloads.clone()); - c_pending_quorum_payloads_complete_cycle.complete_next_tick(c_new_pending_quorum_payloads); + .flat_map(q!(move |_| (0..num_clients_per_node).map(move |i| i as u32))); + + let (c_to_proposers_complete_cycle, c_to_proposers) = + clients.forward_ref::>(); + let c_received_quorum_payloads = transaction_cycle( + c_to_proposers.assume_ordering(), /* we don't send a new write for the same key until the previous one is committed, + * so writes to the same key are ordered */ + ) + .tick_batch(&client_tick); + // Whenever all replicas confirm that a payload was committed, send another payload - let c_new_payloads_when_committed = c_received_quorum_payloads - .clone() - .cross_singleton(current_leader.clone().latest_tick(&client_tick)) - .map(q!(move |(key, cur_leader)| ( - cur_leader, - KvPayload { key, value: c_id } - ))); + let c_new_payloads_when_committed = c_received_quorum_payloads.clone(); c_to_proposers_complete_cycle.complete( - c_new_payloads_when_leader_elected + c_new_payloads_when_restart .chain(c_new_payloads_when_committed) .all_ticks(), ); // Track statistics let (c_timers_complete_cycle, c_timers) = - client_tick.cycle::>(); - let c_new_timers_when_leader_elected = c_new_leader_ballot + client_tick.cycle::>(); + let c_new_timers_when_leader_elected = restart_this_tick .map(q!(|_| SystemTime::now())) .flat_map(q!( move |now| (0..num_clients_per_node).map(move |virtual_id| (virtual_id, now)) @@ -171,7 +168,7 @@ fn bench_client<'a>( .clone() // Update c_timers in tick+1 so we can record differences during this tick (to track latency) .chain(c_new_timers_when_leader_elected) .chain(c_updated_timers.clone()) - .reduce_keyed(q!(|curr_time, new_time| { + .reduce_keyed_commutative(q!(|curr_time, new_time| { if new_time > *curr_time { *curr_time = new_time; } @@ -193,7 +190,7 @@ fn bench_client<'a>( .chain(c_latency_reset.into_stream()) .all_ticks() .flatten() - .fold( + .fold_commutative( // Create window with ring buffer using vec + wraparound index // TODO: Would be nice if I could use vec![] instead, but that doesn't work in HF+ with RuntimeData *median_latency_window_size q!(move || ( diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index cca964893f46..f2f02b7b4224 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -5,8 +5,9 @@ use std::hash::Hash; use hydroflow_plus::*; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; +use stream::NoOrder; -use super::paxos::{paxos_core, Acceptor, Proposer}; +use super::paxos::{paxos_core, Acceptor, Ballot, Proposer}; pub struct Replica {} @@ -57,7 +58,7 @@ pub fn paxos_kv<'a, K: KvKey, V: KvValue>( i_am_leader_check_timeout_delay_multiplier: usize, checkpoint_frequency: usize, ) -> ( - Stream<(), Cluster<'a, Proposer>, Unbounded>, + Stream, Unbounded>, Stream, Cluster<'a, Replica>, Unbounded>, ) { let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = @@ -91,7 +92,7 @@ pub fn paxos_kv<'a, K: KvKey, V: KvValue>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] pub fn replica<'a, K: KvKey, V: KvValue>( replicas: &Cluster<'a, Replica>, - p_to_replicas: Stream, Cluster<'a, Replica>, Unbounded>, + p_to_replicas: Stream, Cluster<'a, Replica>, Unbounded, NoOrder>, checkpoint_frequency: usize, ) -> ( Stream, Unbounded>, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 5a9d311e2063..18a1550ecc6c 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -30,7 +30,7 @@ expression: built.ir() sym: cycle_4, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), @@ -99,7 +99,7 @@ expression: built.ir() sym: cycle_4, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), @@ -164,47 +164,49 @@ expression: built.ir() input: FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ballot_num | Ballot { num : ballot_num , proposer_id : p_id } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , ()) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , ()) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | num | Ballot { num , proposer_id : p_id } }), input: Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_3, - }, - location_kind: Tick( - 1, - Cluster( - 0, - ), - ), + inner: , + }, + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Tee { + inner: : CycleSource { + ident: Ident { + sym: cycle_3, }, + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), }, }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), - input: Source { - source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, - ), - location_kind: Cluster( - 0, - ), }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), + input: Source { + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, + ), + location_kind: Cluster( + 0, + ), }, - ), - }, + }, + ), }, }, }, @@ -219,7 +221,7 @@ expression: built.ir() 0, ), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -256,7 +258,7 @@ expression: built.ir() input: CrossSingleton( CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -293,12 +295,12 @@ expression: built.ir() input: Inspect { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ballot_num | P1a { ballot : Ballot { num : ballot_num , proposer_id : p_id } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; | ballot | P1a { ballot } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , ()) , u32 > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , ()) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), @@ -327,7 +329,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -358,7 +360,7 @@ expression: built.ir() }, }, Tee { - inner: : Chain( + inner: : Chain( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( @@ -367,7 +369,7 @@ expression: built.ir() input: Inspect { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), input: Tee { - inner: , + inner: , }, }, }, @@ -393,7 +395,7 @@ expression: built.ir() sym: cycle_0, }, location_kind: Tick( - 2, + 3, Cluster( 1, ), @@ -411,13 +413,13 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), ), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( FilterMap { @@ -426,21 +428,21 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (p1b , ballot_num) | p1b . ballot . num == * ballot_num && p1b . ballot . proposer_id == p_id }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p1b , ballot) | p1b . ballot == * ballot }), input: CrossSingleton( Persist( Inspect { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), input: Tee { - inner: , + inner: , }, }, ), Tee { - inner: , + inner: , }, ), }, @@ -449,18 +451,21 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Tee { - inner: : Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (received_max_ballot , ballot_num) | * received_max_ballot <= Ballot { num : * ballot_num , proposer_id : p_id } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | (received_max_ballot , cur_ballot) | * received_max_ballot <= * cur_ballot }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, }, }, }, @@ -473,7 +478,7 @@ expression: built.ir() sym: cycle_5, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), @@ -490,18 +495,18 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | max_slot + 1 }), input: Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: : FoldKeyed { + inner: : FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), input: FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { - inner: , + inner: , }, }, }, @@ -529,12 +534,12 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_5, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), @@ -550,12 +555,12 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads }), input: CrossSingleton( Tee { - inner: : Fold { + inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (((index , payload) , next_slot) , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot : next_slot + index , value : Some (payload) } }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (((index , payload) , next_slot) , ballot) | P2a { ballot , slot : next_slot + index , value : Some (payload) } }), input: CrossSingleton( CrossSingleton( Enumerate { @@ -592,23 +597,49 @@ expression: built.ir() }, ), ), - input: CycleSource { - ident: Ident { - sym: cycle_1, - }, - location_kind: Cluster( - 2, + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let client_self_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , leader_id) | (leader_id , KvPayload { key , value : client_self_id }) }), + input: CrossSingleton( + CycleSource { + ident: Ident { + sym: cycle_1, + }, + location_kind: Cluster( + 2, + ), + }, + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot : Ballot | ballot . proposer_id }), + input: Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( + Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), + input: CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 2, + ), + }, + }, + ), + }, + }, + }, ), }, }, }, }, Tee { - inner: , + inner: , }, ), Tee { - inner: , + inner: , }, ), }, @@ -616,7 +647,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -624,7 +655,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -636,7 +667,7 @@ expression: built.ir() sym: cycle_6, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), @@ -646,10 +677,10 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), @@ -657,9 +688,9 @@ expression: built.ir() input: FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { - inner: : Chain( + inner: : Chain( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -695,7 +726,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), input: CrossSingleton( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( @@ -738,35 +769,35 @@ expression: built.ir() input: CrossSingleton( Chain( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , u32) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | ((slot , (count , entry)) , ballot_num) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id , } , slot , value : entry . value , }) } else { None } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , (count , entry)) , ballot) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot , slot , value : entry . value , }) } else { None } } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , u32) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (slot , ballot_num) | P2a { ballot : Ballot { num : ballot_num , proposer_id : p_id } , slot , value : None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , ballot) | P2a { ballot , slot , value : None } }), input: CrossSingleton( Difference( FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: ops :: Range < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), input: Tee { - inner: , + inner: , }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: , + inner: , }, }, ), Tee { - inner: , + inner: , }, ), }, @@ -774,11 +805,11 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: , + inner: , }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), @@ -789,7 +820,7 @@ expression: built.ir() acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: DeferTick( Tee { - inner: , + inner: , }, ), }, @@ -802,13 +833,13 @@ expression: built.ir() ), }, Tee { - inner: , + inner: , }, ), Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), @@ -818,7 +849,7 @@ expression: built.ir() }, }, Tee { - inner: , + inner: , }, ), }, @@ -830,7 +861,7 @@ expression: built.ir() sym: cycle_7, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), @@ -846,10 +877,10 @@ expression: built.ir() }, }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -861,7 +892,7 @@ expression: built.ir() sym: cycle_7, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), @@ -873,11 +904,11 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { - inner: , + inner: , }, }, Tee { - inner: , + inner: , }, ), }, @@ -888,7 +919,7 @@ expression: built.ir() sym: cycle_0, }, location_kind: Tick( - 2, + 3, Cluster( 1, ), @@ -902,10 +933,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -920,7 +951,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , ()) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { - inner: : ReduceKeyed { + inner: : ReduceKeyed { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), input: Persist( Network { @@ -976,7 +1007,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -999,7 +1030,7 @@ expression: built.ir() 0, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1007,7 +1038,7 @@ expression: built.ir() sym: cycle_1, }, location_kind: Tick( - 5, + 6, Cluster( 3, ), @@ -1019,7 +1050,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Chain( Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), @@ -1059,14 +1090,14 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), input: AntiJoin( Tee { - inner: , + inner: , }, CycleSource { ident: Ident { sym: cycle_6, }, location_kind: Tick( - 1, + 2, Cluster( 0, ), @@ -1082,7 +1113,7 @@ expression: built.ir() sym: cycle_1, }, location_kind: Tick( - 5, + 6, Cluster( 3, ), @@ -1092,14 +1123,14 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < usize > , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | v | v }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | None }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < usize > , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let expected_next_slot = std :: cmp :: max (filled_slot . map (| v | v + 1) . unwrap_or (0) , highest_seq . map (| v | v + 1) . unwrap_or (0) ,) ; if sorted_payload . seq == expected_next_slot { * filled_slot = Some (sorted_payload . seq) ; } } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Chain( Map { @@ -1109,7 +1140,7 @@ expression: built.ir() sym: cycle_2, }, location_kind: Tick( - 5, + 6, Cluster( 3, ), @@ -1141,30 +1172,30 @@ expression: built.ir() sym: cycle_2, }, location_kind: Tick( - 5, + 6, Cluster( 3, ), ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , None) }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == (last_seq . map (| s | s + 1) . unwrap_or (0)) , "Hole in log between seq {:?} and {}" , * last_seq , payload . seq) ; * last_seq = Some (payload . seq) ; } }), input: Persist( Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1181,14 +1212,14 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 5, + 6, Cluster( 3, ), ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , usize) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if max_checkpointed_seq . map (| m | new_highest_seq - m >= checkpoint_frequency) . unwrap_or (true) { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Chain( @@ -1202,7 +1233,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 5, + 6, Cluster( 3, ), @@ -1223,7 +1254,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1238,7 +1269,7 @@ expression: built.ir() 3, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1249,7 +1280,7 @@ expression: built.ir() 2, ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , ()) , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (leader_id , _) | leader_id }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -1264,7 +1295,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , ()) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < () > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: Ballot) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& data) . unwrap () . into ()) }", ], }, ), @@ -1275,18 +1306,26 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < () > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use crate :: __staged :: cluster :: paxos :: * ; move | _ | () }), - input: Tee { - inner: , - }, + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , ()) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Tee { + inner: , + }, + }, + ), }, }, }, @@ -1305,45 +1344,48 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Chain( + inner: : Chain( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , (u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (sender , replica_payload) | (replica_payload . key , sender) }), - input: Network { - from_location: Cluster( - 3, - ), - from_key: None, - to_location: Cluster( - 2, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", - ], - }, + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (u32 , ()) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | replica_payload | (replica_payload . key , ()) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 3, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 2, ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), - input: Tee { - inner: , + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), + input: Tee { + inner: , + }, }, }, }, @@ -1363,13 +1405,13 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1386,40 +1428,25 @@ expression: built.ir() ), input: Chain( FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node = 1usize ; move | leader_ballot | (0 .. num_clients_per_node) . map (move | i | (leader_ballot , KvPayload { key : i as u32 , value : c_id })) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node = 1usize ; move | _ | (0 .. num_clients_per_node) . map (move | i | i as u32) }), input: Tee { - inner: : Delta( - Tee { - inner: : Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 2, - ), - }, - }, - ), - }, + inner: : Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < () , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | * curr = new }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | () }), + input: Delta( + Tee { + inner: , + }, + ), }, - ), + }, }, }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let c_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , cur_leader) | (cur_leader , KvPayload { key , value : c_id }) }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), + Tee { + inner: : Tee { + inner: , + }, }, ), }, @@ -1428,7 +1455,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 0, + 1, Cluster( 2, ), @@ -1439,12 +1466,12 @@ expression: built.ir() input: Chain( Chain( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_3, }, location_kind: Tick( - 0, + 1, Cluster( 2, ), @@ -1454,18 +1481,18 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < std :: time :: SystemTime , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node = 1usize ; move | now | (0 .. num_clients_per_node) . map (move | virtual_id | (virtual_id , now)) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: , + inner: , }, }, }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1492,10 +1519,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1503,7 +1530,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: : Source { + inner: : Source { source: Stream( { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, ), @@ -1533,7 +1560,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1544,7 +1571,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1556,7 +1583,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1567,7 +1594,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), diff --git a/hydroflow_plus_test/src/cluster/two_pc.rs b/hydroflow_plus_test/src/cluster/two_pc.rs index 79e400ae38a2..dd89438e0b08 100644 --- a/hydroflow_plus_test/src/cluster/two_pc.rs +++ b/hydroflow_plus_test/src/cluster/two_pc.rs @@ -68,7 +68,7 @@ pub fn two_pc<'a>( .map(q!(|(id, (t, _reply))| (t, id))) // fold_keyed: 1 input stream of type (K, V1), 1 output stream of type (K, V2). // The output will have one tuple for each distinct K, with an accumulated value of type V2. - .tick_batch(&coordinator.tick()).fold_keyed(q!(|| 0), q!(|old: &mut u32, _| *old += 1)).filter_map(q!(move |(t, count)| { + .tick_batch(&coordinator.tick()).fold_keyed_commutative(q!(|| 0), q!(|old: &mut u32, _| *old += 1)).filter_map(q!(move |(t, count)| { // here I set the participant to 3. If want more or less participant, fix line 26 of examples/broadcast.rs if count == num_participants { Some(t) diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index 8483c6f0aa80..8dfea4fb1f8f 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -2,6 +2,7 @@ use hydroflow::tokio::sync::mpsc::UnboundedSender; use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::SingleProcessGraph; use hydroflow_plus::*; +use stream::NoOrder; #[stageleft::entry] pub fn graph_reachability<'a>( @@ -16,7 +17,7 @@ pub fn graph_reachability<'a>( let edges = process.source_stream(edges); let reachability_tick = process.tick(); - let (set_reached_cycle, reached_cycle) = reachability_tick.cycle(); + let (set_reached_cycle, reached_cycle) = reachability_tick.cycle::>(); let reached = roots.tick_batch(&reachability_tick).chain(reached_cycle); let reachable = reached From a93a5e59e1681d325b3433193bb86254d23bdc77 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 21 Nov 2024 12:28:40 -0800 Subject: [PATCH 67/74] feat(hydroflow_plus, stageleft)!: allow cluster self ID to be referenced as a global constant (#1574) This eliminates the need to store `cluster.self_id()` in a local variable first, instead you can directly reference `CLUSTER_SELF_ID`. --- hydroflow_plus/src/builder/compiled.rs | 25 ++- hydroflow_plus/src/builder/deploy.rs | 2 +- hydroflow_plus/src/deploy/deploy_graph.rs | 124 +---------- hydroflow_plus/src/deploy/deploy_runtime.rs | 26 +-- hydroflow_plus/src/deploy/macro_runtime.rs | 6 +- hydroflow_plus/src/deploy/mod.rs | 11 +- hydroflow_plus/src/deploy/trybuild.rs | 80 ++++++- .../src/deploy/trybuild_rewriters.rs | 35 +++ hydroflow_plus/src/lib.rs | 1 + hydroflow_plus/src/location/cluster.rs | 207 ------------------ .../src/location/cluster/cluster_id.rs | 85 +++++++ hydroflow_plus/src/location/cluster/mod.rs | 147 +++++++++++++ .../src/location/external_process.rs | 6 + hydroflow_plus/src/location/mod.rs | 27 ++- hydroflow_plus/src/location/process.rs | 6 + hydroflow_plus/src/location/tick.rs | 19 +- hydroflow_plus/src/optional.rs | 27 ++- hydroflow_plus/src/rewrites/profiler.rs | 2 +- hydroflow_plus/src/rewrites/properties.rs | 24 +- ...ts__profiler_wrapping_all_operators-2.snap | 4 +- hydroflow_plus/src/runtime_context.rs | 8 +- hydroflow_plus/src/singleton.rs | 27 ++- hydroflow_plus/src/stream.rs | 159 ++++++++------ hydroflow_plus_test/src/cluster/paxos.rs | 11 +- .../src/cluster/paxos_bench.rs | 3 +- .../src/cluster/simple_cluster.rs | 10 +- ...ter__compute_pi__tests__compute_pi_ir.snap | 6 +- ..._tests__compute_pi_ir@surface_graph_0.snap | 4 +- ..._tests__compute_pi_ir@surface_graph_1.snap | 6 +- ...er__many_to_many__tests__many_to_many.snap | 4 +- ...ter__map_reduce__tests__map_reduce_ir.snap | 4 +- ..._tests__map_reduce_ir@surface_graph_0.snap | 8 +- ..._tests__map_reduce_ir@surface_graph_1.snap | 4 +- ...cluster__paxos_bench__tests__paxos_ir.snap | 192 ++++++++-------- ...simple_cluster__tests__simple_cluster.snap | 16 +- .../src/local/count_elems.rs | 2 +- ...ests__chat_app_no_replay@graphvis_dot.snap | 2 +- ...__chat_app_no_replay@graphvis_mermaid.snap | 2 +- ...__tests__chat_app_replay@graphvis_dot.snap | 2 +- ...sts__chat_app_replay@graphvis_mermaid.snap | 2 +- ...ount_elems__tests__count@graphvis_dot.snap | 2 +- ..._elems__tests__count@graphvis_mermaid.snap | 2 +- ...ity__tests__reachability@graphvis_dot.snap | 2 +- ..._tests__reachability@graphvis_mermaid.snap | 2 +- ..._anti_join_static_static@graphvis_dot.snap | 2 +- ...i_join_static_static@graphvis_mermaid.snap | 2 +- ...s__anti_join_static_tick@graphvis_dot.snap | 2 +- ...nti_join_static_tick@graphvis_mermaid.snap | 2 +- ...s__anti_join_tick_static@graphvis_dot.snap | 2 +- ...nti_join_tick_static@graphvis_mermaid.snap | 2 +- ...sts__anti_join_tick_tick@graphvis_dot.snap | 2 +- ..._anti_join_tick_tick@graphvis_mermaid.snap | 2 +- ...difference_static_static@graphvis_dot.snap | 2 +- ...erence_static_static@graphvis_mermaid.snap | 2 +- ...__difference_static_tick@graphvis_dot.snap | 2 +- ...fference_static_tick@graphvis_mermaid.snap | 2 +- ...__difference_tick_static@graphvis_dot.snap | 2 +- ...fference_tick_static@graphvis_mermaid.snap | 2 +- ...ts__difference_tick_tick@graphvis_dot.snap | 2 +- ...difference_tick_tick@graphvis_mermaid.snap | 2 +- ...d_join__tests__teed_join@graphvis_dot.snap | 2 +- ...in__tests__teed_join@graphvis_mermaid.snap | 2 +- ...ts__teed_join_multi_node@graphvis_dot.snap | 2 +- ...teed_join_multi_node@graphvis_mermaid.snap | 2 +- ...__tests__teed_join_twice@graphvis_dot.snap | 4 +- ...sts__teed_join_twice@graphvis_mermaid.snap | 4 +- stageleft/src/lib.rs | 185 ++++++++++++++-- stageleft/src/runtime_support.rs | 46 +++- stageleft_macro/src/lib.rs | 2 +- .../src/quote_impl/free_variable/mod.rs | 172 ++++++++------- stageleft_macro/src/quote_impl/mod.rs | 26 ++- ...ests__capture_copy_local@macro_tokens.snap | 22 +- ...capture_copy_local_block@macro_tokens.snap | 23 +- ...ure_copy_local_block_let@macro_tokens.snap | 21 +- ..._tests__capture_in_macro@macro_tokens.snap | 21 +- ...pl__tests__capture_local@macro_tokens.snap | 21 +- ...tests__capture_local_mut@macro_tokens.snap | 21 +- ...on_capture_enum_creation@macro_tokens.snap | 1 + ...tests__non_capture_local@macro_tokens.snap | 1 + ..._capture_struct_creation@macro_tokens.snap | 1 + ..._prelude_enum_variants@macro_tokens-2.snap | 1 + ..._prelude_enum_variants@macro_tokens-3.snap | 1 + ..._prelude_enum_variants@macro_tokens-4.snap | 1 + ...s__prelude_enum_variants@macro_tokens.snap | 1 + ...uote_impl__tests__simple@macro_tokens.snap | 1 + stageleft_tool/src/lib.rs | 12 + 86 files changed, 1167 insertions(+), 805 deletions(-) create mode 100644 hydroflow_plus/src/deploy/trybuild_rewriters.rs delete mode 100644 hydroflow_plus/src/location/cluster.rs create mode 100644 hydroflow_plus/src/location/cluster/cluster_id.rs create mode 100644 hydroflow_plus/src/location/cluster/mod.rs diff --git a/hydroflow_plus/src/builder/compiled.rs b/hydroflow_plus/src/builder/compiled.rs index 1db322926722..5e4164e6c796 100644 --- a/hydroflow_plus/src/builder/compiled.rs +++ b/hydroflow_plus/src/builder/compiled.rs @@ -5,8 +5,8 @@ use hydroflow::scheduled::graph::Hydroflow; use hydroflow_lang::graph::{partition_graph, HydroflowGraph}; use proc_macro2::TokenStream; use quote::quote; -use stageleft::runtime_support::FreeVariable; -use stageleft::Quoted; +use stageleft::runtime_support::FreeVariableWithContext; +use stageleft::QuotedWithContext; use crate::staging_util::Invariant; @@ -27,7 +27,10 @@ impl CompiledFlow<'_, ID> { } impl<'a> CompiledFlow<'a, usize> { - pub fn with_dynamic_id(self, id: impl Quoted<'a, usize>) -> CompiledFlowWithId<'a> { + pub fn with_dynamic_id( + self, + id: impl QuotedWithContext<'a, usize, ()>, + ) -> CompiledFlowWithId<'a> { let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") .expect("hydroflow_plus should be present in `Cargo.toml`"); let root = match hydroflow_crate { @@ -82,10 +85,12 @@ impl<'a> CompiledFlow<'a, usize> { } } -impl<'a> Quoted<'a, Hydroflow<'a>> for CompiledFlow<'a, ()> {} +impl<'a, Ctx> QuotedWithContext<'a, Hydroflow<'a>, Ctx> for CompiledFlow<'a, ()> {} -impl<'a> FreeVariable> for CompiledFlow<'a, ()> { - fn to_tokens(mut self) -> (Option, Option) { +impl<'a, Ctx> FreeVariableWithContext for CompiledFlow<'a, ()> { + type O = Hydroflow<'a>; + + fn to_tokens(mut self, _ctx: &Ctx) -> (Option, Option) { let hydroflow_crate = proc_macro_crate::crate_name("hydroflow_plus") .expect("hydroflow_plus should be present in `Cargo.toml`"); let root = match hydroflow_crate { @@ -116,10 +121,12 @@ pub struct CompiledFlowWithId<'a> { _phantom: Invariant<'a>, } -impl<'a> Quoted<'a, Hydroflow<'a>> for CompiledFlowWithId<'a> {} +impl<'a, Ctx> QuotedWithContext<'a, Hydroflow<'a>, Ctx> for CompiledFlowWithId<'a> {} + +impl<'a, Ctx> FreeVariableWithContext for CompiledFlowWithId<'a> { + type O = Hydroflow<'a>; -impl<'a> FreeVariable> for CompiledFlowWithId<'a> { - fn to_tokens(self) -> (Option, Option) { + fn to_tokens(self, _ctx: &Ctx) -> (Option, Option) { (None, Some(self.tokens)) } } diff --git a/hydroflow_plus/src/builder/deploy.rs b/hydroflow_plus/src/builder/deploy.rs index b44936ac2a6a..6e11e8935dcb 100644 --- a/hydroflow_plus/src/builder/deploy.rs +++ b/hydroflow_plus/src/builder/deploy.rs @@ -8,7 +8,7 @@ use hydroflow::futures::{Sink, Stream}; use proc_macro2::Span; use serde::de::DeserializeOwned; use serde::Serialize; -use stageleft::Quoted; +use stageleft::QuotedWithContext; use super::built::build_inner; use super::compiled::CompiledFlow; diff --git a/hydroflow_plus/src/deploy/deploy_graph.rs b/hydroflow_plus/src/deploy/deploy_graph.rs index 44be80c17f58..15cb6f1aaf91 100644 --- a/hydroflow_plus/src/deploy/deploy_graph.rs +++ b/hydroflow_plus/src/deploy/deploy_graph.rs @@ -20,14 +20,11 @@ use hydroflow::util::deploy::{ConnectedSink, ConnectedSource}; use nameof::name_of; use serde::de::DeserializeOwned; use serde::Serialize; -use sha2::{Digest, Sha256}; -use stageleft::{Quoted, RuntimeData}; -use syn::visit_mut::VisitMut; +use stageleft::{QuotedWithContext, RuntimeData}; use tokio::sync::RwLock; -use trybuild_internals_api::path; use super::deploy_runtime::*; -use super::trybuild::{compile_graph_trybuild, create_trybuild}; +use super::trybuild::create_graph_trybuild; use super::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec, Node, ProcessSpec, RegisterPort}; pub struct HydroDeploy {} @@ -373,14 +370,14 @@ impl<'a> Deploy<'a> for HydroDeploy { fn cluster_ids( _env: &Self::CompileEnv, of_cluster: usize, - ) -> impl Quoted<'a, &'a Vec> + Copy + 'a { + ) -> impl QuotedWithContext<'a, &'a Vec, ()> + Copy + 'a { cluster_members( RuntimeData::new("__hydroflow_plus_trybuild_cli"), of_cluster, ) } - fn cluster_self_id(_env: &Self::CompileEnv) -> impl Quoted<'a, u32> + Copy + 'a { + fn cluster_self_id(_env: &Self::CompileEnv) -> impl QuotedWithContext<'a, u32, ()> + Copy + 'a { cluster_self_id(RuntimeData::new("__hydroflow_plus_trybuild_cli")) } } @@ -872,119 +869,6 @@ impl, I: IntoIterator> ClusterSpec<'_, HydroDepl } } -fn clean_name_hint(name_hint: &str) -> String { - name_hint - .replace("::", "__") - .replace(" ", "_") - .replace(",", "_") - .replace("<", "_") - .replace(">", "") - .replace("(", "") - .replace(")", "") -} - -// TODO(shadaj): has to be public due to stageleft limitations -#[doc(hidden)] -pub struct ReplaceCrateNameWithStaged { - pub crate_name: String, -} - -impl VisitMut for ReplaceCrateNameWithStaged { - fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) { - if let Some(first) = i.path.segments.first() { - if first.ident == self.crate_name { - let tail = i.path.segments.iter().skip(1).collect::>(); - *i = syn::parse_quote!(crate::__staged #(::#tail)*); - } - } - - syn::visit_mut::visit_type_path_mut(self, i); - } -} - -// TODO(shadaj): has to be public due to stageleft limitations -#[doc(hidden)] -pub struct ReplaceCrateWithOrig { - pub crate_name: String, -} - -impl VisitMut for ReplaceCrateWithOrig { - fn visit_item_use_mut(&mut self, i: &mut syn::ItemUse) { - if let syn::UseTree::Path(p) = &mut i.tree { - if p.ident == "crate" { - p.ident = syn::Ident::new(&self.crate_name, p.ident.span()); - i.leading_colon = Some(Default::default()); - } - } - - syn::visit_mut::visit_item_use_mut(self, i); - } -} - -fn create_graph_trybuild( - graph: HydroflowGraph, - extra_stmts: Vec, - name_hint: &Option, -) -> ( - String, - (std::path::PathBuf, std::path::PathBuf, Option>), -) { - let source_dir = trybuild_internals_api::cargo::manifest_dir().unwrap(); - let source_manifest = trybuild_internals_api::dependencies::get_manifest(&source_dir).unwrap(); - let crate_name = &source_manifest.package.name.to_string().replace("-", "_"); - - let is_test = super::trybuild::IS_TEST.load(std::sync::atomic::Ordering::Relaxed); - - let mut generated_code = compile_graph_trybuild(graph, extra_stmts); - - ReplaceCrateNameWithStaged { - crate_name: crate_name.clone(), - } - .visit_file_mut(&mut generated_code); - - let mut inlined_staged = stageleft_tool::gen_staged_trybuild( - &path!(source_dir / "src" / "lib.rs"), - crate_name.clone(), - is_test, - ); - - ReplaceCrateWithOrig { - crate_name: crate_name.clone(), - } - .visit_file_mut(&mut inlined_staged); - - let source = prettyplease::unparse(&syn::parse_quote! { - #generated_code - - #[allow( - unused, - ambiguous_glob_reexports, - clippy::suspicious_else_formatting, - unexpected_cfgs, - reason = "generated code" - )] - pub mod __staged { - #inlined_staged - } - }); - - let mut hasher = Sha256::new(); - hasher.update(&source); - let hash = format!("{:X}", hasher.finalize()) - .chars() - .take(8) - .collect::(); - - let bin_name = if let Some(name_hint) = &name_hint { - format!("{}_{}", clean_name_hint(name_hint), &hash) - } else { - hash - }; - - let trybuild_created = create_trybuild(&source, &bin_name, is_test).unwrap(); - (bin_name, trybuild_created) -} - fn create_trybuild_service( trybuild: TrybuildHost, dir: &std::path::PathBuf, diff --git a/hydroflow_plus/src/deploy/deploy_runtime.rs b/hydroflow_plus/src/deploy/deploy_runtime.rs index ad81b8b76d22..14b383c42d03 100644 --- a/hydroflow_plus/src/deploy/deploy_runtime.rs +++ b/hydroflow_plus/src/deploy/deploy_runtime.rs @@ -4,7 +4,7 @@ use hydroflow::util::deploy::{ ConnectedDemux, ConnectedDirect, ConnectedSink, ConnectedSource, ConnectedTagged, DeployPorts, }; use serde::{Deserialize, Serialize}; -use stageleft::{q, Quoted, RuntimeData}; +use stageleft::{q, QuotedWithContext, RuntimeData}; #[derive(Default, Serialize, Deserialize)] pub struct HydroflowPlusMeta { @@ -16,13 +16,13 @@ pub struct HydroflowPlusMeta { pub fn cluster_members( cli: RuntimeData<&DeployPorts>, of_cluster: usize, -) -> impl Quoted<&Vec> + Copy { +) -> impl QuotedWithContext<&Vec, ()> + Copy { q!(cli.meta.clusters.get(&of_cluster).unwrap()) } pub fn cluster_self_id( cli: RuntimeData<&DeployPorts>, -) -> impl Quoted + Copy { +) -> impl QuotedWithContext + Copy { q!(cli .meta .cluster_id @@ -41,7 +41,7 @@ pub fn deploy_o2o( .connect_local_blocking::() .into_sink() }) - .splice_untyped() + .splice_untyped_ctx(&()) }, { q!({ @@ -49,7 +49,7 @@ pub fn deploy_o2o( .connect_local_blocking::() .into_source() }) - .splice_untyped() + .splice_untyped_ctx(&()) }, ) } @@ -66,7 +66,7 @@ pub fn deploy_o2m( .connect_local_blocking::>() .into_sink() }) - .splice_untyped() + .splice_untyped_ctx(&()) }, { q!({ @@ -74,7 +74,7 @@ pub fn deploy_o2m( .connect_local_blocking::() .into_source() }) - .splice_untyped() + .splice_untyped_ctx(&()) }, ) } @@ -91,7 +91,7 @@ pub fn deploy_m2o( .connect_local_blocking::() .into_sink() }) - .splice_untyped() + .splice_untyped_ctx(&()) }, { q!({ @@ -99,7 +99,7 @@ pub fn deploy_m2o( .connect_local_blocking::>() .into_source() }) - .splice_untyped() + .splice_untyped_ctx(&()) }, ) } @@ -116,7 +116,7 @@ pub fn deploy_m2m( .connect_local_blocking::>() .into_sink() }) - .splice_untyped() + .splice_untyped_ctx(&()) }, { q!({ @@ -124,7 +124,7 @@ pub fn deploy_m2m( .connect_local_blocking::>() .into_source() }) - .splice_untyped() + .splice_untyped_ctx(&()) }, ) } @@ -139,7 +139,7 @@ pub fn deploy_e2o( .connect_local_blocking::() .into_source() }) - .splice_untyped() + .splice_untyped_ctx(&()) } pub fn deploy_o2e( @@ -152,5 +152,5 @@ pub fn deploy_o2e( .connect_local_blocking::() .into_sink() }) - .splice_untyped() + .splice_untyped_ctx(&()) } diff --git a/hydroflow_plus/src/deploy/macro_runtime.rs b/hydroflow_plus/src/deploy/macro_runtime.rs index 5ff3c2533177..50eb298d971a 100644 --- a/hydroflow_plus/src/deploy/macro_runtime.rs +++ b/hydroflow_plus/src/deploy/macro_runtime.rs @@ -7,7 +7,7 @@ use hydroflow::bytes::Bytes; use hydroflow::futures::{Sink, Stream}; use hydroflow::util::deploy::DeployPorts; use hydroflow_lang::graph::HydroflowGraph; -use stageleft::{Quoted, RuntimeData}; +use stageleft::{QuotedWithContext, RuntimeData}; use super::HydroflowPlusMeta; use crate::deploy::{ClusterSpec, Deploy, ExternalSpec, Node, ProcessSpec, RegisterPort}; @@ -170,11 +170,11 @@ impl<'a> Deploy<'a> for DeployRuntime { fn cluster_ids( env: &Self::CompileEnv, of_cluster: usize, - ) -> impl Quoted<'a, &'a Vec> + Copy + 'a { + ) -> impl QuotedWithContext<'a, &'a Vec, ()> + Copy + 'a { super::deploy_runtime::cluster_members(*env, of_cluster) } - fn cluster_self_id(env: &Self::CompileEnv) -> impl Quoted<'a, u32> + Copy + 'a { + fn cluster_self_id(env: &Self::CompileEnv) -> impl QuotedWithContext<'a, u32, ()> + Copy + 'a { super::deploy_runtime::cluster_self_id(*env) } } diff --git a/hydroflow_plus/src/deploy/mod.rs b/hydroflow_plus/src/deploy/mod.rs index d447bd23722a..8eee87ff035e 100644 --- a/hydroflow_plus/src/deploy/mod.rs +++ b/hydroflow_plus/src/deploy/mod.rs @@ -7,7 +7,7 @@ use hydroflow::futures::{Sink, Stream}; use hydroflow_lang::graph::HydroflowGraph; use serde::de::DeserializeOwned; use serde::Serialize; -use stageleft::Quoted; +use stageleft::QuotedWithContext; #[cfg(feature = "deploy_runtime")] pub mod macro_runtime; @@ -15,6 +15,11 @@ pub mod macro_runtime; #[cfg(feature = "deploy")] pub(crate) mod trybuild; +// TODO(shadaj): has to be public due to stageleft limitations +#[cfg(feature = "deploy")] +#[doc(hidden)] +pub mod trybuild_rewriters; + pub use macro_runtime::*; #[cfg(feature = "deploy")] pub use trybuild::init_test; @@ -171,8 +176,8 @@ pub trait Deploy<'a> { fn cluster_ids( env: &Self::CompileEnv, of_cluster: usize, - ) -> impl Quoted<'a, &'a Vec> + Copy + 'a; - fn cluster_self_id(env: &Self::CompileEnv) -> impl Quoted<'a, u32> + Copy + 'a; + ) -> impl QuotedWithContext<'a, &'a Vec, ()> + Copy + 'a; + fn cluster_self_id(env: &Self::CompileEnv) -> impl QuotedWithContext<'a, u32, ()> + Copy + 'a; } impl< diff --git a/hydroflow_plus/src/deploy/trybuild.rs b/hydroflow_plus/src/deploy/trybuild.rs index a517659579e3..479d25962e92 100644 --- a/hydroflow_plus/src/deploy/trybuild.rs +++ b/hydroflow_plus/src/deploy/trybuild.rs @@ -2,18 +2,94 @@ use std::fs; use std::path::PathBuf; use hydroflow_lang::graph::{partition_graph, HydroflowGraph}; +use sha2::{Digest, Sha256}; use stageleft::internal::quote; +use syn::visit_mut::VisitMut; use trybuild_internals_api::cargo::{self, Metadata}; use trybuild_internals_api::env::Update; use trybuild_internals_api::run::{PathDependency, Project}; use trybuild_internals_api::{dependencies, features, path, Runner}; -pub static IS_TEST: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); +use super::trybuild_rewriters::{ReplaceCrateNameWithStaged, ReplaceCrateWithOrig}; + +static IS_TEST: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); pub fn init_test() { IS_TEST.store(true, std::sync::atomic::Ordering::Relaxed); } +fn clean_name_hint(name_hint: &str) -> String { + name_hint + .replace("::", "__") + .replace(" ", "_") + .replace(",", "_") + .replace("<", "_") + .replace(">", "") + .replace("(", "") + .replace(")", "") +} + +pub fn create_graph_trybuild( + graph: HydroflowGraph, + extra_stmts: Vec, + name_hint: &Option, +) -> (String, (PathBuf, PathBuf, Option>)) { + let source_dir = cargo::manifest_dir().unwrap(); + let source_manifest = dependencies::get_manifest(&source_dir).unwrap(); + let crate_name = &source_manifest.package.name.to_string().replace("-", "_"); + + let is_test = IS_TEST.load(std::sync::atomic::Ordering::Relaxed); + + let mut generated_code = compile_graph_trybuild(graph, extra_stmts); + + ReplaceCrateNameWithStaged { + crate_name: crate_name.clone(), + } + .visit_file_mut(&mut generated_code); + + let mut inlined_staged = stageleft_tool::gen_staged_trybuild( + &path!(source_dir / "src" / "lib.rs"), + crate_name.clone(), + is_test, + ); + + ReplaceCrateWithOrig { + crate_name: crate_name.clone(), + } + .visit_file_mut(&mut inlined_staged); + + let source = prettyplease::unparse(&syn::parse_quote! { + #generated_code + + #[allow( + unused, + ambiguous_glob_reexports, + clippy::suspicious_else_formatting, + unexpected_cfgs, + reason = "generated code" + )] + pub mod __staged { + #inlined_staged + } + }); + + let mut hasher = Sha256::new(); + hasher.update(&source); + let hash = format!("{:X}", hasher.finalize()) + .chars() + .take(8) + .collect::(); + + let bin_name = if let Some(name_hint) = &name_hint { + format!("{}_{}", clean_name_hint(name_hint), &hash) + } else { + hash + }; + + let trybuild_created = create_trybuild(&source, &bin_name, is_test).unwrap(); + (bin_name, trybuild_created) +} + pub fn compile_graph_trybuild(graph: HydroflowGraph, extra_stmts: Vec) -> syn::File { let partitioned_graph = partition_graph(graph).expect("Failed to partition (cycle detected)."); @@ -27,7 +103,7 @@ pub fn compile_graph_trybuild(graph: HydroflowGraph, extra_stmts: Vec let source_ast: syn::File = syn::parse_quote! { #![feature(box_patterns)] - #![allow(unused_imports, unused_crate_dependencies, missing_docs)] + #![allow(unused_imports, unused_crate_dependencies, missing_docs, non_snake_case)] use hydroflow_plus::*; #[allow(unused)] diff --git a/hydroflow_plus/src/deploy/trybuild_rewriters.rs b/hydroflow_plus/src/deploy/trybuild_rewriters.rs new file mode 100644 index 000000000000..6a9a14cafe73 --- /dev/null +++ b/hydroflow_plus/src/deploy/trybuild_rewriters.rs @@ -0,0 +1,35 @@ +use syn::visit_mut::VisitMut; + +pub struct ReplaceCrateNameWithStaged { + pub crate_name: String, +} + +impl VisitMut for ReplaceCrateNameWithStaged { + fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) { + if let Some(first) = i.path.segments.first() { + if first.ident == self.crate_name { + let tail = i.path.segments.iter().skip(1).collect::>(); + *i = syn::parse_quote!(crate::__staged #(::#tail)*); + } + } + + syn::visit_mut::visit_type_path_mut(self, i); + } +} + +pub struct ReplaceCrateWithOrig { + pub crate_name: String, +} + +impl VisitMut for ReplaceCrateWithOrig { + fn visit_item_use_mut(&mut self, i: &mut syn::ItemUse) { + if let syn::UseTree::Path(p) = &mut i.tree { + if p.ident == "crate" { + p.ident = syn::Ident::new(&self.crate_name, p.ident.span()); + i.leading_colon = Some(Default::default()); + } + } + + syn::visit_mut::visit_item_use_mut(self, i); + } +} diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 3a0161107214..e6e4af74fd81 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -26,6 +26,7 @@ pub mod optional; pub use optional::Optional; pub mod location; +pub use location::cluster::CLUSTER_SELF_ID; pub use location::{Cluster, ClusterId, Location, Process, Tick}; pub mod deploy; diff --git a/hydroflow_plus/src/location/cluster.rs b/hydroflow_plus/src/location/cluster.rs deleted file mode 100644 index 695bce5d875a..000000000000 --- a/hydroflow_plus/src/location/cluster.rs +++ /dev/null @@ -1,207 +0,0 @@ -use std::fmt::{Debug, Display}; -use std::hash::Hash; -use std::marker::PhantomData; - -use proc_macro2::{Span, TokenStream}; -use quote::quote; -use serde::{Deserialize, Serialize}; -use stageleft::runtime_support::FreeVariable; -use stageleft::{quote_type, Quoted}; - -use super::{Location, LocationId}; -use crate::builder::FlowState; -use crate::staging_util::{get_this_crate, Invariant}; - -pub struct Cluster<'a, C> { - pub(crate) id: usize, - pub(crate) flow_state: FlowState, - pub(crate) _phantom: Invariant<'a, C>, -} - -impl<'a, C> Cluster<'a, C> { - pub fn self_id(&self) -> impl Quoted<'a, ClusterId> + Copy { - ClusterSelfId { - id: self.id, - _phantom: PhantomData, - } - } - - pub fn members(&self) -> impl Quoted<'a, &'a Vec>> + Copy { - ClusterIds { - id: self.id, - _phantom: PhantomData, - } - } -} - -impl Clone for Cluster<'_, C> { - fn clone(&self) -> Self { - Cluster { - id: self.id, - flow_state: self.flow_state.clone(), - _phantom: PhantomData, - } - } -} - -impl<'a, C> Location<'a> for Cluster<'a, C> { - fn id(&self) -> LocationId { - LocationId::Cluster(self.id) - } - - fn flow_state(&self) -> &FlowState { - &self.flow_state - } - - fn is_top_level() -> bool { - true - } -} - -#[repr(transparent)] -pub struct ClusterId { - pub raw_id: u32, - pub(crate) _phantom: PhantomData, -} - -impl Debug for ClusterId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "ClusterId::<{}>({})", - std::any::type_name::(), - self.raw_id - ) - } -} - -impl Display for ClusterId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "ClusterId::<{}>({})", - std::any::type_name::(), - self.raw_id - ) - } -} - -impl Clone for ClusterId { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for ClusterId {} - -impl Serialize for ClusterId { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - self.raw_id.serialize(serializer) - } -} - -impl<'de, C> Deserialize<'de> for ClusterId { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - u32::deserialize(deserializer).map(|id| ClusterId { - raw_id: id, - _phantom: PhantomData, - }) - } -} - -impl PartialEq for ClusterId { - fn eq(&self, other: &Self) -> bool { - self.raw_id == other.raw_id - } -} - -impl Eq for ClusterId {} - -impl Hash for ClusterId { - fn hash(&self, state: &mut H) { - self.raw_id.hash(state) - } -} - -impl ClusterId { - pub fn from_raw(id: u32) -> Self { - ClusterId { - raw_id: id, - _phantom: PhantomData, - } - } -} - -pub struct ClusterIds<'a, C> { - pub(crate) id: usize, - _phantom: Invariant<'a, C>, -} - -impl Clone for ClusterIds<'_, C> { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for ClusterIds<'_, C> {} - -impl<'a, C> FreeVariable<&'a Vec>> for ClusterIds<'a, C> { - fn to_tokens(self) -> (Option, Option) - where - Self: Sized, - { - let ident = syn::Ident::new( - &format!("__hydroflow_plus_cluster_ids_{}", self.id), - Span::call_site(), - ); - let root = get_this_crate(); - let c_type = quote_type::(); - ( - None, - Some( - quote! { unsafe { ::std::mem::transmute::<_, &::std::vec::Vec<#root::ClusterId<#c_type>>>(#ident) } }, - ), - ) - } -} - -impl<'a, C> Quoted<'a, &'a Vec>> for ClusterIds<'a, C> {} - -pub struct ClusterSelfId<'a, C> { - pub(crate) id: usize, - pub(crate) _phantom: Invariant<'a, C>, -} - -impl Clone for ClusterSelfId<'_, C> { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for ClusterSelfId<'_, C> {} - -impl FreeVariable> for ClusterSelfId<'_, C> { - fn to_tokens(self) -> (Option, Option) - where - Self: Sized, - { - let ident = syn::Ident::new( - &format!("__hydroflow_plus_cluster_self_id_{}", self.id), - Span::call_site(), - ); - let root = get_this_crate(); - let c_type: syn::Type = quote_type::(); - ( - None, - Some(quote! { #root::ClusterId::<#c_type>::from_raw(#ident) }), - ) - } -} - -impl<'a, C> Quoted<'a, ClusterId> for ClusterSelfId<'a, C> {} diff --git a/hydroflow_plus/src/location/cluster/cluster_id.rs b/hydroflow_plus/src/location/cluster/cluster_id.rs new file mode 100644 index 000000000000..3fb3b2d91cb5 --- /dev/null +++ b/hydroflow_plus/src/location/cluster/cluster_id.rs @@ -0,0 +1,85 @@ +use std::fmt::{Debug, Display}; +use std::hash::Hash; +use std::marker::PhantomData; + +use serde::{Deserialize, Serialize}; + +#[repr(transparent)] +pub struct ClusterId { + pub raw_id: u32, + pub(crate) _phantom: PhantomData, +} + +impl Debug for ClusterId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ClusterId::<{}>({})", + std::any::type_name::(), + self.raw_id + ) + } +} + +impl Display for ClusterId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ClusterId::<{}>({})", + std::any::type_name::(), + self.raw_id + ) + } +} + +impl Clone for ClusterId { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for ClusterId {} + +impl Serialize for ClusterId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + self.raw_id.serialize(serializer) + } +} + +impl<'de, C> Deserialize<'de> for ClusterId { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + u32::deserialize(deserializer).map(|id| ClusterId { + raw_id: id, + _phantom: PhantomData, + }) + } +} + +impl PartialEq for ClusterId { + fn eq(&self, other: &Self) -> bool { + self.raw_id == other.raw_id + } +} + +impl Eq for ClusterId {} + +impl Hash for ClusterId { + fn hash(&self, state: &mut H) { + self.raw_id.hash(state) + } +} + +impl ClusterId { + pub fn from_raw(id: u32) -> Self { + ClusterId { + raw_id: id, + _phantom: PhantomData, + } + } +} diff --git a/hydroflow_plus/src/location/cluster/mod.rs b/hydroflow_plus/src/location/cluster/mod.rs new file mode 100644 index 000000000000..cfd83e195d8d --- /dev/null +++ b/hydroflow_plus/src/location/cluster/mod.rs @@ -0,0 +1,147 @@ +use std::marker::PhantomData; + +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use stageleft::runtime_support::FreeVariableWithContext; +use stageleft::{quote_type, QuotedWithContext}; + +use super::{Location, LocationId}; +use crate::builder::FlowState; +use crate::staging_util::{get_this_crate, Invariant}; + +pub mod cluster_id; +pub use cluster_id::ClusterId; + +pub struct Cluster<'a, C> { + pub(crate) id: usize, + pub(crate) flow_state: FlowState, + pub(crate) _phantom: Invariant<'a, C>, +} + +pub trait IsCluster { + type Tag; + fn id(&self) -> usize; +} + +impl IsCluster for Cluster<'_, C> { + type Tag = C; + fn id(&self) -> usize { + self.id + } +} + +impl<'a, C> Cluster<'a, C> { + pub fn members(&self) -> ClusterIds<'a, C> { + ClusterIds { + id: self.id, + _phantom: PhantomData, + } + } +} + +impl Clone for Cluster<'_, C> { + fn clone(&self) -> Self { + Cluster { + id: self.id, + flow_state: self.flow_state.clone(), + _phantom: PhantomData, + } + } +} + +impl<'a, C> Location<'a> for Cluster<'a, C> { + type Root = Cluster<'a, C>; + + fn root(&self) -> Self::Root { + self.clone() + } + + fn id(&self) -> LocationId { + LocationId::Cluster(self.id) + } + + fn flow_state(&self) -> &FlowState { + &self.flow_state + } + + fn is_top_level() -> bool { + true + } +} + +pub struct ClusterIds<'a, C> { + pub(crate) id: usize, + _phantom: Invariant<'a, C>, +} + +impl Clone for ClusterIds<'_, C> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for ClusterIds<'_, C> {} + +impl<'a, C: 'a, Ctx> FreeVariableWithContext for ClusterIds<'a, C> { + type O = &'a Vec>; + + fn to_tokens(self, _ctx: &Ctx) -> (Option, Option) + where + Self: Sized, + { + let ident = syn::Ident::new( + &format!("__hydroflow_plus_cluster_ids_{}", self.id), + Span::call_site(), + ); + let root = get_this_crate(); + let c_type = quote_type::(); + ( + None, + Some( + quote! { unsafe { ::std::mem::transmute::<_, &::std::vec::Vec<#root::ClusterId<#c_type>>>(#ident) } }, + ), + ) + } +} + +impl<'a, C, Ctx> QuotedWithContext<'a, &'a Vec>, Ctx> for ClusterIds<'a, C> {} + +/// A free variable representing the cluster's own ID. When spliced in +/// a quoted snippet that will run on a cluster, this turns into a [`ClusterId`]. +pub static CLUSTER_SELF_ID: ClusterSelfId = ClusterSelfId { _private: &() }; + +#[derive(Clone, Copy)] +pub struct ClusterSelfId<'a> { + _private: &'a (), +} + +impl<'a, L: Location<'a>> FreeVariableWithContext for ClusterSelfId<'a> +where + >::Root: IsCluster, +{ + type O = ClusterId<<>::Root as IsCluster>::Tag>; + + fn to_tokens(self, ctx: &L) -> (Option, Option) + where + Self: Sized, + { + let ident = syn::Ident::new( + &format!("__hydroflow_plus_cluster_self_id_{}", ctx.root().id()), + Span::call_site(), + ); + let root = get_this_crate(); + let c_type: syn::Type = quote_type::<<>::Root as IsCluster>::Tag>(); + ( + None, + Some(quote! { #root::ClusterId::<#c_type>::from_raw(#ident) }), + ) + } +} + +impl<'a, L: Location<'a>> + QuotedWithContext<'a, ClusterId<<>::Root as IsCluster>::Tag>, L> + for ClusterSelfId<'a> +where + >::Root: IsCluster, +{ +} diff --git a/hydroflow_plus/src/location/external_process.rs b/hydroflow_plus/src/location/external_process.rs index cb6d24f95b49..103aabef8695 100644 --- a/hydroflow_plus/src/location/external_process.rs +++ b/hydroflow_plus/src/location/external_process.rs @@ -46,6 +46,12 @@ impl

    Clone for ExternalProcess<'_, P> { } impl<'a, P> Location<'a> for ExternalProcess<'a, P> { + type Root = Self; + + fn root(&self) -> Self::Root { + self.clone() + } + fn id(&self) -> LocationId { LocationId::ExternalProcess(self.id) } diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index 061dfd1016cb..61693cb7c66f 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -5,7 +5,7 @@ use std::time::Duration; use hydroflow::futures::stream::Stream as FuturesStream; use hydroflow::{tokio, tokio_stream}; use proc_macro2::Span; -use stageleft::{q, Quoted}; +use stageleft::{q, QuotedWithContext}; use super::builder::FlowState; use crate::cycle::{CycleCollection, ForwardRef, ForwardRefMarker}; @@ -60,6 +60,10 @@ pub fn check_matching_location<'a, L: Location<'a>>(l1: &L, l2: &L) { } pub trait Location<'a>: Clone { + type Root; + + fn root(&self) -> Self::Root; + fn id(&self) -> LocationId; fn flow_state(&self) -> &FlowState; @@ -93,12 +97,12 @@ pub trait Location<'a>: Clone { fn source_stream + Unpin>( &self, - e: impl Quoted<'a, E>, + e: impl QuotedWithContext<'a, E, Self>, ) -> Stream where Self: Sized + NoTick, { - let e = e.splice_untyped(); + let e = e.splice_untyped_ctx(self); Stream::new( self.clone(), @@ -111,14 +115,14 @@ pub trait Location<'a>: Clone { fn source_iter>( &self, - e: impl Quoted<'a, E>, + e: impl QuotedWithContext<'a, E, Self>, ) -> Stream where Self: Sized + NoTick, { // TODO(shadaj): we mark this as unbounded because we do not yet have a representation // for bounded top-level streams, and this is the only way to generate one - let e = e.splice_untyped(); + let e = e.splice_untyped_ctx(self); Stream::new( self.clone(), @@ -129,7 +133,10 @@ pub trait Location<'a>: Clone { ) } - fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton + fn singleton( + &self, + e: impl QuotedWithContext<'a, T, Self>, + ) -> Singleton where Self: Sized + NoTick, { @@ -137,7 +144,7 @@ pub trait Location<'a>: Clone { // for bounded top-level singletons, and this is the only way to generate one let e_arr = q!([e]); - let e = e_arr.splice_untyped(); + let e = e_arr.splice_untyped_ctx(self); // we do a double persist here because if the singleton shows up on every tick, // we first persist the source so that we store that value and then persist again @@ -155,7 +162,7 @@ pub trait Location<'a>: Clone { fn source_interval( &self, - interval: impl Quoted<'a, Duration> + Copy + 'a, + interval: impl QuotedWithContext<'a, Duration, Self> + Copy + 'a, ) -> Stream where Self: Sized + NoTick, @@ -167,8 +174,8 @@ pub trait Location<'a>: Clone { fn source_interval_delayed( &self, - delay: impl Quoted<'a, Duration> + Copy + 'a, - interval: impl Quoted<'a, Duration> + Copy + 'a, + delay: impl QuotedWithContext<'a, Duration, Self> + Copy + 'a, + interval: impl QuotedWithContext<'a, Duration, Self> + Copy + 'a, ) -> Stream where Self: Sized + NoTick, diff --git a/hydroflow_plus/src/location/process.rs b/hydroflow_plus/src/location/process.rs index dcac7c3b0bc7..417cc92165cd 100644 --- a/hydroflow_plus/src/location/process.rs +++ b/hydroflow_plus/src/location/process.rs @@ -21,6 +21,12 @@ impl

    Clone for Process<'_, P> { } impl<'a, P> Location<'a> for Process<'a, P> { + type Root = Self; + + fn root(&self) -> Self::Root { + self.clone() + } + fn id(&self) -> LocationId { LocationId::Process(self.id) } diff --git a/hydroflow_plus/src/location/tick.rs b/hydroflow_plus/src/location/tick.rs index 3e9e8074baf3..809a750c9bca 100644 --- a/hydroflow_plus/src/location/tick.rs +++ b/hydroflow_plus/src/location/tick.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use proc_macro2::Span; -use stageleft::{q, Quoted}; +use stageleft::{q, QuotedWithContext}; use super::{Cluster, Location, LocationId, Process}; use crate::builder::FlowState; @@ -24,6 +24,12 @@ pub struct Tick { } impl<'a, L: Location<'a>> Location<'a> for Tick { + type Root = L::Root; + + fn root(&self) -> Self::Root { + self.l.root() + } + fn id(&self) -> LocationId { LocationId::Tick(self.id, Box::new(self.l.id())) } @@ -44,7 +50,7 @@ impl<'a, L: Location<'a>> Tick { pub fn spin_batch( &self, - batch_size: impl Quoted<'a, usize> + Copy + 'a, + batch_size: impl QuotedWithContext<'a, usize, L> + Copy + 'a, ) -> Stream<(), Self, Bounded> where L: NoTick, @@ -56,7 +62,10 @@ impl<'a, L: Location<'a>> Tick { .tick_batch(self) } - pub fn singleton(&self, e: impl Quoted<'a, T>) -> Singleton + pub fn singleton( + &self, + e: impl QuotedWithContext<'a, T, L>, + ) -> Singleton where L: NoTick, { @@ -65,13 +74,13 @@ impl<'a, L: Location<'a>> Tick { pub fn singleton_first_tick( &self, - e: impl Quoted<'a, T>, + e: impl QuotedWithContext<'a, T, Tick>, ) -> Optional where L: NoTick, { let e_arr = q!([e]); - let e = e_arr.splice_untyped(); + let e = e_arr.splice_untyped_ctx(self); Optional::new( self.clone(), diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index 4387101308bd..9fa8609d5297 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use std::ops::Deref; use std::rc::Rc; -use stageleft::{q, IntoQuotedMut, Quoted}; +use stageleft::{q, IntoQuotedMut, QuotedWithContext}; use syn::parse_quote; use crate::builder::FLOW_USED_MESSAGE; @@ -188,11 +188,12 @@ impl<'a, T, L: Location<'a>, B> Optional { Stream::new(self.location, self.ir_node.into_inner()) } - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F, L>) -> Optional { + let f = f.splice_fn1_ctx(&self.location).into(); Optional::new( self.location, HfPlusNode::Map { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -200,22 +201,27 @@ impl<'a, T, L: Location<'a>, B> Optional { pub fn flat_map, F: Fn(T) -> I + 'a>( self, - f: impl IntoQuotedMut<'a, F>, + f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { + let f = f.splice_fn1_ctx(&self.location).into(); Stream::new( self.location, HfPlusNode::FlatMap { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) } - pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { + pub fn filter bool + 'a>( + self, + f: impl IntoQuotedMut<'a, F, L>, + ) -> Optional { + let f = f.splice_fn1_borrow_ctx(&self.location).into(); Optional::new( self.location, HfPlusNode::Filter { - f: f.splice_fn1_borrow().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -223,12 +229,13 @@ impl<'a, T, L: Location<'a>, B> Optional { pub fn filter_map Option + 'a>( self, - f: impl IntoQuotedMut<'a, F>, + f: impl IntoQuotedMut<'a, F, L>, ) -> Optional { + let f = f.splice_fn1_ctx(&self.location).into(); Optional::new( self.location, HfPlusNode::FilterMap { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -356,7 +363,7 @@ impl<'a, T, L: Location<'a> + NoTick, B> Optional { pub fn sample_every( self, - interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, + interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a, ) -> Stream { let samples = self.location.source_interval(interval); let tick = self.location.tick(); diff --git a/hydroflow_plus/src/rewrites/profiler.rs b/hydroflow_plus/src/rewrites/profiler.rs index be9b030bd83b..e7d344a8f459 100644 --- a/hydroflow_plus/src/rewrites/profiler.rs +++ b/hydroflow_plus/src/rewrites/profiler.rs @@ -11,7 +11,7 @@ pub fn increment_counter(count: &mut u64) { *count += 1; } -fn quoted_any_fn<'a, F: Fn(&usize) + 'a, Q: IntoQuotedMut<'a, F>>(q: Q) -> Q { +fn quoted_any_fn<'a, F: Fn(&usize) + 'a, Q: IntoQuotedMut<'a, F, ()>>(q: Q) -> Q { q } diff --git a/hydroflow_plus/src/rewrites/properties.rs b/hydroflow_plus/src/rewrites/properties.rs index 5d5ce0d3edf5..f09b8a119266 100644 --- a/hydroflow_plus/src/rewrites/properties.rs +++ b/hydroflow_plus/src/rewrites/properties.rs @@ -34,12 +34,20 @@ fn convert_hf_to_binary(f: F) -> impl Fn(I, I) impl PropertyDatabase { /// Tags the expression as commutative. - pub fn add_commutative_tag<'a, I, A, F: Fn(&mut A, I), Q: Quoted<'a, F> + Clone>( + pub fn add_commutative_tag< + 'a, + I, + A, + F: Fn(&mut A, I), + Ctx, + Q: QuotedWithContext<'a, F, Ctx> + Clone, + >( &mut self, expr: Q, + ctx: &Ctx, ) -> Q { let expr_clone = expr.clone(); - self.commutative.insert(expr_clone.splice_untyped()); + self.commutative.insert(expr_clone.splice_untyped_ctx(ctx)); expr } @@ -91,11 +99,15 @@ mod tests { fn test_property_database() { let mut db = PropertyDatabase::default(); - assert!(!db.is_tagged_commutative(&(q!(|a: &mut i32, b: i32| *a += b).splice_untyped()))); + assert!( + !db.is_tagged_commutative(&(q!(|a: &mut i32, b: i32| *a += b).splice_untyped_ctx(&()))) + ); - let _ = db.add_commutative_tag(q!(|a: &mut i32, b: i32| *a += b)); + let _ = db.add_commutative_tag(q!(|a: &mut i32, b: i32| *a += b), &()); - assert!(db.is_tagged_commutative(&(q!(|a: &mut i32, b: i32| *a += b).splice_untyped()))); + assert!( + db.is_tagged_commutative(&(q!(|a: &mut i32, b: i32| *a += b).splice_untyped_ctx(&()))) + ); } #[test] @@ -107,7 +119,7 @@ mod tests { let tick = process.tick(); let counter_func = q!(|count: &mut i32, _| *count += 1); - let _ = database.add_commutative_tag(counter_func); + let _ = database.add_commutative_tag(counter_func, &tick); process .source_iter(q!(vec![])) diff --git a/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators-2.snap b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators-2.snap index 627f53dd38ed..cb1775df747b 100644 --- a/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators-2.snap +++ b/hydroflow_plus/src/rewrites/snapshots/hydroflow_plus__rewrites__profiler__tests__profiler_wrapping_all_operators-2.snap @@ -6,11 +6,11 @@ expression: "&pushed_down.ir()" ForEach { f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , () > ({ use crate :: __staged :: rewrites :: profiler :: tests :: * ; | n | println ! ("{}" , n) }), input: Inspect { - f: { use crate :: __staged :: rewrites :: profiler :: * ; let counter_queue = Fake ; let counters = Fake ; let my_id = 0u32 ; { counter_queue . borrow () . unbounded_send ((my_id as usize , counters . borrow () [my_id as usize])) . unwrap () ; counters . borrow_mut () [my_id as usize] = 0 ; move | _ | { myself :: increment_counter (& mut counters . borrow_mut () [my_id as usize]) ; } } }, + f: { use crate :: __staged :: rewrites :: profiler :: * ; let counter_queue__free = Fake ; let counters__free = Fake ; let my_id__free = 0u32 ; { counter_queue__free . borrow () . unbounded_send ((my_id__free as usize , counters__free . borrow () [my_id__free as usize])) . unwrap () ; counters__free . borrow_mut () [my_id__free as usize] = 0 ; move | _ | { myself :: increment_counter (& mut counters__free . borrow_mut () [my_id__free as usize]) ; } } }, input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , i32 > ({ use crate :: __staged :: rewrites :: profiler :: tests :: * ; | v | v + 1 }), input: Inspect { - f: { use crate :: __staged :: rewrites :: profiler :: * ; let counter_queue = Fake ; let counters = Fake ; let my_id = 1u32 ; { counter_queue . borrow () . unbounded_send ((my_id as usize , counters . borrow () [my_id as usize])) . unwrap () ; counters . borrow_mut () [my_id as usize] = 0 ; move | _ | { myself :: increment_counter (& mut counters . borrow_mut () [my_id as usize]) ; } } }, + f: { use crate :: __staged :: rewrites :: profiler :: * ; let counter_queue__free = Fake ; let counters__free = Fake ; let my_id__free = 1u32 ; { counter_queue__free . borrow () . unbounded_send ((my_id__free as usize , counters__free . borrow () [my_id__free as usize])) . unwrap () ; counters__free . borrow_mut () [my_id__free as usize] = 0 ; move | _ | { myself :: increment_counter (& mut counters__free . borrow_mut () [my_id__free as usize]) ; } } }, input: Source { source: Iter( { use crate :: __staged :: rewrites :: profiler :: tests :: * ; 0 .. 10 }, diff --git a/hydroflow_plus/src/runtime_context.rs b/hydroflow_plus/src/runtime_context.rs index 50e3db945035..6f93a186cbb3 100644 --- a/hydroflow_plus/src/runtime_context.rs +++ b/hydroflow_plus/src/runtime_context.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use hydroflow::scheduled::context::Context; use proc_macro2::TokenStream; use quote::quote; -use stageleft::runtime_support::FreeVariable; +use stageleft::runtime_support::FreeVariableWithContext; use crate::staging_util::Invariant; @@ -28,8 +28,10 @@ impl Default for RuntimeContext<'_> { } } -impl<'a> FreeVariable<&'a Context> for RuntimeContext<'a> { - fn to_tokens(self) -> (Option, Option) { +impl<'a, Ctx> FreeVariableWithContext for RuntimeContext<'a> { + type O = &'a Context; + + fn to_tokens(self, _ctx: &Ctx) -> (Option, Option) { (None, Some(quote!(&context))) } } diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index 35dcdcb08377..f89957e51dc2 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use std::ops::Deref; use std::rc::Rc; -use stageleft::{q, IntoQuotedMut, Quoted}; +use stageleft::{q, IntoQuotedMut, QuotedWithContext}; use crate::builder::FLOW_USED_MESSAGE; use crate::cycle::{ @@ -148,11 +148,12 @@ impl<'a, T, L: Location<'a>, B> Singleton { Stream::new(self.location, self.ir_node.into_inner()) } - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Singleton { + pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F, L>) -> Singleton { + let f = f.splice_fn1_ctx(&self.location).into(); Singleton::new( self.location, HfPlusNode::Map { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -160,22 +161,27 @@ impl<'a, T, L: Location<'a>, B> Singleton { pub fn flat_map, F: Fn(T) -> I + 'a>( self, - f: impl IntoQuotedMut<'a, F>, + f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { + let f = f.splice_fn1_ctx(&self.location).into(); Stream::new( self.location, HfPlusNode::FlatMap { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) } - pub fn filter bool + 'a>(self, f: impl IntoQuotedMut<'a, F>) -> Optional { + pub fn filter bool + 'a>( + self, + f: impl IntoQuotedMut<'a, F, L>, + ) -> Optional { + let f = f.splice_fn1_borrow_ctx(&self.location).into(); Optional::new( self.location, HfPlusNode::Filter { - f: f.splice_fn1_borrow().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -183,12 +189,13 @@ impl<'a, T, L: Location<'a>, B> Singleton { pub fn filter_map Option + 'a>( self, - f: impl IntoQuotedMut<'a, F>, + f: impl IntoQuotedMut<'a, F, L>, ) -> Optional { + let f = f.splice_fn1_ctx(&self.location).into(); Optional::new( self.location, HfPlusNode::FilterMap { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -245,7 +252,7 @@ impl<'a, T, L: Location<'a> + NoTick, B> Singleton { pub fn sample_every( self, - interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, + interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a, ) -> Stream { let samples = self.location.source_interval(interval); let tick = self.location.tick(); diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 0ec265a48f5d..266c64500e16 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -9,13 +9,13 @@ use hydroflow::futures; use hydroflow_lang::parse::Pipeline; use serde::de::DeserializeOwned; use serde::Serialize; -use stageleft::{q, IntoQuotedMut, Quoted}; +use stageleft::{q, IntoQuotedMut, QuotedWithContext}; use syn::parse_quote; use crate::builder::FLOW_USED_MESSAGE; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRefMarker, TickCycleMarker}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, TeeNode}; -use crate::location::cluster::ClusterSelfId; +use crate::location::cluster::CLUSTER_SELF_ID; use crate::location::external_process::{ExternalBincodeStream, ExternalBytesPort}; use crate::location::{ check_matching_location, CanSend, ExternalProcess, Location, LocationId, NoTick, Tick, @@ -192,12 +192,13 @@ impl<'a, T: Clone, L: Location<'a>, B, Order> Clone for Stream { impl<'a, T, L: Location<'a>, B, Order> Stream { pub fn map U + 'a>( self, - f: impl IntoQuotedMut<'a, F>, + f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { + let f = f.splice_fn1_ctx(&self.location).into(); Stream::new( self.location, HfPlusNode::Map { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -212,12 +213,13 @@ impl<'a, T, L: Location<'a>, B, Order> Stream { pub fn flat_map, F: Fn(T) -> I + 'a>( self, - f: impl IntoQuotedMut<'a, F>, + f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { + let f = f.splice_fn1_ctx(&self.location).into(); Stream::new( self.location, HfPlusNode::FlatMap { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -232,12 +234,13 @@ impl<'a, T, L: Location<'a>, B, Order> Stream { pub fn filter bool + 'a>( self, - f: impl IntoQuotedMut<'a, F>, + f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { + let f = f.splice_fn1_borrow_ctx(&self.location).into(); Stream::new( self.location, HfPlusNode::Filter { - f: f.splice_fn1_borrow().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -245,12 +248,13 @@ impl<'a, T, L: Location<'a>, B, Order> Stream { pub fn filter_map Option + 'a>( self, - f: impl IntoQuotedMut<'a, F>, + f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { + let f = f.splice_fn1_ctx(&self.location).into(); Stream::new( self.location, HfPlusNode::FilterMap { - f: f.splice_fn1().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -327,12 +331,17 @@ impl<'a, T, L: Location<'a>, B, Order> Stream { ) } - pub fn inspect(self, f: impl IntoQuotedMut<'a, F>) -> Stream { + pub fn inspect( + self, + f: impl IntoQuotedMut<'a, F, L>, + ) -> Stream { + let f = f.splice_fn1_borrow_ctx(&self.location).into(); + if L::is_top_level() { Stream::new( self.location, HfPlusNode::Persist(Box::new(HfPlusNode::Inspect { - f: f.splice_fn1_borrow().into(), + f, input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), })), ) @@ -340,7 +349,7 @@ impl<'a, T, L: Location<'a>, B, Order> Stream { Stream::new( self.location, HfPlusNode::Inspect { - f: f.splice_fn1_borrow().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -358,12 +367,15 @@ where { pub fn fold_commutative A + 'a, F: Fn(&mut A, T)>( self, - init: impl IntoQuotedMut<'a, I>, - comb: impl IntoQuotedMut<'a, F>, + init: impl IntoQuotedMut<'a, I, L>, + comb: impl IntoQuotedMut<'a, F, L>, ) -> Singleton { + let init = init.splice_fn0_ctx(&self.location).into(); + let comb = comb.splice_fn2_borrow_mut_ctx(&self.location).into(); + let mut core = HfPlusNode::Fold { - init: init.splice_fn0().into(), - acc: comb.splice_fn2_borrow_mut().into(), + init, + acc: comb, input: Box::new(self.ir_node.into_inner()), }; @@ -379,10 +391,11 @@ where pub fn reduce_commutative( self, - comb: impl IntoQuotedMut<'a, F>, + comb: impl IntoQuotedMut<'a, F, L>, ) -> Optional { + let f = comb.splice_fn2_borrow_mut_ctx(&self.location).into(); let mut core = HfPlusNode::Reduce { - f: comb.splice_fn2_borrow_mut().into(), + f, input: Box::new(self.ir_node.into_inner()), }; @@ -451,12 +464,15 @@ impl<'a, T, L: Location<'a>, B> Stream { pub fn fold A + 'a, F: Fn(&mut A, T)>( self, - init: impl IntoQuotedMut<'a, I>, - comb: impl IntoQuotedMut<'a, F>, + init: impl IntoQuotedMut<'a, I, L>, + comb: impl IntoQuotedMut<'a, F, L>, ) -> Singleton { + let init = init.splice_fn0_ctx(&self.location).into(); + let comb = comb.splice_fn2_borrow_mut_ctx(&self.location).into(); + let mut core = HfPlusNode::Fold { - init: init.splice_fn0().into(), - acc: comb.splice_fn2_borrow_mut().into(), + init, + acc: comb, input: Box::new(self.ir_node.into_inner()), }; @@ -472,10 +488,11 @@ impl<'a, T, L: Location<'a>, B> Stream { pub fn reduce( self, - comb: impl IntoQuotedMut<'a, F>, + comb: impl IntoQuotedMut<'a, F, L>, ) -> Optional { + let f = comb.splice_fn2_borrow_mut_ctx(&self.location).into(); let mut core = HfPlusNode::Reduce { - f: comb.splice_fn2_borrow_mut().into(), + f, input: Box::new(self.ir_node.into_inner()), }; @@ -549,14 +566,17 @@ impl<'a, K, V1, L: Location<'a>, B, Order> Stream<(K, V1), L, B, Order> { impl<'a, K: Eq + Hash, V, L: Location<'a>> Stream<(K, V), Tick, Bounded> { pub fn fold_keyed A + 'a, F: Fn(&mut A, V) + 'a>( self, - init: impl IntoQuotedMut<'a, I>, - comb: impl IntoQuotedMut<'a, F>, + init: impl IntoQuotedMut<'a, I, Tick>, + comb: impl IntoQuotedMut<'a, F, Tick>, ) -> Stream<(K, A), Tick, Bounded> { + let init = init.splice_fn0_ctx(&self.location).into(); + let comb = comb.splice_fn2_borrow_mut_ctx(&self.location).into(); + Stream::new( self.location, HfPlusNode::FoldKeyed { - init: init.splice_fn0().into(), - acc: comb.splice_fn2_borrow_mut().into(), + init, + acc: comb, input: Box::new(self.ir_node.into_inner()), }, ) @@ -564,12 +584,14 @@ impl<'a, K: Eq + Hash, V, L: Location<'a>> Stream<(K, V), Tick, Bounded> { pub fn reduce_keyed( self, - comb: impl IntoQuotedMut<'a, F>, + comb: impl IntoQuotedMut<'a, F, Tick>, ) -> Stream<(K, V), Tick, Bounded> { + let f = comb.splice_fn2_borrow_mut_ctx(&self.location).into(); + Stream::new( self.location, HfPlusNode::ReduceKeyed { - f: comb.splice_fn2_borrow_mut().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -582,14 +604,17 @@ where { pub fn fold_keyed_commutative A + 'a, F: Fn(&mut A, V) + 'a>( self, - init: impl IntoQuotedMut<'a, I>, - comb: impl IntoQuotedMut<'a, F>, + init: impl IntoQuotedMut<'a, I, Tick>, + comb: impl IntoQuotedMut<'a, F, Tick>, ) -> Stream<(K, A), Tick, Bounded, Order> { + let init = init.splice_fn0_ctx(&self.location).into(); + let comb = comb.splice_fn2_borrow_mut_ctx(&self.location).into(); + Stream::new( self.location, HfPlusNode::FoldKeyed { - init: init.splice_fn0().into(), - acc: comb.splice_fn2_borrow_mut().into(), + init, + acc: comb, input: Box::new(self.ir_node.into_inner()), }, ) @@ -597,12 +622,14 @@ where pub fn reduce_keyed_commutative( self, - comb: impl IntoQuotedMut<'a, F>, + comb: impl IntoQuotedMut<'a, F, Tick>, ) -> Stream<(K, V), Tick, Bounded, Order> { + let f = comb.splice_fn2_borrow_mut_ctx(&self.location).into(); + Stream::new( self.location, HfPlusNode::ReduceKeyed { - f: comb.splice_fn2_borrow_mut().into(), + f, input: Box::new(self.ir_node.into_inner()), }, ) @@ -626,7 +653,7 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { pub fn sample_every( self, - interval: impl Quoted<'a, std::time::Duration> + Copy + 'a, + interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a, ) -> Stream { let samples = self.location.source_interval(interval); let tick = self.location.tick(); @@ -635,7 +662,8 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { .all_ticks() } - pub fn for_each(self, f: impl IntoQuotedMut<'a, F>) { + pub fn for_each(self, f: impl IntoQuotedMut<'a, F, L>) { + let f = f.splice_fn1_ctx(&self.location).into(); self.location .flow_state() .borrow_mut() @@ -644,11 +672,14 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { .expect(FLOW_USED_MESSAGE) .push(HfPlusLeaf::ForEach { input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), - f: f.splice_fn1().into(), + f, }); } - pub fn dest_sink + 'a>(self, sink: impl Quoted<'a, S>) { + pub fn dest_sink + 'a>( + self, + sink: impl QuotedWithContext<'a, S, L>, + ) { self.location .flow_state() .borrow_mut() @@ -656,7 +687,7 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { .as_mut() .expect(FLOW_USED_MESSAGE) .push(HfPlusLeaf::DestSink { - sink: sink.splice_typed().into(), + sink: sink.splice_typed_ctx(&self.location).into(), input: Box::new(self.ir_node.into_inner()), }); } @@ -736,6 +767,28 @@ pub(super) fn deserialize_bincode(tagged: Option } } +impl<'a, T, C1, B, Order> Stream, B, Order> { + pub fn decouple_cluster( + self, + other: &Cluster<'a, C2>, + ) -> Stream, Unbounded, Order> + where + Cluster<'a, C1>: Location<'a, Root = Cluster<'a, C1>>, + Cluster<'a, C1>: + CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)>, + T: Clone + Serialize + DeserializeOwned, + Order: + MinOrder< as CanSend<'a, Cluster<'a, C2>>>::OutStrongestOrder>, + { + self.map(q!(move |b| ( + ClusterId::from_raw(CLUSTER_SELF_ID.raw_id), + b.clone() + ))) + .send_bincode_interleaved(other) + .assume_ordering() // this is safe because we are mapping clusters 1:1 + } +} + impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { pub fn decouple_process( self, @@ -749,28 +802,6 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { self.send_bincode::, T>(other) } - pub fn decouple_cluster( - self, - other: &Cluster<'a, C2>, - ) -> Stream, Unbounded, Order> - where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)>, - T: Clone + Serialize + DeserializeOwned, - Order: MinOrder>, - { - let self_node_id = match self.location_kind() { - LocationId::Cluster(cluster_id) => ClusterSelfId { - id: cluster_id, - _phantom: PhantomData, - }, - _ => panic!("decouple_cluster must be called on a cluster"), - }; - - self.map(q!(move |b| (self_node_id, b.clone()))) - .send_bincode_interleaved(other) - .assume_ordering() // this is safe because we are mapping clusters 1:1 - } - pub fn send_bincode, CoreType>( self, other: &L2, diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index f575ee1c3f13..060775f8af83 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -197,7 +197,6 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( p_to_proposers_i_am_leader_forward_ref, ); let (p_ballot, p_has_largest_ballot) = p_ballot_calc( - proposers, proposer_tick, p_received_max_ballot.latest_tick(proposer_tick), ); @@ -255,14 +254,12 @@ fn p_max_ballot<'a>( // Proposer logic to calculate the next ballot number. Expects p_received_max_ballot, the largest ballot received so far. Outputs streams: ballot_num, and has_largest_ballot, which only contains a value if we have the largest ballot. #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_ballot_calc<'a>( - proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, p_received_max_ballot: Singleton>, Bounded>, ) -> ( Singleton>, Bounded>, Optional<(), Tick>, Bounded>, ) { - let p_id = proposers.self_id(); let (p_ballot_num_complete_cycle, p_ballot_num) = proposer_tick.cycle_with_initial(proposer_tick.singleton(q!(0))); @@ -273,7 +270,7 @@ fn p_ballot_calc<'a>( if received_max_ballot > (Ballot { num: ballot_num, - proposer_id: p_id, + proposer_id: CLUSTER_SELF_ID, }) { received_max_ballot.num + 1 @@ -285,7 +282,7 @@ fn p_ballot_calc<'a>( let p_ballot = p_ballot_num.clone().map(q!(move |num| Ballot { num, - proposer_id: p_id + proposer_id: CLUSTER_SELF_ID })); let p_has_largest_ballot = p_received_max_ballot @@ -340,7 +337,6 @@ fn p_leader_heartbeat<'a>( Stream, Unbounded, NoOrder>, Optional, Tick>, Bounded>, ) { - let p_id = proposers.self_id(); let p_to_proposers_i_am_leader = p_is_leader .clone() .then(p_ballot) @@ -360,7 +356,8 @@ fn p_leader_heartbeat<'a>( proposers .source_interval_delayed( q!(Duration::from_secs( - (p_id.raw_id * i_am_leader_check_timeout_delay_multiplier as u32).into() + (CLUSTER_SELF_ID.raw_id * i_am_leader_check_timeout_delay_multiplier as u32) + .into() )), q!(Duration::from_secs(i_am_leader_check_timeout)), ) diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 649706f48123..fb763ff60ce3 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -52,7 +52,6 @@ pub fn paxos_bench<'a>( &clients, leader_changed, |c_to_proposers| { - let client_self_id = clients.self_id(); let (new_leader_elected, processed_payloads) = paxos_kv( &proposers, &acceptors, @@ -64,7 +63,7 @@ pub fn paxos_bench<'a>( .map(q!(move |(key, leader_id)| (leader_id, KvPayload { key, // we use our ID as the value and use that so the replica only notifies us - value: client_self_id + value: CLUSTER_SELF_ID }))) .send_bincode_interleaved(&proposers) // clients "own" certain keys, so interleaving elements from clients will not affect diff --git a/hydroflow_plus_test/src/cluster/simple_cluster.rs b/hydroflow_plus_test/src/cluster/simple_cluster.rs index 074230631b69..0a65ae3e81af 100644 --- a/hydroflow_plus_test/src/cluster/simple_cluster.rs +++ b/hydroflow_plus_test/src/cluster/simple_cluster.rs @@ -3,16 +3,14 @@ use hydroflow_plus::*; pub fn decouple_cluster<'a>(flow: &FlowBuilder<'a>) -> (Cluster<'a, ()>, Cluster<'a, ()>) { let cluster1 = flow.cluster(); let cluster2 = flow.cluster(); - let cluster_self_id = cluster2.self_id(); - let cluster1_self_id = cluster1.self_id(); cluster1 - .source_iter(q!(vec!(cluster1_self_id))) + .source_iter(q!(vec!(CLUSTER_SELF_ID))) // .for_each(q!(|message| println!("hey, {}", message))) .inspect(q!(|message| println!("Cluster1 node sending message: {}", message))) .decouple_cluster(&cluster2) .for_each(q!(move |message| println!( "My self id is {}, my message is {}", - cluster_self_id, message + CLUSTER_SELF_ID, message ))); (cluster1, cluster2) } @@ -34,15 +32,13 @@ pub fn simple_cluster<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, ()>, Cluster<' let numbers = process.source_iter(q!(0..5)); let ids = process.source_iter(cluster.members()).map(q!(|&id| id)); - let cluster_self_id = cluster.self_id(); - ids.cross_product(numbers) .map(q!(|(id, n)| (id, (id, n)))) .send_bincode(&cluster) .tick_batch(&cluster.tick()) .inspect(q!(move |n| println!( "cluster received: {:?} (self cluster id: {})", - n, cluster_self_id + n, CLUSTER_SELF_ID ))) .all_ticks() .send_bincode(&process) diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap index a9bac21e29da..751772daad6c 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir.snap @@ -12,7 +12,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u64 , u64) , (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) , (inside_batch , total_batch) | { * inside += inside_batch ; * total += total_batch ; } }), input: Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -53,7 +53,7 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; | _ | () }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: ops :: Range < usize > > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; let batch_size = { use crate :: __staged :: cluster :: compute_pi :: * ; let batch_size = 8192usize ; batch_size } ; move | _ | 0 .. batch_size }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: ops :: Range < usize > > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; let batch_size__free = { use crate :: __staged :: cluster :: compute_pi :: * ; let batch_size__free = 8192usize ; batch_size__free } ; move | _ | 0 .. batch_size__free }), input: Source { source: Spin, location_kind: Cluster( @@ -73,7 +73,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Source { source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, + { use hydroflow_plus :: __staged :: location :: * ; let interval__free = { use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval__free)) }, ), location_kind: Process( 1, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_0.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_0.snap index 0df52aa01c95..6e5ae9003103 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_0.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_0.snap @@ -3,13 +3,13 @@ source: hydroflow_plus_test/src/cluster/compute_pi.rs expression: ir.surface_syntax_string() --- 1v1 = spin (); -2v1 = flat_map (stageleft :: runtime_support :: fn1_type_hint :: < () , std :: ops :: Range < usize > > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; let batch_size = { use crate :: __staged :: cluster :: compute_pi :: * ; let batch_size = 8192usize ; batch_size } ; move | _ | 0 .. batch_size })); +2v1 = flat_map (stageleft :: runtime_support :: fn1_type_hint :: < () , std :: ops :: Range < usize > > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; let batch_size__free = { use crate :: __staged :: cluster :: compute_pi :: * ; let batch_size__free = 8192usize ; batch_size__free } ; move | _ | 0 .. batch_size__free })); 3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: location :: tick :: * ; | _ | () })); 4v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < () , (f64 , f64) > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | _ | rand :: random :: < (f64 , f64) > () })); 5v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (f64 , f64) , bool > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (x , y) | x * x + y * y < 1.0 })); 6v1 = fold :: < 'tick > (stageleft :: runtime_support :: fn0_type_hint :: < (u64 , u64) > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | | (0u64 , 0u64) }) , stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u64 , u64) , bool , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) , sample_inside | { if sample_inside { * inside += 1 ; } * total += 1 ; } })); 7v1 = map (| data | { hydroflow_plus :: runtime_support :: bincode :: serialize :: < (u64 , u64) > (& data) . unwrap () . into () }); -8v1 = dest_sink ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let c1_port = "port_0" ; let env = FAKE ; { env . port (c1_port) . connect_local_blocking :: < ConnectedDirect > () . into_sink () } }); +8v1 = dest_sink ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let c1_port__free = "port_0" ; let env__free = FAKE ; { env__free . port (c1_port__free) . connect_local_blocking :: < ConnectedDirect > () . into_sink () } }); 1v1 -> 2v1; 2v1 -> 3v1; diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap index debe85fe030f..d884f0ba97ca 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__compute_pi__tests__compute_pi_ir@surface_graph_1.snap @@ -2,11 +2,11 @@ source: hydroflow_plus_test/src/cluster/compute_pi.rs expression: ir.surface_syntax_string() --- -1v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env = FAKE ; let p2_port = "port_0" ; { env . port (p2_port) . connect_local_blocking :: < ConnectedTagged < ConnectedDirect > > () . into_source () } }); +1v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env__free = FAKE ; let p2_port__free = "port_0" ; { env__free . port (p2_port__free) . connect_local_blocking :: < ConnectedTagged < ConnectedDirect > > () . into_source () } }); 2v1 = map (| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: compute_pi :: Worker > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (u64 , u64) > (& b) . unwrap ()) }); -3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); +3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: compute_pi :: Worker > , (u64 , u64)) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); 4v1 = reduce :: < 'static > (stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (u64 , u64) , (u64 , u64) , () > ({ use crate :: __staged :: cluster :: compute_pi :: * ; | (inside , total) , (inside_batch , total_batch) | { * inside += inside_batch ; * total += total_batch ; } })); -5v1 = source_stream ({ use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }); +5v1 = source_stream ({ use hydroflow_plus :: __staged :: location :: * ; let interval__free = { use crate :: __staged :: cluster :: compute_pi :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval__free)) }); 6v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () })); 7v1 = cross_singleton (); 8v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < ((u64 , u64) , ()) , (u64 , u64) > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d })); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__many_to_many__tests__many_to_many.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__many_to_many__tests__many_to_many.snap index e10ab16062d6..6c202ced1d14 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__many_to_many__tests__many_to_many.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__many_to_many__tests__many_to_many.snap @@ -4,7 +4,7 @@ expression: built.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) , () > ({ use crate :: __staged :: cluster :: many_to_many :: * ; | n | println ! ("cluster received: {:?}" , n) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32) , () > ({ use crate :: __staged :: cluster :: many_to_many :: * ; | n | println ! ("cluster received: {:?}" , n) }), input: Network { from_location: Cluster( 0, @@ -36,7 +36,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < () > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < () > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < i32 , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < () > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Source { source: Iter( { use crate :: __staged :: cluster :: many_to_many :: * ; 0 .. 2 }, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap index 86e08bc03193..2f1d441de9d8 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir.snap @@ -9,7 +9,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | total , count | * total += count }), input: Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , (std :: string :: String , i32)) , (std :: string :: String , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , (std :: string :: String , i32)) , (std :: string :: String , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 1, @@ -78,7 +78,7 @@ expression: built.ir() ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ids [i % ids . len ()] , w) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ids__free [i % ids__free . len ()] , w) }), input: Enumerate { is_static: true, input: Map { diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap index 329c9c167fe9..417b0bc0ff76 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_0.snap @@ -5,12 +5,12 @@ expression: ir.surface_syntax_string() 1v1 = source_iter ({ use crate :: __staged :: cluster :: map_reduce :: * ; vec ! ["abc" , "abc" , "xyz" , "abc"] }); 2v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < & str , std :: string :: String > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | s | s . to_string () })); 3v1 = enumerate :: < 'static > (); -4v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ids [i % ids . len ()] , w) })); +4v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (usize , std :: string :: String) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , std :: string :: String) > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > > > (__hydroflow_plus_cluster_ids_1) } ; | (i , w) | (ids__free [i % ids__free . len ()] , w) })); 5v1 = map (| (id , data) : (hydroflow_plus :: ClusterId < _ > , std :: string :: String) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < std :: string :: String > (& data) . unwrap () . into ()) }); -6v1 = dest_sink ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env = FAKE ; let p1_port = "port_0" ; { env . port (p1_port) . connect_local_blocking :: < ConnectedDemux < ConnectedDirect > > () . into_sink () } }); -7v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env = FAKE ; let p2_port = "port_1" ; { env . port (p2_port) . connect_local_blocking :: < ConnectedTagged < ConnectedDirect > > () . into_source () } }); +6v1 = dest_sink ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env__free = FAKE ; let p1_port__free = "port_0" ; { env__free . port (p1_port__free) . connect_local_blocking :: < ConnectedDemux < ConnectedDirect > > () . into_sink () } }); +7v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let env__free = FAKE ; let p2_port__free = "port_1" ; { env__free . port (p2_port__free) . connect_local_blocking :: < ConnectedTagged < ConnectedDirect > > () . into_source () } }); 8v1 = map (| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: map_reduce :: Worker > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (std :: string :: String , i32) > (& b) . unwrap ()) }); -9v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , (std :: string :: String , i32)) , (std :: string :: String , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); +9v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: map_reduce :: Worker > , (std :: string :: String , i32)) , (std :: string :: String , i32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b })); 10v1 = reduce_keyed :: < 'static > (stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , i32 , () > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | total , count | * total += count })); 11v1 = for_each (stageleft :: runtime_support :: fn1_type_hint :: < (std :: string :: String , i32) , () > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | (string , count) | println ! ("{}: {}" , string , count) })); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_1.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_1.snap index 2c79806257e9..4024ed117c8d 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_1.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__map_reduce__tests__map_reduce_ir@surface_graph_1.snap @@ -2,13 +2,13 @@ source: hydroflow_plus_test/src/cluster/map_reduce.rs expression: ir.surface_syntax_string() --- -1v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let c2_port = "port_0" ; let env = FAKE ; { env . port (c2_port) . connect_local_blocking :: < ConnectedDirect > () . into_source () } }); +1v1 = source_stream ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let c2_port__free = "port_0" ; let env__free = FAKE ; { env__free . port (c2_port__free) . connect_local_blocking :: < ConnectedDirect > () . into_source () } }); 2v1 = map (| res | { hydroflow_plus :: runtime_support :: bincode :: deserialize :: < std :: string :: String > (& res . unwrap ()) . unwrap () }); 3v1 = map (stageleft :: runtime_support :: fn1_type_hint :: < std :: string :: String , (std :: string :: String , ()) > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | string | (string , ()) })); 4v1 = fold_keyed :: < 'tick > (stageleft :: runtime_support :: fn0_type_hint :: < i32 > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | | 0 }) , stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < i32 , () , () > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | count , _ | * count += 1 })); 5v1 = inspect (stageleft :: runtime_support :: fn1_borrow_type_hint :: < (std :: string :: String , i32) , () > ({ use crate :: __staged :: cluster :: map_reduce :: * ; | (string , count) | println ! ("partition count: {} - {}" , string , count) })); 6v1 = map (| data | { hydroflow_plus :: runtime_support :: bincode :: serialize :: < (std :: string :: String , i32) > (& data) . unwrap () . into () }); -7v1 = dest_sink ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let c1_port = "port_1" ; let env = FAKE ; { env . port (c1_port) . connect_local_blocking :: < ConnectedDirect > () . into_sink () } }); +7v1 = dest_sink ({ use hydroflow_plus :: __staged :: deploy :: deploy_runtime :: * ; let c1_port__free = "port_1" ; let env__free = FAKE ; { env__free . port (c1_port__free) . connect_local_blocking :: < ConnectedDirect > () . into_sink () } }); 1v1 -> 2v1; 2v1 -> 3v1; diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 18a1550ecc6c..23c5ab44165d 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -37,7 +37,7 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , proposer_id : p_id , }) { received_max_ballot . num + 1 } else { ballot_num } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , u32) , u32 > ({ use crate :: __staged :: cluster :: paxos :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | (received_max_ballot , ballot_num) | { if received_max_ballot > (Ballot { num : ballot_num , proposer_id : CLUSTER_SELF_ID__free , }) { received_max_ballot . num + 1 } else { ballot_num } } }), input: CrossSingleton( Tee { inner: : Chain( @@ -47,7 +47,7 @@ expression: built.ir() Chain( Chain( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_1, @@ -58,7 +58,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -83,7 +83,7 @@ expression: built.ir() Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e__free = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e__free] }, ), location_kind: Cluster( 0, @@ -108,7 +108,7 @@ expression: built.ir() Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e__free = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e__free] }, ), location_kind: Cluster( 0, @@ -130,7 +130,7 @@ expression: built.ir() ), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -162,7 +162,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > > (__hydroflow_plus_cluster_ids_0) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , ()) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( @@ -171,7 +171,7 @@ expression: built.ir() input: CrossSingleton( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | num | Ballot { num , proposer_id : p_id } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; move | num | Ballot { num , proposer_id : CLUSTER_SELF_ID__free } }), input: Tee { inner: , }, @@ -199,7 +199,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Source { source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, + { use hydroflow_plus :: __staged :: location :: * ; let interval__free = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_send_timeout__free = 1u64 ; Duration :: from_secs (i_am_leader_send_timeout__free) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval__free)) }, ), location_kind: Cluster( 0, @@ -222,7 +222,7 @@ expression: built.ir() ), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 1, @@ -237,7 +237,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -248,18 +248,18 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), input: CrossSingleton( CrossSingleton( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -291,7 +291,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Inspect { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), input: Map { @@ -308,7 +308,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout) } else { true } } }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout__free = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout__free) } else { true } } }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( @@ -341,7 +341,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Source { source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let delay = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout_delay_multiplier = 1usize ; let p_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; Duration :: from_secs ((p_id . raw_id * i_am_leader_check_timeout_delay_multiplier as u32) . into ()) } ; let interval = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay , interval)) }, + { use hydroflow_plus :: __staged :: location :: * ; let delay__free = { use crate :: __staged :: cluster :: paxos :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let i_am_leader_check_timeout_delay_multiplier__free = 1usize ; Duration :: from_secs ((CLUSTER_SELF_ID__free . raw_id * i_am_leader_check_timeout_delay_multiplier__free as u32) . into ()) } ; let interval__free = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout__free = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout__free) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay__free , interval__free)) }, ), location_kind: Cluster( 0, @@ -378,7 +378,7 @@ expression: built.ir() Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e__free = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e__free] }, ), location_kind: Cluster( 1, @@ -389,7 +389,7 @@ expression: built.ir() }, ), Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -423,19 +423,19 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received > f { Some (true) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | num_received | if num_received > f__free { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p1b , ballot) | p1b . ballot == * ballot }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p1b , ballot) | p1b . ballot == * ballot }), input: CrossSingleton( Persist( Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), input: Tee { inner: , }, @@ -498,13 +498,13 @@ expression: built.ir() inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), input: Tee { inner: , }, @@ -518,7 +518,7 @@ expression: built.ir() Persist( Source { source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e] }, + { use hydroflow_plus :: __staged :: location :: * ; let e__free = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e__free] }, ), location_kind: Cluster( 0, @@ -557,16 +557,16 @@ expression: built.ir() Tee { inner: : Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (((index , payload) , next_slot) , ballot) | P2a { ballot , slot : next_slot + index , value : Some (payload) } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (((index , payload) , next_slot) , ballot) | P2a { ballot , slot : next_slot + index , value : Some (payload) } }), input: CrossSingleton( CrossSingleton( Enumerate { is_static: false, input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 2, @@ -581,7 +581,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -592,13 +592,13 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let client_self_id = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , leader_id) | (leader_id , KvPayload { key , value : client_self_id }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , leader_id) | (leader_id , KvPayload { key , value : CLUSTER_SELF_ID__free }) }), input: CrossSingleton( CycleSource { ident: Ident { @@ -610,7 +610,7 @@ expression: built.ir() }, Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot : Ballot | ballot . proposer_id }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot : Ballot | ballot . proposer_id }), input: Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Persist( @@ -675,23 +675,23 @@ expression: built.ir() input: DeferTick( Difference( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), input: Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f { Some ((slot , value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f__free { Some ((slot , value)) } else { None } }), input: Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), input: FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = Some (value) ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = Some (value) ; } }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), input: Tee { inner: : Chain( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 1, @@ -706,7 +706,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -717,17 +717,17 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), input: CrossSingleton( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -742,7 +742,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -753,23 +753,23 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Chain( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Chain( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , (count , entry)) , ballot) | { let entry = entry . unwrap () ; if count <= f { Some (P2a { ballot , slot , value : entry . value , }) } else { None } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , (count , entry)) , ballot) | { let entry = entry . unwrap () ; if count <= f__free { Some (P2a { ballot , slot , value : entry . value , }) } else { None } } }), input: CrossSingleton( Tee { inner: , @@ -780,7 +780,7 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , ballot) | P2a { ballot , slot , value : None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , ballot) | P2a { ballot , slot , value : None } }), input: CrossSingleton( Difference( FlatMap { @@ -790,7 +790,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: , }, @@ -878,7 +878,7 @@ expression: built.ir() }, Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f + 1 { Some (slot) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f__free + 1 { Some (slot) } else { None } }), input: Tee { inner: , }, @@ -899,10 +899,10 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), input: AntiJoin( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), input: Tee { inner: , }, @@ -925,12 +925,12 @@ expression: built.ir() ), ), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in (prev_checkpoint . unwrap_or (0)) .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = Some (new_checkpoint) ; } CheckpointOrP2a :: P2a (p2a) => { if prev_checkpoint . map (| prev | p2a . slot > prev) . unwrap_or (true) && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in (prev_checkpoint . unwrap_or (0)) .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = Some (new_checkpoint) ; } CheckpointOrP2a :: P2a (p2a) => { if prev_checkpoint . map (| prev | p2a . slot > prev) . unwrap_or (true) && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), input: Persist( Chain( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), input: CrossSingleton( Tee { inner: , @@ -941,14 +941,14 @@ expression: built.ir() ), }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), input: Delta( Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , ()) , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , ()) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), input: CrossSingleton( Tee { inner: : ReduceKeyed { @@ -985,7 +985,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: CycleSource { ident: Ident { sym: cycle_0, @@ -1002,10 +1002,10 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f = 1usize ; move | num_received | if num_received == f + 1 { Some (true) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | num_received | if num_received == f__free + 1 { Some (true) } else { None } }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { inner: , }, @@ -1045,15 +1045,15 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { inner: : Sort( Chain( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -1068,7 +1068,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -1079,15 +1079,15 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), input: AntiJoin( Tee { inner: , @@ -1127,7 +1127,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < usize > , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | v | v }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < usize > , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let expected_next_slot = std :: cmp :: max (filled_slot . map (| v | v + 1) . unwrap_or (0) , highest_seq . map (| v | v + 1) . unwrap_or (0) ,) ; if sorted_payload . seq == expected_next_slot { * filled_slot = Some (sorted_payload . seq) ; } } }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < usize > , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let expected_next_slot = std :: cmp :: max (filled_slot . map (| v | v + 1) . unwrap_or (0) , highest_seq . map (| v | v + 1) . unwrap_or (0) ,) ; if sorted_payload . seq == expected_next_slot { * filled_slot = Some (sorted_payload . seq) ; } } }), input: CrossSingleton( Tee { inner: , @@ -1180,16 +1180,16 @@ expression: built.ir() input: DeferTick( Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == (last_seq . map (| s | s + 1) . unwrap_or (0)) , "Hole in log between seq {:?} and {}" , * last_seq , payload . seq) ; * last_seq = Some (payload . seq) ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == (last_seq . map (| s | s + 1) . unwrap_or (0)) , "Hole in log between seq {:?} and {}" , * last_seq , payload . seq) ; * last_seq = Some (payload . seq) ; } }), input: Persist( Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { inner: , @@ -1220,7 +1220,7 @@ expression: built.ir() input: DeferTick( Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , usize) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; let checkpoint_frequency = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if max_checkpointed_seq . map (| m | new_highest_seq - m >= checkpoint_frequency) . unwrap_or (true) { Some (new_highest_seq) } else { None } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , usize) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; let checkpoint_frequency__free = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if max_checkpointed_seq . map (| m | new_highest_seq - m >= checkpoint_frequency__free) . unwrap_or (true) { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Chain( Map { @@ -1280,7 +1280,7 @@ expression: built.ir() 2, ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -1312,7 +1312,7 @@ expression: built.ir() ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (__hydroflow_plus_cluster_ids_2) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , ()) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( @@ -1346,9 +1346,9 @@ expression: built.ir() Tee { inner: : Chain( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (u32 , ()) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | replica_payload | (replica_payload . key , ()) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (u32 , ()) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | replica_payload | (replica_payload . key , ()) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 3, @@ -1363,7 +1363,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", ], }, ), @@ -1374,15 +1374,15 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), input: Tee { inner: , }, @@ -1406,7 +1406,7 @@ expression: built.ir() }, Tee { inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f = 1usize ; move | (key , count) | { if count == f + 1 { Some (key) } else { None } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f__free = 1usize ; move | (key , count) | { if count == f__free + 1 { Some (key) } else { None } } }), input: FoldKeyed { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), @@ -1428,12 +1428,12 @@ expression: built.ir() ), input: Chain( FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node = 1usize ; move | _ | (0 .. num_clients_per_node) . map (move | i | i as u32) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node__free = 1usize ; move | _ | (0 .. num_clients_per_node__free) . map (move | i | i as u32) }), input: Tee { inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < () , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | * curr = new }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | () }), input: Delta( Tee { inner: , @@ -1479,7 +1479,7 @@ expression: built.ir() }, }, FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < std :: time :: SystemTime , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node = 1usize ; move | now | (0 .. num_clients_per_node) . map (move | virtual_id | (virtual_id , now)) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < std :: time :: SystemTime , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node__free = 1usize ; move | now | (0 .. num_clients_per_node__free) . map (move | virtual_id | (virtual_id , now)) }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { @@ -1509,8 +1509,8 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (latencies , _) | latencies }), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size = 1usize ; move | | (Rc :: new (RefCell :: new (Vec :: < u128 > :: with_capacity (median_latency_window_size))) , 0usize ,) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , u128 , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size = 1usize ; move | (latencies , write_index) , latency | { let mut latencies_mut = latencies . borrow_mut () ; if * write_index < latencies_mut . len () { latencies_mut [* write_index] = latency ; } else { latencies_mut . push (latency) ; } * write_index = (* write_index + 1) % median_latency_window_size ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size__free = 1usize ; move | | (Rc :: new (RefCell :: new (Vec :: < u128 > :: with_capacity (median_latency_window_size__free))) , 0usize ,) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , u128 , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size__free = 1usize ; move | (latencies , write_index) , latency | { let mut latencies_mut = latencies . borrow_mut () ; if * write_index < latencies_mut . len () { latencies_mut [* write_index] = latency ; } else { latencies_mut . push (latency) ; } * write_index = (* write_index + 1) % median_latency_window_size__free ; } }), input: Persist( FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < u128 > , core :: option :: Option < u128 > > ({ use hydroflow_plus :: __staged :: stream :: * ; | d | d }), @@ -1532,7 +1532,7 @@ expression: built.ir() input: Tee { inner: : Source { source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let interval = { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval)) }, + { use hydroflow_plus :: __staged :: location :: * ; let interval__free = { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval__free)) }, ), location_kind: Cluster( 2, diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__simple_cluster__tests__simple_cluster.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__simple_cluster__tests__simple_cluster.snap index a011fb746712..e7083d8ccf6d 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__simple_cluster__tests__simple_cluster.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__simple_cluster__tests__simple_cluster.snap @@ -4,7 +4,7 @@ expression: built.ir() --- [ ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32)) , () > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | (id , d) | println ! ("node received: ({}, {:?})" , id , d) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32)) , () > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | (id , d) | println ! ("node received: ({}, {:?})" , id , d) }), input: Network { from_location: Cluster( 1, @@ -19,7 +19,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| data | { hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) > (& data) . unwrap () . into () }", + "| data | { hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32) > (& data) . unwrap () . into () }", ], }, ), @@ -30,13 +30,13 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < () > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < () > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32) > (& b) . unwrap ()) }", ], }, ), ), input: Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) , () > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; let cluster_self_id = hydroflow_plus :: ClusterId :: < () > :: from_raw (__hydroflow_plus_cluster_self_id_1) ; move | n | println ! ("cluster received: {:?} (self cluster id: {})" , n , cluster_self_id) }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32) , () > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < () > :: from_raw (__hydroflow_plus_cluster_self_id_1) ; move | n | println ! ("cluster received: {:?} (self cluster id: {})" , n , CLUSTER_SELF_ID__free) }), input: Network { from_location: Process( 0, @@ -51,7 +51,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32)) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32)) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32) > (& data) . unwrap () . into ()) }", ], }, ), @@ -62,18 +62,18 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) > (& res . unwrap ()) . unwrap () }", + "| res | { hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32) > (& res . unwrap ()) . unwrap () }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32) , (hydroflow_plus :: location :: cluster :: ClusterId < () > , (hydroflow_plus :: location :: cluster :: ClusterId < () > , i32)) > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | (id , n) | (id , (id , n)) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , i32)) > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | (id , n) | (id , (id , n)) }), input: Delta( CrossProduct( Persist( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < & hydroflow_plus :: location :: cluster :: ClusterId < () > , hydroflow_plus :: location :: cluster :: ClusterId < () > > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | & id | id }), + f: stageleft :: runtime_support :: fn1_type_hint :: < & hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < () > > ({ use crate :: __staged :: cluster :: simple_cluster :: * ; | & id | id }), input: Source { source: Iter( unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < () > > > (__hydroflow_plus_cluster_ids_1) }, diff --git a/hydroflow_plus_test_local/src/local/count_elems.rs b/hydroflow_plus_test_local/src/local/count_elems.rs index 8e25dedf0db3..ee670a6bfd02 100644 --- a/hydroflow_plus_test_local/src/local/count_elems.rs +++ b/hydroflow_plus_test_local/src/local/count_elems.rs @@ -7,7 +7,7 @@ pub fn count_elems_generic<'a, T: 'a>( flow: FlowBuilder<'a>, input_stream: RuntimeData>, output: RuntimeData<&'a UnboundedSender>, -) -> impl Quoted<'a, Hydroflow<'a>> { +) -> impl QuotedWithContext<'a, Hydroflow<'a>, ()> { let process = flow.process::<()>(); let tick = process.tick(); diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_no_replay@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_no_replay@graphvis_dot.snap index 357ce04046f0..aa61b78fad2f 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_no_replay@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_no_replay@graphvis_dot.snap @@ -9,7 +9,7 @@ digraph { n2v1 [label="(n2v1) source_stream(messages)", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l std::string::String,\l std::string::String,\l >({\l use crate::__staged::local::chat_app::*;\l |s| s.to_uppercase()\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] n4v1 [label="(n4v1) cross_join_multiset::<'static, 'tick>()", shape=invhouse, fillcolor="#88aaff"] - n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, std::string::String),\l (),\l >({\l use crate::__staged::local::chat_app::*;\l let output = output;\l |t| {\l output.send(t).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, std::string::String),\l (),\l >({\l use crate::__staged::local::chat_app::*;\l let output__free = output;\l |t| {\l output__free.send(t).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n2v1 -> n3v1 n1v1 -> n4v1 [label="0"] n3v1 -> n4v1 [label="1"] diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_no_replay@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_no_replay@graphvis_mermaid.snap index 1e4f82827685..d7f3ff6decc9 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_no_replay@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_no_replay@graphvis_mermaid.snap @@ -12,7 +12,7 @@ linkStyle default stroke:#aaa 2v1[\"(2v1) source_stream(messages)"/]:::pullClass 3v1[\"

    map(
    stageleft::runtime_support::fn1_type_hint::<
    std::string::String,
    std::string::String,
    >({
    use crate::__staged::local::chat_app::*;
    |s| s.to_uppercase()
    }),
    )
    "/]:::pullClass 4v1[\"(4v1) cross_join_multiset::<'static, 'tick>()"/]:::pullClass -5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, std::string::String),
    (),
    >({
    use crate::__staged::local::chat_app::*;
    let output = output;
    |t| {
    output.send(t).unwrap();
    }
    }),
    )
    "\]:::pushClass +5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, std::string::String),
    (),
    >({
    use crate::__staged::local::chat_app::*;
    let output__free = output;
    |t| {
    output__free.send(t).unwrap();
    }
    }),
    )
    "\]:::pushClass 2v1-->3v1 1v1-->|0|4v1 3v1-->|1|4v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_replay@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_replay@graphvis_dot.snap index 4126022abd7e..3fb6e6d48cf8 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_replay@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_replay@graphvis_dot.snap @@ -10,7 +10,7 @@ digraph { n3v1 [label="(n3v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l std::string::String,\l std::string::String,\l >({\l use crate::__staged::local::chat_app::*;\l |s| s.to_uppercase()\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] n4v1 [label="(n4v1) cross_join_multiset::<'static, 'static>()", shape=invhouse, fillcolor="#88aaff"] n5v1 [label="(n5v1) multiset_delta()", shape=invhouse, fillcolor="#88aaff"] - n6v1 [label="(n6v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, std::string::String),\l (),\l >({\l use crate::__staged::local::chat_app::*;\l let output = output;\l |t| {\l output.send(t).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n6v1 [label="(n6v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, std::string::String),\l (),\l >({\l use crate::__staged::local::chat_app::*;\l let output__free = output;\l |t| {\l output__free.send(t).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n2v1 -> n3v1 n1v1 -> n4v1 [label="0"] n3v1 -> n4v1 [label="1"] diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_replay@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_replay@graphvis_mermaid.snap index 893b860d09ba..9c3d82a4e5e8 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_replay@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__chat_app__tests__chat_app_replay@graphvis_mermaid.snap @@ -13,7 +13,7 @@ linkStyle default stroke:#aaa 3v1[\"
    (3v1)
    map(
    stageleft::runtime_support::fn1_type_hint::<
    std::string::String,
    std::string::String,
    >({
    use crate::__staged::local::chat_app::*;
    |s| s.to_uppercase()
    }),
    )
    "/]:::pullClass 4v1[\"(4v1) cross_join_multiset::<'static, 'static>()"/]:::pullClass 5v1[\"(5v1) multiset_delta()"/]:::pullClass -6v1[/"
    (6v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, std::string::String),
    (),
    >({
    use crate::__staged::local::chat_app::*;
    let output = output;
    |t| {
    output.send(t).unwrap();
    }
    }),
    )
    "\]:::pushClass +6v1[/"
    (6v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, std::string::String),
    (),
    >({
    use crate::__staged::local::chat_app::*;
    let output__free = output;
    |t| {
    output__free.send(t).unwrap();
    }
    }),
    )
    "\]:::pushClass 2v1-->3v1 1v1-->|0|4v1 3v1-->|1|4v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__count_elems__tests__count@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__count_elems__tests__count@graphvis_dot.snap index 177414b8d207..374f7193775e 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__count_elems__tests__count@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__count_elems__tests__count@graphvis_dot.snap @@ -8,7 +8,7 @@ digraph { n1v1 [label="(n1v1) source_stream(input_stream)", shape=invhouse, fillcolor="#88aaff"] n2v1 [label="(n2v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l usize,\l u32,\l >({\l use crate::__staged::local::count_elems::*;\l |_| 1\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) fold::<\l 'tick,\l>(\l stageleft::runtime_support::fn0_type_hint::<\l u32,\l >({\l use crate::__staged::local::count_elems::*;\l || 0\l }),\l stageleft::runtime_support::fn2_borrow_mut_type_hint::<\l u32,\l u32,\l (),\l >({\l use crate::__staged::local::count_elems::*;\l |a, b| *a += b\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] - n4v1 [label="(n4v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::count_elems::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n4v1 [label="(n4v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::count_elems::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n5v1 [label="(n5v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n5v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__count_elems__tests__count@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__count_elems__tests__count@graphvis_mermaid.snap index f0e71c8608e7..47423697fb14 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__count_elems__tests__count@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__count_elems__tests__count@graphvis_mermaid.snap @@ -11,7 +11,7 @@ linkStyle default stroke:#aaa 1v1[\"(1v1) source_stream(input_stream)"/]:::pullClass 2v1[\"
    (2v1)
    map(
    stageleft::runtime_support::fn1_type_hint::<
    usize,
    u32,
    >({
    use crate::__staged::local::count_elems::*;
    |_| 1
    }),
    )
    "/]:::pullClass 3v1[\"
    (3v1)
    fold::<
    'tick,
    >(
    stageleft::runtime_support::fn0_type_hint::<
    u32,
    >({
    use crate::__staged::local::count_elems::*;
    || 0
    }),
    stageleft::runtime_support::fn2_borrow_mut_type_hint::<
    u32,
    u32,
    (),
    >({
    use crate::__staged::local::count_elems::*;
    |a, b| *a += b
    }),
    )
    "/]:::pullClass -4v1[/"
    (4v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::count_elems::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +4v1[/"
    (4v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::count_elems::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 5v1["(5v1) handoff"]:::otherClass 1v1-->2v1 2v1-->5v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_dot.snap index 7e84019f8a9c..a29934f0eb58 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_dot.snap @@ -17,7 +17,7 @@ digraph { n10v1 [label="(n10v1) persist::<'static>()", shape=house, fillcolor="#ffff88"] n11v1 [label="(n11v1) unique::<'tick>()", shape=house, fillcolor="#ffff88"] n12v1 [label="(n12v1) multiset_delta()", shape=house, fillcolor="#ffff88"] - n13v1 [label="(n13v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::graph_reachability::*;\l let reached_out = reached_out;\l |v| {\l reached_out.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n13v1 [label="(n13v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::graph_reachability::*;\l let reached_out__free = reached_out;\l |v| {\l reached_out__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n14v1 [label="(n14v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n15v1 [label="(n15v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n16v1 [label="(n16v1) handoff", shape=parallelogram, fillcolor="#ddddff"] diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_mermaid.snap index 3ddd47390ae3..bdd9a1910fc6 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__graph_reachability__tests__reachability@graphvis_mermaid.snap @@ -20,7 +20,7 @@ linkStyle default stroke:#aaa 10v1[/"(10v1) persist::<'static>()"\]:::pushClass 11v1[/"(11v1) unique::<'tick>()"\]:::pushClass 12v1[/"(12v1) multiset_delta()"\]:::pushClass -13v1[/"
    (13v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::graph_reachability::*;
    let reached_out = reached_out;
    |v| {
    reached_out.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +13v1[/"
    (13v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::graph_reachability::*;
    let reached_out__free = reached_out;
    |v| {
    reached_out__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 14v1["(14v1) handoff"]:::otherClass 15v1["(15v1) handoff"]:::otherClass 16v1["(16v1) handoff"]:::otherClass diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_static@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_static@graphvis_dot.snap index a42f15c8200d..c1d64fab31b3 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_static@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_static@graphvis_dot.snap @@ -10,7 +10,7 @@ digraph { n3v1 [label="(n3v1) persist::<'static>()", shape=invhouse, fillcolor="#88aaff"] n4v1 [label="(n4v1) source_iter({\l use crate::__staged::local::negation::*;\l 3..6\l})\l", shape=invhouse, fillcolor="#88aaff"] n5v1 [label="(n5v1) anti_join_multiset::<'tick, 'static>()", shape=invhouse, fillcolor="#88aaff"] - n6v1 [label="(n6v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, u32),\l (),\l >({\l use crate::__staged::local::negation::*;\l let output = output;\l |v| {\l output.send(v.0).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n6v1 [label="(n6v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, u32),\l (),\l >({\l use crate::__staged::local::negation::*;\l let output__free = output;\l |v| {\l output__free.send(v.0).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n7v1 [label="(n7v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n3v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_static@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_static@graphvis_mermaid.snap index a551e93b7771..e461c8275ec8 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_static@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_static@graphvis_mermaid.snap @@ -13,7 +13,7 @@ linkStyle default stroke:#aaa 3v1[\"(3v1) persist::<'static>()"/]:::pullClass 4v1[\"
    (4v1)
    source_iter({
    use crate::__staged::local::negation::*;
    3..6
    })
    "/]:::pullClass 5v1[\"(5v1) anti_join_multiset::<'tick, 'static>()"/]:::pullClass -6v1[/"
    (6v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, u32),
    (),
    >({
    use crate::__staged::local::negation::*;
    let output = output;
    |v| {
    output.send(v.0).unwrap();
    }
    }),
    )
    "\]:::pushClass +6v1[/"
    (6v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, u32),
    (),
    >({
    use crate::__staged::local::negation::*;
    let output__free = output;
    |v| {
    output__free.send(v.0).unwrap();
    }
    }),
    )
    "\]:::pushClass 7v1["(7v1) handoff"]:::otherClass 1v1-->2v1 2v1-->3v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_tick@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_tick@graphvis_dot.snap index 064e8c2ad217..125415caa1f9 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_tick@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_tick@graphvis_dot.snap @@ -10,7 +10,7 @@ digraph { n3v1 [label="(n3v1) persist::<'static>()", shape=invhouse, fillcolor="#88aaff"] n4v1 [label="(n4v1) source_iter({\l use crate::__staged::local::negation::*;\l 3..6\l})\l", shape=invhouse, fillcolor="#88aaff"] n5v1 [label="(n5v1) anti_join_multiset::<'tick, 'tick>()", shape=invhouse, fillcolor="#88aaff"] - n6v1 [label="(n6v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, u32),\l (),\l >({\l use crate::__staged::local::negation::*;\l let output = output;\l |v| {\l output.send(v.0).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n6v1 [label="(n6v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, u32),\l (),\l >({\l use crate::__staged::local::negation::*;\l let output__free = output;\l |v| {\l output__free.send(v.0).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n7v1 [label="(n7v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n3v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_tick@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_tick@graphvis_mermaid.snap index 72e7e8e12cc0..daa59bcd9c60 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_tick@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_static_tick@graphvis_mermaid.snap @@ -13,7 +13,7 @@ linkStyle default stroke:#aaa 3v1[\"(3v1) persist::<'static>()"/]:::pullClass 4v1[\"
    (4v1)
    source_iter({
    use crate::__staged::local::negation::*;
    3..6
    })
    "/]:::pullClass 5v1[\"(5v1) anti_join_multiset::<'tick, 'tick>()"/]:::pullClass -6v1[/"
    (6v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, u32),
    (),
    >({
    use crate::__staged::local::negation::*;
    let output = output;
    |v| {
    output.send(v.0).unwrap();
    }
    }),
    )
    "\]:::pushClass +6v1[/"
    (6v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, u32),
    (),
    >({
    use crate::__staged::local::negation::*;
    let output__free = output;
    |v| {
    output__free.send(v.0).unwrap();
    }
    }),
    )
    "\]:::pushClass 7v1["(7v1) handoff"]:::otherClass 1v1-->2v1 2v1-->3v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_static@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_static@graphvis_dot.snap index e08580edcfca..79936132989d 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_static@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_static@graphvis_dot.snap @@ -9,7 +9,7 @@ digraph { n2v1 [label="(n2v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (u32, u32),\l >({\l use crate::__staged::local::negation::*;\l |v| (v, v)\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) source_iter({\l use crate::__staged::local::negation::*;\l 3..6\l})\l", shape=invhouse, fillcolor="#88aaff"] n4v1 [label="(n4v1) anti_join_multiset::<'tick, 'static>()", shape=invhouse, fillcolor="#88aaff"] - n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, u32),\l (),\l >({\l use crate::__staged::local::negation::*;\l let output = output;\l |v| {\l output.send(v.0).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, u32),\l (),\l >({\l use crate::__staged::local::negation::*;\l let output__free = output;\l |v| {\l output__free.send(v.0).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n6v1 [label="(n6v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n4v1 [label="pos"] diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_static@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_static@graphvis_mermaid.snap index bd3ed201ed8b..6d60700e2d83 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_static@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_static@graphvis_mermaid.snap @@ -12,7 +12,7 @@ linkStyle default stroke:#aaa 2v1[\"
    (2v1)
    map(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (u32, u32),
    >({
    use crate::__staged::local::negation::*;
    |v| (v, v)
    }),
    )
    "/]:::pullClass 3v1[\"
    (3v1)
    source_iter({
    use crate::__staged::local::negation::*;
    3..6
    })
    "/]:::pullClass 4v1[\"(4v1) anti_join_multiset::<'tick, 'static>()"/]:::pullClass -5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, u32),
    (),
    >({
    use crate::__staged::local::negation::*;
    let output = output;
    |v| {
    output.send(v.0).unwrap();
    }
    }),
    )
    "\]:::pushClass +5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, u32),
    (),
    >({
    use crate::__staged::local::negation::*;
    let output__free = output;
    |v| {
    output__free.send(v.0).unwrap();
    }
    }),
    )
    "\]:::pushClass 6v1["(6v1) handoff"]:::otherClass 1v1-->2v1 2v1-->|pos|4v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_tick@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_tick@graphvis_dot.snap index 19c6f15739b8..8a4b988f4ce0 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_tick@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_tick@graphvis_dot.snap @@ -9,7 +9,7 @@ digraph { n2v1 [label="(n2v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (u32, u32),\l >({\l use crate::__staged::local::negation::*;\l |v| (v, v)\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) source_iter({\l use crate::__staged::local::negation::*;\l 3..6\l})\l", shape=invhouse, fillcolor="#88aaff"] n4v1 [label="(n4v1) anti_join_multiset::<'tick, 'tick>()", shape=invhouse, fillcolor="#88aaff"] - n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, u32),\l (),\l >({\l use crate::__staged::local::negation::*;\l let output = output;\l |v| {\l output.send(v.0).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, u32),\l (),\l >({\l use crate::__staged::local::negation::*;\l let output__free = output;\l |v| {\l output__free.send(v.0).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n6v1 [label="(n6v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n4v1 [label="pos"] diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_tick@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_tick@graphvis_mermaid.snap index f43ea369d190..16fd2abddc27 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_tick@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__anti_join_tick_tick@graphvis_mermaid.snap @@ -12,7 +12,7 @@ linkStyle default stroke:#aaa 2v1[\"
    (2v1)
    map(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (u32, u32),
    >({
    use crate::__staged::local::negation::*;
    |v| (v, v)
    }),
    )
    "/]:::pullClass 3v1[\"
    (3v1)
    source_iter({
    use crate::__staged::local::negation::*;
    3..6
    })
    "/]:::pullClass 4v1[\"(4v1) anti_join_multiset::<'tick, 'tick>()"/]:::pullClass -5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, u32),
    (),
    >({
    use crate::__staged::local::negation::*;
    let output = output;
    |v| {
    output.send(v.0).unwrap();
    }
    }),
    )
    "\]:::pushClass +5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, u32),
    (),
    >({
    use crate::__staged::local::negation::*;
    let output__free = output;
    |v| {
    output__free.send(v.0).unwrap();
    }
    }),
    )
    "\]:::pushClass 6v1["(6v1) handoff"]:::otherClass 1v1-->2v1 2v1-->|pos|4v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_static@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_static@graphvis_dot.snap index 0089af5b6d8f..b9112226c9de 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_static@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_static@graphvis_dot.snap @@ -9,7 +9,7 @@ digraph { n2v1 [label="(n2v1) persist::<'static>()", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) source_iter({\l use crate::__staged::local::negation::*;\l 3..6\l})\l", shape=invhouse, fillcolor="#88aaff"] n4v1 [label="(n4v1) difference_multiset::<'tick, 'static>()", shape=invhouse, fillcolor="#88aaff"] - n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::negation::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::negation::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n6v1 [label="(n6v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n4v1 [label="pos"] diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_static@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_static@graphvis_mermaid.snap index 78b507ade59d..c28b6b0d4d5d 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_static@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_static@graphvis_mermaid.snap @@ -12,7 +12,7 @@ linkStyle default stroke:#aaa 2v1[\"(2v1) persist::<'static>()"/]:::pullClass 3v1[\"
    (3v1)
    source_iter({
    use crate::__staged::local::negation::*;
    3..6
    })
    "/]:::pullClass 4v1[\"(4v1) difference_multiset::<'tick, 'static>()"/]:::pullClass -5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::negation::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::negation::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 6v1["(6v1) handoff"]:::otherClass 1v1-->2v1 2v1-->|pos|4v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_tick@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_tick@graphvis_dot.snap index 319db4ff376f..1e9a8e2cf4e5 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_tick@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_tick@graphvis_dot.snap @@ -9,7 +9,7 @@ digraph { n2v1 [label="(n2v1) persist::<'static>()", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) source_iter({\l use crate::__staged::local::negation::*;\l 3..6\l})\l", shape=invhouse, fillcolor="#88aaff"] n4v1 [label="(n4v1) difference_multiset::<'tick, 'tick>()", shape=invhouse, fillcolor="#88aaff"] - n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::negation::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n5v1 [label="(n5v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::negation::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n6v1 [label="(n6v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n4v1 [label="pos"] diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_tick@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_tick@graphvis_mermaid.snap index b2582dbf4bbd..e7d673d11e08 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_tick@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_static_tick@graphvis_mermaid.snap @@ -12,7 +12,7 @@ linkStyle default stroke:#aaa 2v1[\"(2v1) persist::<'static>()"/]:::pullClass 3v1[\"
    (3v1)
    source_iter({
    use crate::__staged::local::negation::*;
    3..6
    })
    "/]:::pullClass 4v1[\"(4v1) difference_multiset::<'tick, 'tick>()"/]:::pullClass -5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::negation::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +5v1[/"
    (5v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::negation::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 6v1["(6v1) handoff"]:::otherClass 1v1-->2v1 2v1-->|pos|4v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_static@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_static@graphvis_dot.snap index 3686d5a6bff6..1eea0f181a5a 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_static@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_static@graphvis_dot.snap @@ -8,7 +8,7 @@ digraph { n1v1 [label="(n1v1) source_iter({\l use crate::__staged::local::negation::*;\l 0..5\l})\l", shape=invhouse, fillcolor="#88aaff"] n2v1 [label="(n2v1) source_iter({\l use crate::__staged::local::negation::*;\l 3..6\l})\l", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) difference_multiset::<'tick, 'static>()", shape=invhouse, fillcolor="#88aaff"] - n4v1 [label="(n4v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::negation::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n4v1 [label="(n4v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::negation::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n5v1 [label="(n5v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n3v1 [label="pos"] n2v1 -> n5v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_static@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_static@graphvis_mermaid.snap index 4de7f82c02b4..ad3cf51c3a1c 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_static@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_static@graphvis_mermaid.snap @@ -11,7 +11,7 @@ linkStyle default stroke:#aaa 1v1[\"
    (1v1)
    source_iter({
    use crate::__staged::local::negation::*;
    0..5
    })
    "/]:::pullClass 2v1[\"
    (2v1)
    source_iter({
    use crate::__staged::local::negation::*;
    3..6
    })
    "/]:::pullClass 3v1[\"(3v1) difference_multiset::<'tick, 'static>()"/]:::pullClass -4v1[/"
    (4v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::negation::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +4v1[/"
    (4v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::negation::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 5v1["(5v1) handoff"]:::otherClass 1v1-->|pos|3v1 2v1-->5v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_tick@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_tick@graphvis_dot.snap index 2ef95dfc528c..67dc43c1e7fe 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_tick@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_tick@graphvis_dot.snap @@ -8,7 +8,7 @@ digraph { n1v1 [label="(n1v1) source_iter({\l use crate::__staged::local::negation::*;\l 0..5\l})\l", shape=invhouse, fillcolor="#88aaff"] n2v1 [label="(n2v1) source_iter({\l use crate::__staged::local::negation::*;\l 3..6\l})\l", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) difference_multiset::<'tick, 'tick>()", shape=invhouse, fillcolor="#88aaff"] - n4v1 [label="(n4v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::negation::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n4v1 [label="(n4v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::negation::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n5v1 [label="(n5v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n3v1 [label="pos"] n2v1 -> n5v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_tick@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_tick@graphvis_mermaid.snap index 7b67d52f7b82..af952d07e1d6 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_tick@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__negation__tests__difference_tick_tick@graphvis_mermaid.snap @@ -11,7 +11,7 @@ linkStyle default stroke:#aaa 1v1[\"
    (1v1)
    source_iter({
    use crate::__staged::local::negation::*;
    0..5
    })
    "/]:::pullClass 2v1[\"
    (2v1)
    source_iter({
    use crate::__staged::local::negation::*;
    3..6
    })
    "/]:::pullClass 3v1[\"(3v1) difference_multiset::<'tick, 'tick>()"/]:::pullClass -4v1[/"
    (4v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::negation::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +4v1[/"
    (4v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::negation::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 5v1["(5v1) handoff"]:::otherClass 1v1-->|pos|3v1 2v1-->5v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join@graphvis_dot.snap index 600618e9b6b1..ac9a190ad283 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join@graphvis_dot.snap @@ -12,7 +12,7 @@ digraph { n5v1 [label="(n5v1) join_multiset::<'tick, 'tick>()", shape=invhouse, fillcolor="#88aaff"] n6v1 [label="(n6v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, ((), ())),\l u32,\l >({\l use crate::__staged::local::teed_join::*;\l |t| t.0\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] n7v3 [label="(n7v3) handoff", shape=parallelogram, fillcolor="#ddddff"] - n8v1 [label="(n8v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::teed_join::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n8v1 [label="(n8v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::teed_join::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n9v1 [label="(n9v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n3v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join@graphvis_mermaid.snap index 78c612ffe837..4da0aa56994c 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join@graphvis_mermaid.snap @@ -15,7 +15,7 @@ linkStyle default stroke:#aaa 5v1[\"(5v1) join_multiset::<'tick, 'tick>()"/]:::pullClass 6v1[\"
    (6v1)
    map(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, ((), ())),
    u32,
    >({
    use crate::__staged::local::teed_join::*;
    |t| t.0
    }),
    )
    "/]:::pullClass 7v3["(7v3) handoff"]:::otherClass -8v1[/"
    (8v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::teed_join::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +8v1[/"
    (8v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::teed_join::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 9v1["(9v1) handoff"]:::otherClass 1v1-->2v1 2v1-->3v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_multi_node@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_multi_node@graphvis_dot.snap index 82416ab8855c..c557ed3d7ad5 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_multi_node@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_multi_node@graphvis_dot.snap @@ -6,7 +6,7 @@ digraph { node [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace", style=filled]; edge [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace"]; n1v1 [label="(n1v1) source_iter({\l use crate::__staged::local::teed_join::*;\l 0..5\l})\l", shape=invhouse, fillcolor="#88aaff"] - n2v1 [label="(n2v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::teed_join::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n2v1 [label="(n2v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::teed_join::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n1v1 -> n2v1 subgraph "cluster n1v1" { fillcolor="#dddddd" diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_multi_node@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_multi_node@graphvis_mermaid.snap index 9cbaddadeb1a..72cdbf388e0e 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_multi_node@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_multi_node@graphvis_mermaid.snap @@ -9,7 +9,7 @@ classDef pushClass fill:#ff8,stroke:#000,text-align:left,white-space:pre classDef otherClass fill:#fdc,stroke:#000,text-align:left,white-space:pre linkStyle default stroke:#aaa 1v1[\"
    (1v1)
    source_iter({
    use crate::__staged::local::teed_join::*;
    0..5
    })
    "/]:::pullClass -2v1[/"
    (2v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::teed_join::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +2v1[/"
    (2v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::teed_join::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 1v1-->2v1 subgraph sg_1v1 ["sg_1v1 stratum 0"] 1v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_twice@graphvis_dot.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_twice@graphvis_dot.snap index 267d3d849447..d35e86f614ec 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_twice@graphvis_dot.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_twice@graphvis_dot.snap @@ -12,8 +12,8 @@ digraph { n5v1 [label="(n5v1) join_multiset::<'tick, 'tick>()", shape=invhouse, fillcolor="#88aaff"] n6v1 [label="(n6v1) map(\l stageleft::runtime_support::fn1_type_hint::<\l (u32, ((), ())),\l u32,\l >({\l use crate::__staged::local::teed_join::*;\l |t| t.0\l }),\l)\l", shape=invhouse, fillcolor="#88aaff"] n7v1 [label="(n7v1) tee()", shape=house, fillcolor="#ffff88"] - n8v1 [label="(n8v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::teed_join::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] - n9v1 [label="(n9v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::teed_join::*;\l let output = output;\l |v| {\l output.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n8v1 [label="(n8v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::teed_join::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] + n9v1 [label="(n9v1) for_each(\l stageleft::runtime_support::fn1_type_hint::<\l u32,\l (),\l >({\l use crate::__staged::local::teed_join::*;\l let output__free = output;\l |v| {\l output__free.send(v).unwrap();\l }\l }),\l)\l", shape=house, fillcolor="#ffff88"] n10v1 [label="(n10v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n11v1 [label="(n11v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 diff --git a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_twice@graphvis_mermaid.snap b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_twice@graphvis_mermaid.snap index e8ddfbbd0b98..5efe825e367d 100644 --- a/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_twice@graphvis_mermaid.snap +++ b/hydroflow_plus_test_local/src/local/snapshots/hydroflow_plus_test_local__local__teed_join__tests__teed_join_twice@graphvis_mermaid.snap @@ -15,8 +15,8 @@ linkStyle default stroke:#aaa 5v1[\"(5v1) join_multiset::<'tick, 'tick>()"/]:::pullClass 6v1[\"
    (6v1)
    map(
    stageleft::runtime_support::fn1_type_hint::<
    (u32, ((), ())),
    u32,
    >({
    use crate::__staged::local::teed_join::*;
    |t| t.0
    }),
    )
    "/]:::pullClass 7v1[/"(7v1) tee()"\]:::pushClass -8v1[/"
    (8v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::teed_join::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass -9v1[/"
    (9v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::teed_join::*;
    let output = output;
    |v| {
    output.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +8v1[/"
    (8v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::teed_join::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass +9v1[/"
    (9v1)
    for_each(
    stageleft::runtime_support::fn1_type_hint::<
    u32,
    (),
    >({
    use crate::__staged::local::teed_join::*;
    let output__free = output;
    |v| {
    output__free.send(v).unwrap();
    }
    }),
    )
    "\]:::pushClass 10v1["(10v1) handoff"]:::otherClass 11v1["(11v1) handoff"]:::otherClass 1v1-->2v1 diff --git a/stageleft/src/lib.rs b/stageleft/src/lib.rs index 6113fd5979d6..d38026335275 100644 --- a/stageleft/src/lib.rs +++ b/stageleft/src/lib.rs @@ -17,7 +17,7 @@ pub mod internal { pub use stageleft_macro::{entry, q, quse_fn, runtime, top_level_mod}; pub mod runtime_support; -use runtime_support::FreeVariable; +use runtime_support::FreeVariableWithContext; use crate::runtime_support::get_final_crate_name; @@ -110,12 +110,12 @@ impl QuotedContext for BorrowBounds<'_> { } } -pub trait Quoted<'a, T>: FreeVariable { - fn splice_untyped(self) -> syn::Expr +pub trait QuotedWithContext<'a, T, Ctx>: FreeVariableWithContext { + fn splice_untyped_ctx(self, ctx: &Ctx) -> syn::Expr where Self: Sized, { - let (prelude, value) = self.to_tokens(); + let (prelude, value) = self.to_tokens(ctx); if prelude.is_some() { panic!("Quoted value should not have prelude"); } @@ -123,9 +123,96 @@ pub trait Quoted<'a, T>: FreeVariable { syn::parse2(value.unwrap()).unwrap() } + fn splice_typed_ctx(self, ctx: &Ctx) -> syn::Expr + where + Self: Sized, + { + let inner_expr = self.splice_untyped_ctx(ctx); + let stageleft_root = stageleft_root(); + + let out_type = quote_type::(); + + syn::parse_quote! { + #stageleft_root::runtime_support::type_hint::<#out_type>(#inner_expr) + } + } + + fn splice_fn0_ctx(self, ctx: &Ctx) -> syn::Expr + where + Self: Sized, + T: Fn() -> O, + { + let inner_expr = self.splice_untyped_ctx(ctx); + let stageleft_root = stageleft_root(); + + let out_type = quote_type::(); + + syn::parse_quote! { + #stageleft_root::runtime_support::fn0_type_hint::<#out_type>(#inner_expr) + } + } + + fn splice_fn1_ctx(self, ctx: &Ctx) -> syn::Expr + where + Self: Sized, + T: Fn(I) -> O, + { + let inner_expr = self.splice_untyped_ctx(ctx); + let stageleft_root = stageleft_root(); + + let in_type = quote_type::(); + let out_type = quote_type::(); + + syn::parse_quote! { + #stageleft_root::runtime_support::fn1_type_hint::<#in_type, #out_type>(#inner_expr) + } + } + + fn splice_fn1_borrow_ctx(self, ctx: &Ctx) -> syn::Expr + where + Self: Sized, + T: Fn(&I) -> O, + { + let inner_expr = self.splice_untyped_ctx(ctx); + let stageleft_root = stageleft_root(); + + let in_type = quote_type::(); + let out_type = quote_type::(); + + syn::parse_quote! { + #stageleft_root::runtime_support::fn1_borrow_type_hint::<#in_type, #out_type>(#inner_expr) + } + } + + fn splice_fn2_borrow_mut_ctx(self, ctx: &Ctx) -> syn::Expr + where + Self: Sized, + T: Fn(&mut I1, I2) -> O, + { + let inner_expr = self.splice_untyped_ctx(ctx); + let stageleft_root = stageleft_root(); + + let in1_type = quote_type::(); + let in2_type = quote_type::(); + let out_type = quote_type::(); + + syn::parse_quote! { + #stageleft_root::runtime_support::fn2_borrow_mut_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr) + } + } + + fn splice_untyped(self) -> syn::Expr + where + Self: Sized, + Ctx: Default, + { + self.splice_untyped_ctx(&Default::default()) + } + fn splice_typed(self) -> syn::Expr where Self: Sized, + Ctx: Default, { let inner_expr = self.splice_untyped(); let stageleft_root = stageleft_root(); @@ -140,6 +227,7 @@ pub trait Quoted<'a, T>: FreeVariable { fn splice_fn0(self) -> syn::Expr where Self: Sized, + Ctx: Default, T: Fn() -> O, { let inner_expr = self.splice_untyped(); @@ -155,6 +243,7 @@ pub trait Quoted<'a, T>: FreeVariable { fn splice_fn1(self) -> syn::Expr where Self: Sized, + Ctx: Default, T: Fn(I) -> O, { let inner_expr = self.splice_untyped(); @@ -171,6 +260,7 @@ pub trait Quoted<'a, T>: FreeVariable { fn splice_fn1_borrow(self) -> syn::Expr where Self: Sized, + Ctx: Default, T: Fn(&I) -> O, { let inner_expr = self.splice_untyped(); @@ -187,6 +277,7 @@ pub trait Quoted<'a, T>: FreeVariable { fn splice_fn2_borrow_mut(self) -> syn::Expr where Self: Sized, + Ctx: Default, T: Fn(&mut I1, I2) -> O, { let inner_expr = self.splice_untyped(); @@ -202,6 +293,9 @@ pub trait Quoted<'a, T>: FreeVariable { } } +pub trait Quoted<'a, T>: QuotedWithContext<'a, T, ()> {} +impl<'a, T, F: QuotedWithContext<'a, T, ()>> Quoted<'a, T> for F {} + fn stageleft_root() -> syn::Ident { let stageleft_crate = proc_macro_crate::crate_name("stageleft") .unwrap_or_else(|_| panic!("stageleft should be present in `Cargo.toml`")); @@ -212,12 +306,19 @@ fn stageleft_root() -> syn::Ident { } } -pub trait IntoQuotedOnce<'a, T>: - FnOnce(&mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T +pub trait IntoQuotedOnce<'a, T, Ctx>: + for<'b> FnOnce( + &'b Ctx, + &mut String, + &mut &'static str, + &mut TokenStream, + &mut CaptureVec, + bool, + ) -> T + 'a - + Quoted<'a, T> + + QuotedWithContext<'a, T, Ctx> { - fn boxed(self) -> Box> + fn boxed(self) -> Box> where Self: Sized, { @@ -228,31 +329,60 @@ pub trait IntoQuotedOnce<'a, T>: impl< 'a, T, - F: FnOnce(&mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T + 'a, - > Quoted<'a, T> for F + Ctx, + F: for<'b> FnOnce( + &'b Ctx, + &mut String, + &mut &'static str, + &mut TokenStream, + &mut CaptureVec, + bool, + ) -> T + + 'a, + > QuotedWithContext<'a, T, Ctx> for F { } impl< 'a, T, - F: FnOnce(&mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T + 'a, - > IntoQuotedOnce<'a, T> for F + Ctx, + F: for<'b> FnOnce( + &'b Ctx, + &mut String, + &mut &'static str, + &mut TokenStream, + &mut CaptureVec, + bool, + ) -> T + + 'a, + > IntoQuotedOnce<'a, T, Ctx> for F { } impl< T, - F: FnOnce(&mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T, - > FreeVariable for F + Ctx, + F: for<'b> FnOnce( + &'b Ctx, + &mut String, + &mut &'static str, + &mut TokenStream, + &mut CaptureVec, + bool, + ) -> T, + > FreeVariableWithContext for F { - fn to_tokens(self) -> (Option, Option) { + type O = T; + + fn to_tokens(self, ctx: &Ctx) -> (Option, Option) { let mut module_path = String::new(); let mut crate_name = ""; let mut expr_tokens = TokenStream::new(); let mut free_variables = Vec::new(); // this is an uninit value so we can't drop it std::mem::forget(self( + ctx, &mut module_path, &mut crate_name, &mut expr_tokens, @@ -307,16 +437,25 @@ impl< } } -pub trait IntoQuotedMut<'a, T>: - FnMut(&mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T + 'a +pub trait IntoQuotedMut<'a, T, Ctx>: + FnMut(&Ctx, &mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T + 'a { } impl< 'a, T, - F: FnMut(&mut String, &mut &'static str, &mut TokenStream, &mut CaptureVec, bool) -> T + 'a, - > IntoQuotedMut<'a, T> for F + Ctx, + F: FnMut( + &Ctx, + &mut String, + &mut &'static str, + &mut TokenStream, + &mut CaptureVec, + bool, + ) -> T + + 'a, + > IntoQuotedMut<'a, T, Ctx> for F { } @@ -326,7 +465,7 @@ pub struct RuntimeData { _phantom: PhantomData, } -impl<'a, T: 'a> Quoted<'a, T> for RuntimeData {} +impl<'a, T: 'a, Ctx> QuotedWithContext<'a, T, Ctx> for RuntimeData {} impl Copy for RuntimeData {} @@ -346,8 +485,10 @@ impl RuntimeData { } } -impl FreeVariable for RuntimeData { - fn to_tokens(self) -> (Option, Option) { +impl FreeVariableWithContext for RuntimeData { + type O = T; + + fn to_tokens(self, _ctx: &Ctx) -> (Option, Option) { let ident = syn::Ident::new(self.ident, Span::call_site()); (None, Some(quote!(#ident))) } diff --git a/stageleft/src/runtime_support.rs b/stageleft/src/runtime_support.rs index cdca23ec078e..2f825152b74d 100644 --- a/stageleft/src/runtime_support.rs +++ b/stageleft/src/runtime_support.rs @@ -5,7 +5,7 @@ use std::mem::MaybeUninit; use proc_macro2::{Span, TokenStream}; use quote::quote; -use crate::Quoted; +use crate::QuotedWithContext; pub fn get_final_crate_name(crate_name: &str) -> TokenStream { let final_crate = proc_macro_crate::crate_name(crate_name) @@ -75,11 +75,29 @@ impl ParseFromLiteral for bool { impl_parse_from_literal_numeric!(i8, i16, i32, i64, i128, isize); impl_parse_from_literal_numeric!(u8, u16, u32, u64, u128, usize); -pub trait FreeVariable { - fn to_tokens(self) -> (Option, Option) +pub trait FreeVariableWithContext { + type O; + + fn to_tokens(self, ctx: &Ctx) -> (Option, Option) where Self: Sized; + fn uninitialized(&self, _ctx: &Ctx) -> Self::O { + #[expect(clippy::uninit_assumed_init, reason = "this code should never run")] + unsafe { + MaybeUninit::uninit().assume_init() + } + } +} + +pub trait FreeVariable: FreeVariableWithContext<(), O = O> { + fn to_tokens(self) -> (Option, Option) + where + Self: Sized, + { + FreeVariableWithContext::to_tokens(self, &()) + } + fn uninitialized(&self) -> O { #[expect(clippy::uninit_assumed_init, reason = "this code should never run")] unsafe { @@ -88,16 +106,20 @@ pub trait FreeVariable { } } +impl> FreeVariable for T {} + macro_rules! impl_free_variable_from_literal_numeric { ($($ty:ty),*) => { $( - impl FreeVariable<$ty> for $ty { - fn to_tokens(self) -> (Option, Option) { + impl FreeVariableWithContext for $ty { + type O = $ty; + + fn to_tokens(self, _ctx: &Ctx) -> (Option, Option) { (None, Some(quote!(#self))) } } - impl<'a> Quoted<'a, $ty> for $ty {} + impl<'a, Ctx> QuotedWithContext<'a, $ty, Ctx> for $ty {} )* }; } @@ -105,8 +127,10 @@ macro_rules! impl_free_variable_from_literal_numeric { impl_free_variable_from_literal_numeric!(i8, i16, i32, i64, i128, isize); impl_free_variable_from_literal_numeric!(u8, u16, u32, u64, u128, usize); -impl FreeVariable<&str> for &str { - fn to_tokens(self) -> (Option, Option) { +impl FreeVariableWithContext for &str { + type O = &'static str; + + fn to_tokens(self, _ctx: &Ctx) -> (Option, Option) { (None, Some(quote!(#self))) } } @@ -142,8 +166,10 @@ pub fn create_import( } } -impl FreeVariable for Import { - fn to_tokens(self) -> (Option, Option) { +impl FreeVariableWithContext for Import { + type O = T; + + fn to_tokens(self, _ctx: &Ctx) -> (Option, Option) { let final_crate_root = get_final_crate_name(self.crate_name); let module_path = syn::parse_str::(self.module_path).unwrap(); diff --git a/stageleft_macro/src/lib.rs b/stageleft_macro/src/lib.rs index c6972c694c23..279a53a88c71 100644 --- a/stageleft_macro/src/lib.rs +++ b/stageleft_macro/src/lib.rs @@ -370,7 +370,7 @@ pub fn entry( #root::runtime_support::set_macro_to_crate(macro_crate_name, final_crate_name); let output_core = { - #root::Quoted::splice_untyped(#input_name #passed_generics(#root::QuotedContext::create(), #(#params_to_pass),*)) + #root::QuotedWithContext::splice_untyped_ctx(#input_name #passed_generics(#root::QuotedContext::create(), #(#params_to_pass),*), &()) }; let final_crate_root = #root::runtime_support::get_final_crate_name(final_crate_name); diff --git a/stageleft_macro/src/quote_impl/free_variable/mod.rs b/stageleft_macro/src/quote_impl/free_variable/mod.rs index d323b26ea141..23c6ba85f19c 100644 --- a/stageleft_macro/src/quote_impl/free_variable/mod.rs +++ b/stageleft_macro/src/quote_impl/free_variable/mod.rs @@ -2,6 +2,7 @@ use std::collections::{BTreeSet, HashSet}; mod prelude; use prelude::is_prelude; +use quote::ToTokens; #[derive(Debug)] pub struct ScopeStack { @@ -64,25 +65,25 @@ pub struct FreeVariableVisitor { pub current_scope: ScopeStack, } -impl<'ast> syn::visit::Visit<'ast> for FreeVariableVisitor { - fn visit_expr_closure(&mut self, i: &'ast syn::ExprClosure) { +impl syn::visit_mut::VisitMut for FreeVariableVisitor { + fn visit_expr_closure_mut(&mut self, i: &mut syn::ExprClosure) { self.current_scope.push(); - i.inputs.iter().for_each(|input| { - self.visit_pat(input); + i.inputs.iter_mut().for_each(|input| { + self.visit_pat_mut(input); }); - syn::visit::visit_expr_closure(self, i); + syn::visit_mut::visit_expr_closure_mut(self, i); self.current_scope.pop(); } - fn visit_item_fn(&mut self, i: &'ast syn::ItemFn) { + fn visit_item_fn_mut(&mut self, i: &mut syn::ItemFn) { self.current_scope.push(); - syn::visit::visit_item_fn(self, i); + syn::visit_mut::visit_item_fn_mut(self, i); self.current_scope.pop(); } - fn visit_generic_param(&mut self, i: &'ast syn::GenericParam) { + fn visit_generic_param_mut(&mut self, i: &mut syn::GenericParam) { match i { syn::GenericParam::Type(type_param) => { self.current_scope.insert_type(type_param.ident.clone()); @@ -97,140 +98,157 @@ impl<'ast> syn::visit::Visit<'ast> for FreeVariableVisitor { } } - fn visit_block(&mut self, i: &'ast syn::Block) { + fn visit_block_mut(&mut self, i: &mut syn::Block) { self.current_scope.push(); - syn::visit::visit_block(self, i); + syn::visit_mut::visit_block_mut(self, i); self.current_scope.pop(); } - fn visit_local(&mut self, i: &'ast syn::Local) { - i.init.iter().for_each(|init| { - syn::visit::visit_local_init(self, init); + fn visit_local_mut(&mut self, i: &mut syn::Local) { + i.init.iter_mut().for_each(|init| { + syn::visit_mut::visit_local_init_mut(self, init); }); - match &i.pat { + match &mut i.pat { syn::Pat::Ident(pat_ident) => { self.current_scope.insert_term(pat_ident.ident.clone()); } syn::Pat::Type(pat_type) => { - self.visit_pat(&pat_type.pat); + self.visit_pat_mut(&mut pat_type.pat); } syn::Pat::Wild(_) => { // Do nothing } syn::Pat::Tuple(pat_tuple) => { - for el in &pat_tuple.elems { - self.visit_pat(el); + for el in &mut pat_tuple.elems { + self.visit_pat_mut(el); } } _ => panic!("Local variables must be identifiers, got {:?}", i.pat), } } - fn visit_ident(&mut self, i: &'ast proc_macro2::Ident) { + fn visit_ident_mut(&mut self, i: &mut proc_macro2::Ident) { if !self.current_scope.contains_term(i) { self.free_variables.insert(i.clone()); + *i = syn::Ident::new(&format!("{}__free", i), i.span()); } } - fn visit_lifetime(&mut self, i: &'ast syn::Lifetime) { + fn visit_lifetime_mut(&mut self, i: &mut syn::Lifetime) { if !self.current_scope.contains_type(&i.ident) { self.free_variables.insert(i.ident.clone()); + i.ident = syn::Ident::new(&format!("{}__free", i.ident), i.ident.span()); } } - fn visit_path(&mut self, i: &'ast syn::Path) { + fn visit_path_mut(&mut self, i: &mut syn::Path) { if i.leading_colon.is_none() && !is_prelude(&i.segments.first().unwrap().ident) { - let node = i.segments.first().unwrap(); - if i.segments.len() == 1 && !self.current_scope.contains_term(&node.ident) { + let one_segment = i.segments.len() == 1; + let node = i.segments.first_mut().unwrap(); + if one_segment && !self.current_scope.contains_term(&node.ident) { self.free_variables.insert(node.ident.clone()); + node.ident = syn::Ident::new(&format!("{}__free", node.ident), node.ident.span()); } } - for node in i.segments.iter() { - self.visit_path_arguments(&node.arguments); + for node in i.segments.iter_mut() { + self.visit_path_arguments_mut(&mut node.arguments); } } - fn visit_arm(&mut self, i: &'ast syn::Arm) { + fn visit_arm_mut(&mut self, i: &mut syn::Arm) { self.current_scope.push(); - syn::visit::visit_arm(self, i); + syn::visit_mut::visit_arm_mut(self, i); self.current_scope.pop(); } - fn visit_field_pat(&mut self, i: &'ast syn::FieldPat) { - for it in &i.attrs { - self.visit_attribute(it); + fn visit_field_pat_mut(&mut self, i: &mut syn::FieldPat) { + for it in &mut i.attrs { + self.visit_attribute_mut(it); } - self.visit_pat(&i.pat); + self.visit_pat_mut(&mut i.pat); } - fn visit_pat_ident(&mut self, i: &'ast syn::PatIdent) { + fn visit_pat_ident_mut(&mut self, i: &mut syn::PatIdent) { self.current_scope.insert_term(i.ident.clone()); } - fn visit_expr_method_call(&mut self, i: &'ast syn::ExprMethodCall) { - syn::visit::visit_expr(self, &i.receiver); - for arg in &i.args { - self.visit_expr(arg); + fn visit_expr_method_call_mut(&mut self, i: &mut syn::ExprMethodCall) { + syn::visit_mut::visit_expr_mut(self, &mut i.receiver); + for arg in &mut i.args { + self.visit_expr_mut(arg); } } - fn visit_type(&mut self, _: &'ast syn::Type) {} + fn visit_type_mut(&mut self, _: &mut syn::Type) {} - fn visit_expr_struct(&mut self, node: &'ast syn::ExprStruct) { - for it in &node.attrs { - self.visit_attribute(it); + fn visit_expr_struct_mut(&mut self, node: &mut syn::ExprStruct) { + for it in &mut node.attrs { + self.visit_attribute_mut(it); } - if let Some(it) = &node.qself { - self.visit_qself(it); + if let Some(it) = &mut node.qself { + self.visit_qself_mut(it); } // No need to capture the struct path // self.visit_path(&node.path); - for el in syn::punctuated::Punctuated::pairs(&node.fields) { - let it = el.value(); - self.visit_expr(&it.expr); + for el in syn::punctuated::Punctuated::pairs_mut(&mut node.fields) { + let it = el.into_value(); + self.visit_expr_mut(&mut it.expr); } - if let Some(it) = &node.rest { - self.visit_expr(it); + if let Some(it) = &mut node.rest { + self.visit_expr_mut(it); } } - fn visit_expr_field(&mut self, i: &'ast syn::ExprField) { - self.visit_expr(&i.base); + fn visit_expr_field_mut(&mut self, i: &mut syn::ExprField) { + self.visit_expr_mut(&mut i.base); } - fn visit_macro(&mut self, i: &'ast syn::Macro) { + fn visit_macro_mut(&mut self, i: &mut syn::Macro) { // TODO(shadaj): emit a warning if our guess at parsing fails match i.delimiter { - syn::MacroDelimiter::Paren(_binding_0) => i - .parse_body_with( - syn::punctuated::Punctuated::::parse_terminated, - ) - .ok() - .iter() - .flatten() - .for_each(|expr| { - self.visit_expr(expr); - }), - syn::MacroDelimiter::Brace(_binding_0) => i - .parse_body_with(syn::Block::parse_within) - .ok() - .iter() - .flatten() - .for_each(|stmt| { - self.visit_stmt(stmt); - }), - syn::MacroDelimiter::Bracket(_binding_0) => i - .parse_body_with( - syn::punctuated::Punctuated::::parse_terminated, - ) - .ok() - .iter() - .flatten() - .for_each(|expr| { - self.visit_expr(expr); - }), + syn::MacroDelimiter::Paren(_binding_0) => { + i.tokens = i + .parse_body_with( + syn::punctuated::Punctuated::::parse_terminated, + ) + .ok() + .map(|mut exprs| { + for arg in &mut exprs { + self.visit_expr_mut(arg); + } + exprs.to_token_stream() + }) + .unwrap_or(i.tokens.clone()); + } + syn::MacroDelimiter::Brace(_binding_0) => { + i.tokens = i + .parse_body_with(syn::Block::parse_within) + .ok() + .map(|mut stmts| { + for stmt in &mut stmts { + self.visit_stmt_mut(stmt); + } + syn::punctuated::Punctuated::::from_iter(stmts) + .to_token_stream() + }) + .unwrap_or(i.tokens.clone()); + } + syn::MacroDelimiter::Bracket(_binding_0) => { + i.tokens = i + .parse_body_with( + syn::punctuated::Punctuated::::parse_terminated, + ) + .ok() + .map(|mut exprs| { + for arg in &mut exprs { + self.visit_expr_mut(arg); + } + exprs.to_token_stream() + }) + .unwrap_or(i.tokens.clone()); + } } } } diff --git a/stageleft_macro/src/quote_impl/mod.rs b/stageleft_macro/src/quote_impl/mod.rs index f26b533be7d3..475999680ee1 100644 --- a/stageleft_macro/src/quote_impl/mod.rs +++ b/stageleft_macro/src/quote_impl/mod.rs @@ -1,32 +1,34 @@ use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; -use syn::visit::Visit; +use syn::visit_mut::VisitMut; use self::free_variable::FreeVariableVisitor; mod free_variable; -pub fn q_impl(root: TokenStream, expr: syn::Expr) -> TokenStream { +pub fn q_impl(root: TokenStream, mut expr: syn::Expr) -> TokenStream { let mut visitor = FreeVariableVisitor::default(); - visitor.visit_expr(&expr); + visitor.visit_expr_mut(&mut expr); let unitialized_free_variables = visitor.free_variables.iter().map(|i| { - let mut i_without_span = i.clone(); - i_without_span.set_span(Span::call_site()); - let ident_str = i.to_string(); + let ident_str = format!("{}__free", i); + + let i_renamed = syn::Ident::new(&ident_str, i.span()); + let mut i_outer = i.clone(); + i_outer.set_span(Span::call_site()); + quote!( #[allow(unused, non_upper_case_globals, non_snake_case)] - let #i = { - let _out = ::#root::runtime_support::FreeVariable::uninitialized(&#i_without_span); - _vec_to_set.push((#ident_str.to_string(), ::#root::runtime_support::FreeVariable::to_tokens(#i_without_span))); + let #i_renamed = { + let _out = ::#root::runtime_support::FreeVariableWithContext::uninitialized(&#i_outer, __stageleft_ctx); + _vec_to_set.push((#ident_str.to_string(), ::#root::runtime_support::FreeVariableWithContext::to_tokens(#i_outer, __stageleft_ctx))); _out }; ) }); let uninit_forgets = visitor.free_variables.iter().map(|i| { - let mut i_without_span = i.clone(); - i_without_span.set_span(Span::call_site()); + let i_without_span = syn::Ident::new(&format!("{}__free", i), Span::call_site()); quote!( #[allow(unused, non_upper_case_globals, non_snake_case)] ::std::mem::forget(#i_without_span); @@ -38,7 +40,7 @@ pub fn q_impl(root: TokenStream, expr: syn::Expr) -> TokenStream { syn::parse_str(&expr.clone().into_token_stream().to_string()).unwrap(); quote!({ - move |set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut #root::internal::TokenStream, _vec_to_set: &mut #root::internal::CaptureVec, run: bool| { + move |__stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut #root::internal::TokenStream, _vec_to_set: &mut #root::internal::CaptureVec, run: bool| { #(#unitialized_free_variables;)* *set_mod = module_path!().to_string(); diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local@macro_tokens.snap index 7a48e3cdfdef..07671ec33863 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, @@ -12,12 +13,18 @@ fn main() { run: bool| { #[allow(unused, non_upper_case_globals, non_snake_case)] - let x = { - let _out = ::stageleft::runtime_support::FreeVariable::uninitialized(&x); + let x__free = { + let _out = ::stageleft::runtime_support::FreeVariableWithContext::uninitialized( + &x, + __stageleft_ctx, + ); _vec_to_set .push(( - "x".to_string(), - ::stageleft::runtime_support::FreeVariable::to_tokens(x), + "x__free".to_string(), + ::stageleft::runtime_support::FreeVariableWithContext::to_tokens( + x, + __stageleft_ctx, + ), )); _out }; @@ -25,16 +32,17 @@ fn main() { *set_crate_name = option_env!("STAGELEFT_FINAL_CRATE_NAME") .unwrap_or(env!("CARGO_PKG_NAME")); *set_tokens = stageleft::internal::quote! { - (x + 2) + (x + 2) + (x__free + 2) + (x__free + 2) }; if !run { #[allow(unused, non_upper_case_globals, non_snake_case)] - ::std::mem::forget(x); + ::std::mem::forget(x__free); unsafe { return ::std::mem::MaybeUninit::uninit().assume_init(); } } - #[allow(unreachable_code, unused_qualifications)] { (x + 2) + (x + 2) } + #[allow(unreachable_code, unused_qualifications)] + { (x__free + 2) + (x__free + 2) } } } } diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local_block@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local_block@macro_tokens.snap index b5f826004f69..3446a20152f0 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local_block@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local_block@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, @@ -12,12 +13,18 @@ fn main() { run: bool| { #[allow(unused, non_upper_case_globals, non_snake_case)] - let x = { - let _out = ::stageleft::runtime_support::FreeVariable::uninitialized(&x); + let x__free = { + let _out = ::stageleft::runtime_support::FreeVariableWithContext::uninitialized( + &x, + __stageleft_ctx, + ); _vec_to_set .push(( - "x".to_string(), - ::stageleft::runtime_support::FreeVariable::to_tokens(x), + "x__free".to_string(), + ::stageleft::runtime_support::FreeVariableWithContext::to_tokens( + x, + __stageleft_ctx, + ), )); _out }; @@ -25,11 +32,11 @@ fn main() { *set_crate_name = option_env!("STAGELEFT_FINAL_CRATE_NAME") .unwrap_or(env!("CARGO_PKG_NAME")); *set_tokens = stageleft::internal::quote! { - { let _ = x + 2; let _ = x + 2; } + { let _ = x__free + 2; let _ = x__free + 2; } }; if !run { #[allow(unused, non_upper_case_globals, non_snake_case)] - ::std::mem::forget(x); + ::std::mem::forget(x__free); unsafe { return ::std::mem::MaybeUninit::uninit().assume_init(); } @@ -37,8 +44,8 @@ fn main() { #[allow(unreachable_code, unused_qualifications)] { { - let _ = x + 2; - let _ = x + 2; + let _ = x__free + 2; + let _ = x__free + 2; } } } diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local_block_let@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local_block_let@macro_tokens.snap index 84a8c67dea1d..cecf987e32d9 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local_block_let@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_copy_local_block_let@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, @@ -12,12 +13,18 @@ fn main() { run: bool| { #[allow(unused, non_upper_case_globals, non_snake_case)] - let x = { - let _out = ::stageleft::runtime_support::FreeVariable::uninitialized(&x); + let x__free = { + let _out = ::stageleft::runtime_support::FreeVariableWithContext::uninitialized( + &x, + __stageleft_ctx, + ); _vec_to_set .push(( - "x".to_string(), - ::stageleft::runtime_support::FreeVariable::to_tokens(x), + "x__free".to_string(), + ::stageleft::runtime_support::FreeVariableWithContext::to_tokens( + x, + __stageleft_ctx, + ), )); _out }; @@ -25,11 +32,11 @@ fn main() { *set_crate_name = option_env!("STAGELEFT_FINAL_CRATE_NAME") .unwrap_or(env!("CARGO_PKG_NAME")); *set_tokens = stageleft::internal::quote! { - { let x = x + 2; let _ = x + 2; } + { let x = x__free + 2; let _ = x + 2; } }; if !run { #[allow(unused, non_upper_case_globals, non_snake_case)] - ::std::mem::forget(x); + ::std::mem::forget(x__free); unsafe { return ::std::mem::MaybeUninit::uninit().assume_init(); } @@ -37,7 +44,7 @@ fn main() { #[allow(unreachable_code, unused_qualifications)] { { - let x = x + 2; + let x = x__free + 2; let _ = x + 2; } } diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_in_macro@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_in_macro@macro_tokens.snap index 13d47903d640..bca5e0f6058b 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_in_macro@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_in_macro@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, @@ -12,12 +13,18 @@ fn main() { run: bool| { #[allow(unused, non_upper_case_globals, non_snake_case)] - let x = { - let _out = ::stageleft::runtime_support::FreeVariable::uninitialized(&x); + let x__free = { + let _out = ::stageleft::runtime_support::FreeVariableWithContext::uninitialized( + &x, + __stageleft_ctx, + ); _vec_to_set .push(( - "x".to_string(), - ::stageleft::runtime_support::FreeVariable::to_tokens(x), + "x__free".to_string(), + ::stageleft::runtime_support::FreeVariableWithContext::to_tokens( + x, + __stageleft_ctx, + ), )); _out }; @@ -25,16 +32,16 @@ fn main() { *set_crate_name = option_env!("STAGELEFT_FINAL_CRATE_NAME") .unwrap_or(env!("CARGO_PKG_NAME")); *set_tokens = stageleft::internal::quote! { - dbg!(x) + dbg!(x__free) }; if !run { #[allow(unused, non_upper_case_globals, non_snake_case)] - ::std::mem::forget(x); + ::std::mem::forget(x__free); unsafe { return ::std::mem::MaybeUninit::uninit().assume_init(); } } - #[allow(unreachable_code, unused_qualifications)] { dbg!(x) } + #[allow(unreachable_code, unused_qualifications)] { dbg!(x__free) } } } } diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_local@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_local@macro_tokens.snap index 1ba7491cf55f..9ac5fe90be4e 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_local@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_local@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, @@ -12,12 +13,18 @@ fn main() { run: bool| { #[allow(unused, non_upper_case_globals, non_snake_case)] - let x = { - let _out = ::stageleft::runtime_support::FreeVariable::uninitialized(&x); + let x__free = { + let _out = ::stageleft::runtime_support::FreeVariableWithContext::uninitialized( + &x, + __stageleft_ctx, + ); _vec_to_set .push(( - "x".to_string(), - ::stageleft::runtime_support::FreeVariable::to_tokens(x), + "x__free".to_string(), + ::stageleft::runtime_support::FreeVariableWithContext::to_tokens( + x, + __stageleft_ctx, + ), )); _out }; @@ -25,16 +32,16 @@ fn main() { *set_crate_name = option_env!("STAGELEFT_FINAL_CRATE_NAME") .unwrap_or(env!("CARGO_PKG_NAME")); *set_tokens = stageleft::internal::quote! { - x + 2 + x__free + 2 }; if !run { #[allow(unused, non_upper_case_globals, non_snake_case)] - ::std::mem::forget(x); + ::std::mem::forget(x__free); unsafe { return ::std::mem::MaybeUninit::uninit().assume_init(); } } - #[allow(unreachable_code, unused_qualifications)] { x + 2 } + #[allow(unreachable_code, unused_qualifications)] { x__free + 2 } } } } diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_local_mut@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_local_mut@macro_tokens.snap index 70bb2d59ba99..90141a59dd71 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_local_mut@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__capture_local_mut@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, @@ -12,12 +13,18 @@ fn main() { run: bool| { #[allow(unused, non_upper_case_globals, non_snake_case)] - let x = { - let _out = ::stageleft::runtime_support::FreeVariable::uninitialized(&x); + let x__free = { + let _out = ::stageleft::runtime_support::FreeVariableWithContext::uninitialized( + &x, + __stageleft_ctx, + ); _vec_to_set .push(( - "x".to_string(), - ::stageleft::runtime_support::FreeVariable::to_tokens(x), + "x__free".to_string(), + ::stageleft::runtime_support::FreeVariableWithContext::to_tokens( + x, + __stageleft_ctx, + ), )); _out }; @@ -25,16 +32,16 @@ fn main() { *set_crate_name = option_env!("STAGELEFT_FINAL_CRATE_NAME") .unwrap_or(env!("CARGO_PKG_NAME")); *set_tokens = stageleft::internal::quote! { - x += 2 + x__free += 2 }; if !run { #[allow(unused, non_upper_case_globals, non_snake_case)] - ::std::mem::forget(x); + ::std::mem::forget(x__free); unsafe { return ::std::mem::MaybeUninit::uninit().assume_init(); } } - #[allow(unreachable_code, unused_qualifications)] { x += 2 } + #[allow(unreachable_code, unused_qualifications)] { x__free += 2 } } } } diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_enum_creation@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_enum_creation@macro_tokens.snap index 819cbbd28c3c..1cae8b08b7d5 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_enum_creation@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_enum_creation@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_local@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_local@macro_tokens.snap index 7169ceb62557..3130580842c0 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_local@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_local@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_struct_creation@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_struct_creation@macro_tokens.snap index e72911de50bc..298d31b1ed83 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_struct_creation@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__non_capture_struct_creation@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-2.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-2.snap index 236653f1143e..a2cc7a1d4240 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-2.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-2.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-3.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-3.snap index a7c8bf5a87af..041b3a8a8ddd 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-3.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-3.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-4.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-4.snap index 3a7639ba687a..cca0039f9bfd 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-4.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens-4.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens.snap index 06b3866d73bd..5f02ee3b0f6d 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__prelude_enum_variants@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, diff --git a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__simple@macro_tokens.snap b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__simple@macro_tokens.snap index d9143ddb3d98..c80aa9720ce9 100644 --- a/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__simple@macro_tokens.snap +++ b/stageleft_macro/src/quote_impl/snapshots/stageleft_macro__quote_impl__tests__simple@macro_tokens.snap @@ -5,6 +5,7 @@ expression: "prettyplease :: unparse(& wrapped)" fn main() { { move | + __stageleft_ctx: &_, set_mod: &mut String, set_crate_name: &mut &'static str, set_tokens: &mut stageleft::internal::TokenStream, diff --git a/stageleft_tool/src/lib.rs b/stageleft_tool/src/lib.rs index ea239830bfb3..6cc232eeb6b5 100644 --- a/stageleft_tool/src/lib.rs +++ b/stageleft_tool/src/lib.rs @@ -244,6 +244,18 @@ impl VisitMut for GenFinalPubVistor { #[cfg(stageleft_macro)] #e ); + } else if let syn::Item::Static(e) = i { + if matches!(e.vis, syn::Visibility::Public(_)) { + let e_name = &e.ident; + *i = parse_quote!(pub use #cur_path::#e_name;); + return; + } + } else if let syn::Item::Const(e) = i { + if matches!(e.vis, syn::Visibility::Public(_)) { + let e_name = &e.ident; + *i = parse_quote!(pub use #cur_path::#e_name;); + return; + } } } From f96676d2d53d824c5c168e3db69722c2e9956fe2 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Thu, 21 Nov 2024 12:43:07 -0800 Subject: [PATCH 68/74] feat(hydroflow_plus)!: allow runtime context to be referenced as a global constant (#1575) --- hydroflow_plus/src/builder/mod.rs | 5 -- hydroflow_plus/src/lib.rs | 2 +- hydroflow_plus/src/rewrites/profiler.rs | 12 +--- hydroflow_plus/src/runtime_context.rs | 63 +++++++++++++------ .../examples/perf_compute_pi.rs | 3 +- 5 files changed, 48 insertions(+), 37 deletions(-) diff --git a/hydroflow_plus/src/builder/mod.rs b/hydroflow_plus/src/builder/mod.rs index a28357e1ac22..dbfe070ede72 100644 --- a/hydroflow_plus/src/builder/mod.rs +++ b/hydroflow_plus/src/builder/mod.rs @@ -11,7 +11,6 @@ use crate::deploy::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec, LocalDep use crate::ir::HfPlusLeaf; use crate::location::{Cluster, ExternalProcess, Process}; use crate::staging_util::Invariant; -use crate::RuntimeContext; pub mod built; pub mod compiled; @@ -159,10 +158,6 @@ impl<'a> FlowBuilder<'a> { } } - pub fn runtime_context(&self) -> RuntimeContext<'a> { - RuntimeContext::new() - } - pub fn with_process>( self, process: &Process

    , diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index e6e4af74fd81..758e71098a28 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -11,7 +11,7 @@ pub mod runtime_support { } pub mod runtime_context; -pub use runtime_context::RuntimeContext; +pub use runtime_context::RUNTIME_CONTEXT; pub mod boundedness; pub use boundedness::{Bounded, Unbounded}; diff --git a/hydroflow_plus/src/rewrites/profiler.rs b/hydroflow_plus/src/rewrites/profiler.rs index e7d344a8f459..379952a54dac 100644 --- a/hydroflow_plus/src/rewrites/profiler.rs +++ b/hydroflow_plus/src/rewrites/profiler.rs @@ -5,7 +5,6 @@ use stageleft::*; use super::profiler as myself; // TODO(shadaj): stageleft does not support `self::...` use crate::ir::*; -use crate::RuntimeContext; pub fn increment_counter(count: &mut u64) { *count += 1; @@ -18,7 +17,6 @@ fn quoted_any_fn<'a, F: Fn(&usize) + 'a, Q: IntoQuotedMut<'a, F, ()>>(q: Q) -> Q /// Add a profiling node before each node to count the cardinality of its input fn add_profiling_node<'a>( node: &mut HfPlusNode, - _context: RuntimeContext<'a>, counters: RuntimeData<&'a RefCell>>, counter_queue: RuntimeData<&'a RefCell>>, id: &mut u32, @@ -28,9 +26,7 @@ fn add_profiling_node<'a>( *id += 1; node.transform_children( - |node, seen_tees| { - add_profiling_node(node, _context, counters, counter_queue, id, seen_tees) - }, + |node, seen_tees| add_profiling_node(node, counters, counter_queue, id, seen_tees), seen_tees, ); let orig_node = std::mem::replace(node, HfPlusNode::Placeholder); @@ -55,7 +51,6 @@ fn add_profiling_node<'a>( /// Count the cardinality of each input and periodically output to a file pub fn profiling<'a>( ir: Vec, - context: RuntimeContext<'a>, counters: RuntimeData<&'a RefCell>>, counter_queue: RuntimeData<&'a RefCell>>, ) -> Vec { @@ -65,7 +60,7 @@ pub fn profiling<'a>( .map(|l| { l.transform_children( |node, seen_tees| { - add_profiling_node(node, context, counters, counter_queue, &mut id, seen_tees) + add_profiling_node(node, counters, counter_queue, &mut id, seen_tees) }, &mut seen_tees, ) @@ -90,7 +85,6 @@ mod tests { .map(q!(|v| v + 1)) .for_each(q!(|n| println!("{}", n))); - let runtime_context = flow.runtime_context(); let built = flow.finalize(); insta::assert_debug_snapshot!(&built.ir()); @@ -106,7 +100,7 @@ mod tests { let pushed_down = built .optimize_with(crate::rewrites::persist_pullup::persist_pullup) - .optimize_with(|ir| super::profiling(ir, runtime_context, counters, counter_queue)); + .optimize_with(|ir| super::profiling(ir, counters, counter_queue)); insta::assert_debug_snapshot!(&pushed_down.ir()); diff --git a/hydroflow_plus/src/runtime_context.rs b/hydroflow_plus/src/runtime_context.rs index 6f93a186cbb3..8fe289c252ea 100644 --- a/hydroflow_plus/src/runtime_context.rs +++ b/hydroflow_plus/src/runtime_context.rs @@ -1,37 +1,60 @@ -use std::marker::PhantomData; - use hydroflow::scheduled::context::Context; use proc_macro2::TokenStream; use quote::quote; use stageleft::runtime_support::FreeVariableWithContext; -use crate::staging_util::Invariant; +use crate::Location; + +pub static RUNTIME_CONTEXT: RuntimeContext = RuntimeContext { _private: &() }; -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct RuntimeContext<'a> { - _phantom: Invariant<'a>, + _private: &'a (), } -impl RuntimeContext<'_> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } +impl<'a, L: Location<'a>> FreeVariableWithContext for RuntimeContext<'a> { + type O = &'a Context; + + fn to_tokens(self, _ctx: &L) -> (Option, Option) { + (None, Some(quote!(&context))) } } -impl Copy for RuntimeContext<'_> {} +#[cfg(test)] +mod tests { + use hydro_deploy::Deployment; + use hydroflow::futures::StreamExt; -impl Default for RuntimeContext<'_> { - fn default() -> Self { - Self::new() - } -} + use crate::*; -impl<'a, Ctx> FreeVariableWithContext for RuntimeContext<'a> { - type O = &'a Context; + struct P1 {} - fn to_tokens(self, _ctx: &Ctx) -> (Option, Option) { - (None, Some(quote!(&context))) + #[tokio::test] + async fn runtime_context() { + let mut deployment = Deployment::new(); + + let flow = FlowBuilder::new(); + let node = flow.process::(); + let external = flow.external_process::<()>(); + + let out_port = node + .source_iter(q!(0..5)) + .map(q!(|v| (v, RUNTIME_CONTEXT.current_tick().0))) + .send_bincode_external(&external); + + let nodes = flow + .with_process(&node, deployment.Localhost()) + .with_external(&external, deployment.Localhost()) + .deploy(&mut deployment); + + deployment.deploy().await.unwrap(); + + let mut external_out = nodes.connect_source_bincode(out_port).await; + + deployment.start().await.unwrap(); + + for i in 0..5 { + assert_eq!(external_out.next().await.unwrap(), (i, 0)); + } } } diff --git a/hydroflow_plus_test/examples/perf_compute_pi.rs b/hydroflow_plus_test/examples/perf_compute_pi.rs index 8382e0b18fc2..da9e491ec632 100644 --- a/hydroflow_plus_test/examples/perf_compute_pi.rs +++ b/hydroflow_plus_test/examples/perf_compute_pi.rs @@ -45,9 +45,8 @@ async fn main() { let (cluster, leader) = hydroflow_plus_test::cluster::compute_pi::compute_pi(&builder, 8192); // Uncomment below, change .bin("counter_compute_pi") in order to track cardinality per operation - // let runtime_context = builder.runtime_context(); // dbg!(builder.with_default_optimize() - // .optimize_with(|ir| profiling(ir, runtime_context, RuntimeData::new("FAKE"), RuntimeData::new("FAKE"))) + // .optimize_with(|ir| profiling(ir, RuntimeData::new("FAKE"), RuntimeData::new("FAKE"))) // .ir()); let _nodes = builder From ec55910f5a41d4f08059b5feda4b96fbd058c959 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 22 Nov 2024 14:25:01 -0800 Subject: [PATCH 69/74] refactor(paxos): generalize quorum logic (#1583) --- hydroflow_plus/src/optional.rs | 7 + hydroflow_plus/src/stream.rs | 78 +- hydroflow_plus_test/src/cluster/mod.rs | 2 + hydroflow_plus_test/src/cluster/paxos.rs | 474 ++--- .../src/cluster/paxos_bench.rs | 94 +- hydroflow_plus_test/src/cluster/paxos_kv.rs | 2 +- hydroflow_plus_test/src/cluster/quorum.rs | 97 + .../src/cluster/request_response.rs | 35 + ...cluster__paxos_bench__tests__paxos_ir.snap | 1630 +++++++++-------- .../src/local/graph_reachability.rs | 4 +- stageleft/src/type_name.rs | 26 +- 11 files changed, 1379 insertions(+), 1070 deletions(-) create mode 100644 hydroflow_plus_test/src/cluster/quorum.rs create mode 100644 hydroflow_plus_test/src/cluster/request_response.rs diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index 9fa8609d5297..bfde8c2ef5e1 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -213,6 +213,13 @@ impl<'a, T, L: Location<'a>, B> Optional { ) } + pub fn flatten(self) -> Stream + where + T: IntoIterator, + { + self.flat_map(q!(|v| v)) + } + pub fn filter bool + 'a>( self, f: impl IntoQuotedMut<'a, F, L>, diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 266c64500e16..2da21bf8e47f 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -73,6 +73,16 @@ pub struct Stream { _phantom: PhantomData<(T, L, B, Order)>, } +impl<'a, T, L: Location<'a>, B> From> for Stream { + fn from(stream: Stream) -> Stream { + Stream { + location: stream.location, + ir_node: stream.ir_node, + _phantom: PhantomData, + } + } +} + impl<'a, T, L: Location<'a>, B, Order> Stream { fn location_kind(&self) -> LocationId { self.location.id() @@ -417,6 +427,33 @@ where })) } + pub fn max_by_key K + 'a>( + self, + key: impl IntoQuotedMut<'a, F, L> + Copy, + ) -> Optional { + let f = key.splice_fn1_borrow_ctx(&self.location); + + let wrapped: syn::Expr = parse_quote!({ + let key_fn = #f; + move |curr, new| { + if key_fn(&new) > key_fn(&*curr) { + *curr = new; + } + } + }); + + let mut core = HfPlusNode::Reduce { + f: wrapped.into(), + input: Box::new(self.ir_node.into_inner()), + }; + + if L::is_top_level() { + core = HfPlusNode::Persist(Box::new(core)); + } + + Optional::new(self.location, core) + } + pub fn min(self) -> Optional where T: Ord, @@ -504,6 +541,35 @@ impl<'a, T, L: Location<'a>, B> Stream { } } +impl<'a, T, L: Location<'a>> Stream { + pub fn chain( + self, + other: Stream, + ) -> Stream { + check_matching_location(&self.location, &other.location); + + Stream::new( + self.location, + HfPlusNode::Chain( + Box::new(self.ir_node.into_inner()), + Box::new(other.ir_node.into_inner()), + ), + ) + } +} + +impl<'a, T, L: Location<'a> + NoTick> Stream { + pub fn union( + self, + other: Stream, + ) -> Stream { + let tick = self.location.tick(); + self.tick_batch(&tick) + .union(other.tick_batch(&tick)) + .all_ticks() + } +} + impl<'a, T, L: Location<'a>, Order> Stream { pub fn sort(self) -> Stream where @@ -515,7 +581,7 @@ impl<'a, T, L: Location<'a>, Order> Stream { ) } - pub fn chain(self, other: Stream) -> Stream + pub fn union(self, other: Stream) -> Stream where Order: MinOrder, { @@ -598,10 +664,7 @@ impl<'a, K: Eq + Hash, V, L: Location<'a>> Stream<(K, V), Tick, Bounded> { } } -impl<'a, K: Eq + Hash, V, L: Location<'a>, Order> Stream<(K, V), Tick, Bounded, Order> -where - Order: MinOrder, -{ +impl<'a, K: Eq + Hash, V, L: Location<'a>, Order> Stream<(K, V), Tick, Bounded, Order> { pub fn fold_keyed_commutative A + 'a, F: Fn(&mut A, V) + 'a>( self, init: impl IntoQuotedMut<'a, I, Tick>, @@ -620,6 +683,11 @@ where ) } + pub fn keys(self) -> Stream, Bounded, Order> { + self.fold_keyed_commutative(q!(|| ()), q!(|_, _| {})) + .map(q!(|(k, _)| k)) + } + pub fn reduce_keyed_commutative( self, comb: impl IntoQuotedMut<'a, F, Tick>, diff --git a/hydroflow_plus_test/src/cluster/mod.rs b/hydroflow_plus_test/src/cluster/mod.rs index b4e5be9bb006..88bcbe2d9497 100644 --- a/hydroflow_plus_test/src/cluster/mod.rs +++ b/hydroflow_plus_test/src/cluster/mod.rs @@ -4,5 +4,7 @@ pub mod map_reduce; pub mod paxos; pub mod paxos_bench; pub mod paxos_kv; +pub mod quorum; +pub mod request_response; pub mod simple_cluster; pub mod two_pc; diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 060775f8af83..875d3626dd7f 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -1,20 +1,24 @@ use std::collections::HashMap; use std::fmt::Debug; +use std::hash::Hash; use std::time::Duration; use hydroflow_plus::*; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use stream::{NoOrder, TotalOrder}; +use stream::NoOrder; use tokio::time::Instant; +use super::quorum::collect_quorum; +use super::request_response::join_responses; + pub struct Proposer {} pub struct Acceptor {} pub trait PaxosPayload: Serialize + DeserializeOwned + PartialEq + Eq + Clone + Debug {} impl PaxosPayload for T {} -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Hash)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Copy, Clone, Debug, Hash)] pub struct Ballot { pub num: u32, pub proposer_id: ClusterId, @@ -34,24 +38,12 @@ impl PartialOrd for Ballot { } } -#[derive(Serialize, Deserialize, Clone, Debug)] -struct P1a { - ballot: Ballot, -} - #[derive(Serialize, Deserialize, Clone, Debug)] struct LogValue

    { ballot: Ballot, value: Option

    , // might be a hole } -#[derive(Serialize, Deserialize, Clone, Debug)] -struct P1b { - ballot: Ballot, - max_ballot: Ballot, - accepted: L, -} - #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] struct P2a

    { ballot: Ballot, @@ -59,14 +51,6 @@ struct P2a

    { value: Option

    , // might be a re-committed hole } -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] -struct P2b

    { - ballot: Ballot, - max_ballot: Ballot, - slot: usize, - value: Option

    , // might be a hole -} - #[expect( clippy::too_many_arguments, clippy::type_complexity, @@ -101,11 +85,10 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( let proposer_tick = proposers.tick(); let acceptor_tick = acceptors.tick(); - let (a_to_proposers_p2b_complete_cycle, a_to_proposers_p2b_forward_reference) = - proposers.forward_ref::, _, _, NoOrder>>(); + let (sequencing_max_ballot_complete_cycle, sequencing_max_ballot_forward_reference) = + proposers.forward_ref::>(); let (a_log_complete_cycle, a_log_forward_reference) = - acceptor_tick - .forward_ref::, HashMap>), _, _>>(); + acceptor_tick.forward_ref::>(); let (p_ballot, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( proposers, @@ -116,46 +99,36 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( i_am_leader_send_timeout, i_am_leader_check_timeout, i_am_leader_check_timeout_delay_multiplier, - a_to_proposers_p2b_forward_reference.map(q!(|p2b| p2b.max_ballot)), - a_log_forward_reference.map(q!(|(_ckpnt, log)| log.clone())), + sequencing_max_ballot_forward_reference, + a_log_forward_reference, ); let just_became_leader = p_is_leader .clone() .continue_unless(p_is_leader.clone().defer_tick()); - // Tell clients that leader election has completed and they can begin sending messages - let p_to_clients_new_leader_elected = just_became_leader - .clone() - .then(p_ballot.clone()) // Only tell the clients once when leader election concludes - .all_ticks(); - - let (p_log_to_try_commit, p_max_slot, p_log_holes) = - recommit_after_leader_election(p_relevant_p1bs, p_ballot.clone(), f); - - let p_log_to_recommit = p_log_to_try_commit - .chain(p_log_holes) - .continue_if(just_became_leader); // Only resend p1b stuff once the moment we become leader. - - let (p_to_replicas, a_log, a_to_proposers_p2b) = sequence_payload( + let (p_to_replicas, a_log, sequencing_max_ballots) = sequence_payload( proposers, acceptors, &proposer_tick, &acceptor_tick, c_to_proposers, r_to_acceptors_checkpoint, - p_ballot, + p_ballot.clone(), p_is_leader, - p_max_slot, - p_log_to_recommit, + p_relevant_p1bs, f, a_max_ballot, ); a_log_complete_cycle.complete(a_log); - a_to_proposers_p2b_complete_cycle.complete(a_to_proposers_p2b); + sequencing_max_ballot_complete_cycle.complete(sequencing_max_ballots); - (p_to_clients_new_leader_elected, p_to_replicas) + ( + // Only tell the clients once when leader election concludes + just_became_leader.then(p_ballot).all_ticks(), + p_to_replicas, + ) } #[expect( @@ -176,26 +149,29 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( a_log: Singleton>, Bounded>, ) -> ( Singleton>, Bounded>, - Optional>, Bounded>, - Stream, Tick>, Bounded, NoOrder>, + Optional<(), Tick>, Bounded>, + Stream>, Bounded, NoOrder>, Singleton>, Bounded>, ) { - let (a_to_proposers_p1b_complete_cycle, a_to_proposers_p1b_forward_ref) = - proposers.forward_ref::, _, _, NoOrder>>(); + let (p1b_fail_complete, p1b_fail) = + proposers.forward_ref::>(); let (p_to_proposers_i_am_leader_complete_cycle, p_to_proposers_i_am_leader_forward_ref) = proposers.forward_ref::>(); let (p_is_leader_complete_cycle, p_is_leader_forward_ref) = - proposer_tick.forward_ref::>(); + proposer_tick.forward_ref::>(); // a_to_proposers_p2b.clone().for_each(q!(|(_, p2b): (u32, P2b)| println!("Proposer received P2b: {:?}", p2b))); // p_to_proposers_i_am_leader.clone().for_each(q!(|ballot: Ballot| println!("Proposer received I am leader: {:?}", ballot))); // c_to_proposers.clone().for_each(q!(|payload: ClientPayload| println!("Client sent proposer payload: {:?}", payload))); - let p_received_max_ballot = p_max_ballot( - proposers, - a_to_proposers_p1b_forward_ref.map(q!(|p1a| p1a.max_ballot)), - p_received_p2b_ballots, - p_to_proposers_i_am_leader_forward_ref, - ); + let p_received_max_ballot = p1b_fail + .union(p_received_p2b_ballots) + .union(p_to_proposers_i_am_leader_forward_ref) + .max() + .unwrap_or(proposers.singleton(q!(Ballot { + num: 0, + proposer_id: ClusterId::from_raw(0) + }))); + let (p_ballot, p_has_largest_ballot) = p_ballot_calc( proposer_tick, p_received_max_ballot.latest_tick(proposer_tick), @@ -213,13 +189,16 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( p_to_proposers_i_am_leader_complete_cycle.complete(p_to_proposers_i_am_leader); - let p_to_acceptors_p1a = p_p1a(p_ballot.clone(), p_trigger_election, acceptors); + let p_to_acceptors_p1a = p_trigger_election + .then(p_ballot.clone()) + .all_ticks() + .inspect(q!(|_| println!("Proposer leader expired, sending P1a"))) + .broadcast_bincode_interleaved(acceptors); let (a_max_ballot, a_to_proposers_p1b) = acceptor_p1(acceptor_tick, p_to_acceptors_p1a, a_log, proposers); - a_to_proposers_p1b_complete_cycle.complete(a_to_proposers_p1b.clone()); - let (p_is_leader, p_relevant_p1bs) = p_p1b( + let (p_is_leader, p_accepted_values, fail_ballots) = p_p1b( proposer_tick, a_to_proposers_p1b.inspect(q!(|p1b| println!("Proposer received P1b: {:?}", p1b))), p_ballot.clone(), @@ -227,28 +206,9 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( f, ); p_is_leader_complete_cycle.complete(p_is_leader.clone()); + p1b_fail_complete.complete(fail_ballots.all_ticks()); - (p_ballot, p_is_leader, p_relevant_p1bs, a_max_ballot) -} - -// Proposer logic to calculate the largest ballot received so far. -fn p_max_ballot<'a>( - proposers: &Cluster<'a, Proposer>, - p_received_p1b_ballots: Stream, Unbounded, NoOrder>, - p_received_p2b_ballots: Stream, Unbounded, NoOrder>, - p_to_proposers_i_am_leader: Stream, Unbounded, NoOrder>, -) -> Singleton, Unbounded> { - let ballot_batcher = proposers.tick(); - p_received_p1b_ballots - .tick_batch(&ballot_batcher) - .chain(p_received_p2b_ballots.tick_batch(&ballot_batcher)) - .chain(p_to_proposers_i_am_leader.tick_batch(&ballot_batcher)) - .all_ticks() - .max() - .unwrap_or(proposers.singleton(q!(Ballot { - num: 0, - proposer_id: ClusterId::from_raw(0) - }))) + (p_ballot, p_is_leader, p_accepted_values, a_max_ballot) } // Proposer logic to calculate the next ballot number. Expects p_received_max_ballot, the largest ballot received so far. Outputs streams: ballot_num, and has_largest_ballot, which only contains a value if we have the largest ballot. @@ -280,7 +240,7 @@ fn p_ballot_calc<'a>( })); p_ballot_num_complete_cycle.complete_next_tick(p_new_ballot_num); - let p_ballot = p_ballot_num.clone().map(q!(move |num| Ballot { + let p_ballot = p_ballot_num.map(q!(move |num| Ballot { num, proposer_id: CLUSTER_SELF_ID })); @@ -300,7 +260,7 @@ fn p_ballot_calc<'a>( fn p_leader_expired<'a>( proposer_tick: &Tick>, p_to_proposers_i_am_leader: Stream, Unbounded, NoOrder>, - p_is_leader: Optional>, Bounded>, + p_is_leader: Optional<(), Tick>, Bounded>, i_am_leader_check_timeout: u64, // How often to check if heartbeat expired ) -> Optional, Tick>, Bounded> { let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold_commutative( @@ -328,7 +288,7 @@ fn p_leader_expired<'a>( fn p_leader_heartbeat<'a>( proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, - p_is_leader: Optional>, Bounded>, + p_is_leader: Optional<(), Tick>, Bounded>, p_ballot: Singleton>, Bounded>, i_am_leader_send_timeout: u64, // How often to heartbeat i_am_leader_check_timeout: u64, // How often to check if heartbeat expired @@ -367,36 +327,21 @@ fn p_leader_heartbeat<'a>( (p_to_proposers_i_am_leader, p_trigger_election) } -// Proposer logic to send "I am leader" messages periodically to other proposers, or send p1a to acceptors if other leaders expired. -fn p_p1a<'a>( - p_ballot: Singleton>, Bounded>, - p_trigger_election: Optional, Tick>, Bounded>, - acceptors: &Cluster<'a, Acceptor>, -) -> Stream, Unbounded, NoOrder> { - p_trigger_election - .then(p_ballot) - .map(q!(|ballot| P1a { ballot })) - .all_ticks() - .inspect(q!(|_| println!("Proposer leader expired, sending P1a"))) - .broadcast_bincode_interleaved(acceptors) -} - #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( acceptor_tick: &Tick>, - p_to_acceptors_p1a: Stream, Unbounded, NoOrder>, + p_to_acceptors_p1a: Stream, Unbounded, NoOrder>, a_log: Singleton>, Bounded>, proposers: &Cluster<'a, Proposer>, ) -> ( Singleton>, Bounded>, - Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, + Stream<(Ballot, Result), Cluster<'a, Proposer>, Unbounded, NoOrder>, ) { let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(acceptor_tick); let a_max_ballot = p_to_acceptors_p1a .clone() .inspect(q!(|p1a| println!("Acceptor received P1a: {:?}", p1a))) .persist() - .map(q!(|p1a| p1a.ballot)) .max() .unwrap_or(acceptor_tick.singleton(q!(Ballot { num: 0, @@ -408,13 +353,16 @@ fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( p_to_acceptors_p1a .cross_singleton(a_max_ballot) .cross_singleton(a_log) - .map(q!(|((p1a, max_ballot), log)| ( - p1a.ballot.proposer_id, - P1b { - ballot: p1a.ballot, - max_ballot, - accepted: log - } + .map(q!(|((ballot, max_ballot), log)| ( + ballot.proposer_id, + ( + ballot, + if ballot == max_ballot { + Ok(log) + } else { + Err(max_ballot) + } + ) ))) .all_ticks() .send_bincode_interleaved(proposers), @@ -425,39 +373,62 @@ fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( proposer_tick: &Tick>, - a_to_proposers_p1b: Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, + a_to_proposers_p1b: Stream< + (Ballot, Result), + Cluster<'a, Proposer>, + Unbounded, + NoOrder, + >, p_ballot: Singleton>, Bounded>, p_has_largest_ballot: Optional<(), Tick>, Bounded>, f: usize, ) -> ( - Optional>, Bounded>, - Stream, Tick>, Bounded, NoOrder>, + Optional<(), Tick>, Bounded>, + Stream>, Bounded, NoOrder>, + Stream>, Bounded, NoOrder>, ) { - let p_relevant_p1bs = a_to_proposers_p1b - .tick_prefix(proposer_tick) - // NOTE: because `p_ballot` grows monotonically across ticks, we could garbage gollect - // but we don't do that here since leader election is a rare event - .cross_singleton(p_ballot.clone()) - .filter(q!(|(p1b, ballot)| p1b.ballot == *ballot)) - .map(q!(|t| t.0)); - let p_received_quorum_of_p1bs = - p_relevant_p1bs - .clone() - .count() - .filter_map(q!(move |num_received| if num_received > f { - Some(true) + let (quorums, fails) = collect_quorum( + proposer_tick, + a_to_proposers_p1b.tick_batch(proposer_tick), + f + 1, + 2 * f + 1, + ); + + let p_received_quorum_of_p1bs = quorums + .persist() + .fold_keyed_commutative( + q!(|| vec![]), + q!(|logs, log| { + logs.push(log); + }), + ) + .max_by_key(q!(|t| t.0)) + .zip(p_ballot.clone()) + .filter_map(q!( + move |((quorum_ballot, quorum_accepted), my_ballot)| if quorum_ballot == my_ballot { + Some(quorum_accepted) } else { None - })); - let p_is_leader = p_received_quorum_of_p1bs.continue_if(p_has_largest_ballot.clone()); + } + )); + + let p_is_leader = p_received_quorum_of_p1bs + .clone() + .map(q!(|_| ())) + .continue_if(p_has_largest_ballot.clone()); - (p_is_leader, p_relevant_p1bs) + ( + p_is_leader, + // The fold was not commutative, so this is unordered. + p_received_quorum_of_p1bs.flatten().assume_ordering(), + fails.map(q!(|(_, ballot)| ballot)), + ) } #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn recommit_after_leader_election<'a, P: PaxosPayload>( - p_relevant_p1bs: Stream< - P1b>>, + accepted_logs: Stream< + HashMap>, Tick>, Bounded, NoOrder, @@ -467,10 +438,9 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( ) -> ( Stream, Tick>, Bounded, NoOrder>, Optional>, Bounded>, - Stream, Tick>, Bounded>, ) { - let p_p1b_highest_entries_and_count = p_relevant_p1bs - .flat_map(q!(|p1b| p1b.accepted.into_iter())) // Convert HashMap log back to stream + let p_p1b_highest_entries_and_count = accepted_logs + .flatten() // Convert HashMap log back to stream .fold_keyed_commutative::<(usize, Option>), _, _>(q!(|| (0, None)), q!(|curr_entry, new_entry| { if let Some(curr_entry_payload) = &mut curr_entry.1 { let same_values = new_entry.value == curr_entry_payload.value; @@ -524,7 +494,8 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( slot, value: None })); - (p_log_to_try_commit, p_max_slot, p_log_holes) + + (p_log_to_try_commit.union(p_log_holes), p_max_slot) } #[expect( @@ -546,42 +517,78 @@ fn sequence_payload<'a, P: PaxosPayload, R>( >, p_ballot: Singleton>, Bounded>, - p_is_leader: Optional>, Bounded>, - p_max_slot: Optional>, Bounded>, + p_is_leader: Optional<(), Tick>, Bounded>, - p_log_to_recommit: Stream, Tick>, Bounded, NoOrder>, + p_relevant_p1bs: Stream< + HashMap>, + Tick>, + Bounded, + NoOrder, + >, f: usize, a_max_ballot: Singleton>, Bounded>, ) -> ( Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded, NoOrder>, - Singleton<(Option, HashMap>), Tick>, Bounded>, - Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, + Singleton>, Tick>, Bounded>, + Stream, Unbounded, NoOrder>, ) { - let p_to_acceptors_p2a = p_p2a( + let (p_log_to_recommit, p_max_slot) = + recommit_after_leader_election(p_relevant_p1bs, p_ballot.clone(), f); + + let indexed_payloads = index_payloads( proposer_tick, p_max_slot, c_to_proposers, - p_ballot.clone(), - p_log_to_recommit, p_is_leader.clone(), - acceptors, ); - // Acceptors. - // p_to_acceptors_p2a.clone().for_each(q!(|p2a: P2a| println!("Acceptor received P2a: {:?}", p2a))); + let payloads_to_send = indexed_payloads + .cross_singleton(p_ballot.clone()) + .map(q!(|((slot, payload), ballot)| ( + (slot, ballot), + Some(payload) + ))) + .union(p_log_to_recommit.map(q!(|p2a| ((p2a.slot, p2a.ballot), p2a.value)))) + .continue_if(p_is_leader); + let (a_log, a_to_proposers_p2b) = acceptor_p2( acceptor_tick, a_max_ballot.clone(), - p_to_acceptors_p2a, + payloads_to_send + .clone() + .all_ticks() + .map(q!(|((slot, ballot), value)| P2a { + ballot, + slot, + value + })) + .broadcast_bincode_interleaved(acceptors), r_to_acceptors_checkpoint, proposers, f, ); - let p_to_replicas = p_p2b(proposer_tick, a_to_proposers_p2b.clone(), f); + // TOOD: only persist if we are the leader + let (quorums, fails) = collect_quorum( + proposer_tick, + a_to_proposers_p2b.clone().tick_batch(proposer_tick), + f + 1, + 2 * f + 1, + ); + + let p_to_replicas = join_responses( + proposer_tick, + quorums.keys().map(q!(|k| (k, ()))), + payloads_to_send, + ) + .all_ticks(); - (p_to_replicas, a_log, a_to_proposers_p2b) + ( + p_to_replicas.map(q!(|((slot, _ballot), (value, _))| (slot, value))), + a_log.map(q!(|(_ckpnt, log)| log)), + fails.map(q!(|(_, ballot)| ballot)).all_ticks(), + ) } #[derive(Clone)] @@ -591,56 +598,37 @@ enum CheckpointOrP2a

    { } // Proposer logic to send p2as, outputting the next slot and the p2as to send to acceptors. -fn p_p2a<'a, P: PaxosPayload>( +fn index_payloads<'a, P: PaxosPayload>( proposer_tick: &Tick>, p_max_slot: Optional>, Bounded>, c_to_proposers: Stream, Unbounded>, - p_ballot: Singleton>, Bounded>, - p_log_to_recommit: Stream, Tick>, Bounded, NoOrder>, - p_is_leader: Optional>, Bounded>, - acceptors: &Cluster<'a, Acceptor>, -) -> Stream, Cluster<'a, Acceptor>, Unbounded, NoOrder> { - let (p_next_slot_complete_cycle, p_next_slot) = proposer_tick.cycle::>(); - let p_next_slot_after_reconciling_p1bs = p_max_slot - .map(q!(|max_slot| max_slot + 1)) - .unwrap_or(proposer_tick.singleton(q!(0))) - // .inspect(q!(|max_slot| println!("{} p_max_slot: {:?}", context.current_tick(), max_slot))) - .continue_unless(p_next_slot.clone()); - - // Send p2as + p_is_leader: Optional<(), Tick>, Bounded>, +) -> Stream<(usize, P), Tick>, Bounded> { + let (p_next_slot_complete_cycle, p_next_slot) = + proposer_tick.cycle_with_initial::>(proposer_tick.singleton(q!(0))); + let p_next_slot_after_reconciling_p1bs = p_max_slot.map(q!(|max_slot| max_slot + 1)); + + let base_slot = p_next_slot_after_reconciling_p1bs.unwrap_or(p_next_slot); + let p_indexed_payloads = c_to_proposers .tick_batch(proposer_tick) + .continue_if(p_is_leader) .enumerate() - .cross_singleton(p_next_slot.clone()) - // .inspect(q!(|next| println!("{} p_indexed_payloads next slot: {}", context.current_tick(), next)))) - .cross_singleton(p_ballot.clone()) - // .inspect(q!(|ballot_num| println!("{} p_indexed_payloads ballot_num: {}", context.current_tick(), ballot_num)))) - .map(q!(|(((index, payload), next_slot), ballot)| P2a { - ballot, - slot: next_slot + index, - value: Some(payload) - })); - // .inspect(q!(|p2a: &P2a| println!("{} p_indexed_payloads P2a: {:?}", context.current_tick(), p2a))); - let p_to_acceptors_p2a = p_log_to_recommit - .chain(p_indexed_payloads.clone()) - .continue_if(p_is_leader.clone()) - .all_ticks() - .broadcast_bincode_interleaved(acceptors); - - let p_num_payloads = p_indexed_payloads.count(); - let p_next_slot_after_sending_payloads = p_num_payloads - .clone() - .zip(p_next_slot.clone()) - .map(q!(|(num_payloads, next_slot)| next_slot + num_payloads)); - - let p_new_next_slot = p_next_slot_after_reconciling_p1bs - // .inspect(q!(|slot| println!("{} p_new_next_slot_after_reconciling_p1bs: {:?}", context.current_tick(), slot))) - .union(p_next_slot_after_sending_payloads) - // .inspect(q!(|slot| println!("{} p_next_slot_after_sending_payloads: {:?}", context.current_tick(), slot)))) - .continue_if(p_is_leader.clone()); + .cross_singleton(base_slot.clone()) + .map(q!(|((index, payload), base_slot)| ( + base_slot + index, + payload + ))); + + let p_num_payloads = p_indexed_payloads.clone().count(); + let p_next_slot_after_sending_payloads = + p_num_payloads + .clone() + .zip(base_slot) + .map(q!(|(num_payloads, base_slot)| base_slot + num_payloads)); - p_next_slot_complete_cycle.complete_next_tick(p_new_next_slot); - p_to_acceptors_p2a + p_next_slot_complete_cycle.complete_next_tick(p_next_slot_after_sending_payloads); + p_indexed_payloads } #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] @@ -658,7 +646,7 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( f: usize, ) -> ( Singleton<(Option, HashMap>), Tick>, Bounded>, - Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, + Stream<((usize, Ballot), Result<(), Ballot>), Cluster<'a, Proposer>, Unbounded, NoOrder>, ) { let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(acceptor_tick); @@ -695,21 +683,25 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( } else { None } - )) - .assume_ordering::(); + )); let a_log = a_p2as_to_place_in_log - .chain(a_new_checkpoint.into_stream()) + .union(a_new_checkpoint.into_stream()) .persist() - .fold( + .fold_commutative( q!(|| (None, HashMap::new())), q!(|(prev_checkpoint, log), checkpoint_or_p2a| { match checkpoint_or_p2a { CheckpointOrP2a::Checkpoint(new_checkpoint) => { - // This is a checkpoint message. Delete all entries up to the checkpoint - for slot in (prev_checkpoint.unwrap_or(0))..new_checkpoint { - log.remove(&slot); + if prev_checkpoint + .map(|prev| new_checkpoint > prev) + .unwrap_or(true) + { + for slot in (prev_checkpoint.unwrap_or(0))..new_checkpoint { + log.remove(&slot); + } + + *prev_checkpoint = Some(new_checkpoint); } - *prev_checkpoint = Some(new_checkpoint); } CheckpointOrP2a::P2a(p2a) => { // This is a regular p2a message. Insert it into the log if it is not checkpointed and has a higher ballot than what was there before @@ -736,82 +728,16 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( .cross_singleton(a_max_ballot) .map(q!(|(p2a, max_ballot)| ( p2a.ballot.proposer_id, - P2b { - ballot: p2a.ballot, - max_ballot, - slot: p2a.slot, - value: p2a.value - } + ( + (p2a.slot, p2a.ballot), + if p2a.ballot == max_ballot { + Ok(()) + } else { + Err(max_ballot) + } + ) ))) .all_ticks() .send_bincode_interleaved(proposers); (a_log, a_to_proposers_p2b) } - -fn p_p2b<'a, P: PaxosPayload>( - proposer_tick: &Tick>, - a_to_proposers_p2b: Stream, Cluster<'a, Proposer>, Unbounded, NoOrder>, - f: usize, -) -> Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded, NoOrder> { - let (p_broadcasted_p2b_slots_complete_cycle, p_broadcasted_p2b_slots) = proposer_tick.cycle(); - let (p_persisted_p2bs_complete_cycle, p_persisted_p2bs) = - proposer_tick.cycle::>(); - let p_p2b = a_to_proposers_p2b - .tick_batch(proposer_tick) - .chain(p_persisted_p2bs); - let p_count_matching_p2bs = p_p2b - .clone() - .filter_map(q!(|p2b| if p2b.ballot == p2b.max_ballot { - // Only consider p2bs where max ballot = ballot, which means that no one preempted us - Some(((p2b.slot, p2b.ballot), p2b.value)) - } else { - None - })) - .fold_keyed_commutative( - q!(|| (0, None)), - q!(|accum, value| { - accum.0 += 1; - accum.1 = Some(value); - // this is commutative because p2bs with the same slot and ballot - // will have identical values since they originated from the payload - }), - ) - .map(q!(|(k, (count, v))| (k, (count, v.unwrap())))); - let p_p2b_quorum_reached = - p_count_matching_p2bs - .clone() - .filter_map(q!(move |((slot, _ballot), (count, value))| if count > f { - Some((slot, value)) - } else { - None - })); - let p_to_replicas = p_p2b_quorum_reached - .clone() - .anti_join(p_broadcasted_p2b_slots) // Only tell the replicas about committed values once - .all_ticks(); - - let p_p2b_all_commit_slots = - p_count_matching_p2bs.clone().filter_map(q!( - move |((slot, _ballot), (count, _p2b))| if count == 2 * f + 1 { - Some(slot) - } else { - None - } - )); - // p_p2b_all_commit_slots.inspect(q!(|slot: i32| println!("Proposer slot all received: {:?}", slot))); - let p_broadcasted_p2b_slots_new = p_p2b_quorum_reached - .clone() - .map(q!(|(slot, _value)| slot)) - .filter_not_in(p_p2b_all_commit_slots.clone()); - // p_broadcasted_p2b_slots_new.inspect(q!(|slot: i32| println!("Proposer slot broadcasted: {:?}", slot))); - p_broadcasted_p2b_slots_complete_cycle.complete_next_tick(p_broadcasted_p2b_slots_new); - let p_persisted_p2bs_new = p_p2b - .clone() - .map(q!(|p2b| (p2b.slot, p2b))) - .anti_join(p_p2b_all_commit_slots.clone()) - .map(q!(|(_slot, p2b)| p2b)); - // TOOD: only persist if we are the leader - // p_persisted_p2bs_new.inspect(q!(|(sender, p2b): (u32, P2b)| println!("Proposer persisting p2b: {:?}", p2b))); - p_persisted_p2bs_complete_cycle.complete_next_tick(p_persisted_p2bs_new); - p_to_replicas -} diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index fb763ff60ce3..f8b8d9625d09 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -3,10 +3,11 @@ use std::rc::Rc; use std::time::{Duration, SystemTime}; use hydroflow_plus::*; -use stream::NoOrder; +use stream::{NoOrder, TotalOrder}; use super::paxos::{Acceptor, Ballot, Proposer}; use super::paxos_kv::{paxos_kv, KvPayload, Replica}; +use super::quorum::collect_quorum; pub struct Client {} @@ -60,10 +61,13 @@ pub fn paxos_bench<'a>( .tick_batch(&client_tick) .cross_singleton(cur_leader_id) .all_ticks() - .map(q!(move |(key, leader_id)| (leader_id, KvPayload { + .map(q!(move |((key, value), leader_id)| (leader_id, KvPayload { key, - // we use our ID as the value and use that so the replica only notifies us - value: CLUSTER_SELF_ID + // we use our ID as part of the value and use that so the replica only notifies us + value: ( + CLUSTER_SELF_ID, + value + ) }))) .send_bincode_interleaved(&proposers) // clients "own" certain keys, so interleaving elements from clients will not affect @@ -79,36 +83,22 @@ pub fn paxos_bench<'a>( new_leader_elected_complete .complete(new_leader_elected.broadcast_bincode_interleaved(&clients)); - // we only mark a transaction as committed when `f + 1` replicas have committed it - let (c_pending_quorum_payloads_complete_cycle, c_pending_quorum_payloads) = - client_tick.cycle::>(); let c_received_payloads = processed_payloads - .map(q!(|payload| (payload.value, payload))) - .send_bincode_interleaved(&clients) - .tick_batch(&client_tick) - .map(q!(|replica_payload| (replica_payload.key, ()))) - .chain(c_pending_quorum_payloads); - let c_received_quorum_payloads = c_received_payloads - .clone() - .fold_keyed_commutative( - q!(|| 0), - q!(|curr_count, _sender| { - *curr_count += 1; // Assumes the same replica will only send commit once - }), - ) - .filter_map(q!(move |(key, count)| { - if count == f + 1 { - Some(key) - } else { - None - } - })); - let c_new_pending_quorum_payloads = - c_received_payloads.anti_join(c_received_quorum_payloads.clone()); - c_pending_quorum_payloads_complete_cycle - .complete_next_tick(c_new_pending_quorum_payloads); + .map(q!(|payload| ( + payload.value.0, + ((payload.key, payload.value.1), Ok(())) + ))) + .send_bincode_interleaved(&clients); - c_received_quorum_payloads.all_ticks() + // we only mark a transaction as committed when all replicas have applied it + let (c_quorum_payloads, _) = collect_quorum::<_, _, _, _, ()>( + &client_tick, + c_received_payloads.tick_batch(&client_tick), + f + 1, + f + 1, + ); + + c_quorum_payloads.keys().all_ticks() }, num_clients_per_node, median_latency_window_size, @@ -121,8 +111,8 @@ fn bench_client<'a>( clients: &Cluster<'a, Client>, trigger_restart: Stream<(), Cluster<'a, Client>, Unbounded>, transaction_cycle: impl FnOnce( - Stream, Unbounded>, - ) -> Stream, Unbounded, NoOrder>, + Stream<(u32, u32), Cluster<'a, Client>, Unbounded>, + ) -> Stream<(u32, u32), Cluster<'a, Client>, Unbounded, NoOrder>, num_clients_per_node: usize, median_latency_window_size: usize, ) { @@ -132,24 +122,28 @@ fn bench_client<'a>( // Whenever the leader changes, make all clients send a message let restart_this_tick = trigger_restart.tick_batch(&client_tick).last(); - let c_new_payloads_when_restart = restart_this_tick - .clone() - .flat_map(q!(move |_| (0..num_clients_per_node).map(move |i| i as u32))); + let c_new_payloads_when_restart = restart_this_tick.clone().flat_map(q!(move |_| (0 + ..num_clients_per_node) + .map(move |i| ( + (CLUSTER_SELF_ID.raw_id * (num_clients_per_node as u32)) + i as u32, + 0 + )))); let (c_to_proposers_complete_cycle, c_to_proposers) = - clients.forward_ref::>(); - let c_received_quorum_payloads = transaction_cycle( - c_to_proposers.assume_ordering(), /* we don't send a new write for the same key until the previous one is committed, - * so writes to the same key are ordered */ - ) - .tick_batch(&client_tick); + clients.forward_ref::>(); + let c_received_quorum_payloads = transaction_cycle(c_to_proposers).tick_batch(&client_tick); // Whenever all replicas confirm that a payload was committed, send another payload - let c_new_payloads_when_committed = c_received_quorum_payloads.clone(); + let c_new_payloads_when_committed = c_received_quorum_payloads + .clone() + .map(q!(|payload| (payload.0, payload.1 + 1))); c_to_proposers_complete_cycle.complete( + // we don't send a new write for the same key until the previous one is committed, + // so writes to the same key are ordered c_new_payloads_when_restart - .chain(c_new_payloads_when_committed) - .all_ticks(), + .union(c_new_payloads_when_committed) + .all_ticks() + .assume_ordering(), ); // Track statistics @@ -162,11 +156,11 @@ fn bench_client<'a>( )); let c_updated_timers = c_received_quorum_payloads .clone() - .map(q!(|key| (key as usize, SystemTime::now()))); + .map(q!(|(key, _prev_count)| (key as usize, SystemTime::now()))); let c_new_timers = c_timers .clone() // Update c_timers in tick+1 so we can record differences during this tick (to track latency) - .chain(c_new_timers_when_leader_elected) - .chain(c_updated_timers.clone()) + .union(c_new_timers_when_leader_elected) + .union(c_updated_timers.clone()) .reduce_keyed_commutative(q!(|curr_time, new_time| { if new_time > *curr_time { *curr_time = new_time; @@ -186,7 +180,7 @@ fn bench_client<'a>( .map(q!(|(_virtual_id, (prev_time, curr_time))| Some( curr_time.duration_since(prev_time).unwrap().as_micros() ))) - .chain(c_latency_reset.into_stream()) + .union(c_latency_reset.into_stream()) .all_ticks() .flatten() .fold_commutative( diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index f2f02b7b4224..83d1ff0bfd88 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -104,7 +104,7 @@ pub fn replica<'a, K: KvKey, V: KvValue>( // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); let r_sorted_payloads = p_to_replicas .tick_batch(&replica_tick) - .chain(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet + .union(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet .sort(); // Create a cycle since we'll use this seq before we define it let (r_highest_seq_complete_cycle, r_highest_seq) = diff --git a/hydroflow_plus_test/src/cluster/quorum.rs b/hydroflow_plus_test/src/cluster/quorum.rs new file mode 100644 index 000000000000..7990cba7c233 --- /dev/null +++ b/hydroflow_plus_test/src/cluster/quorum.rs @@ -0,0 +1,97 @@ +use std::hash::Hash; + +use hydroflow_plus::*; +use location::NoTick; + +#[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] +pub fn collect_quorum< + 'a, + L: Location<'a> + NoTick, + Order, + K: Clone + Eq + Hash, + V: Clone, + E: Clone, +>( + tick: &Tick, + responses: Stream<(K, Result), Tick, Bounded, Order>, + min: usize, + max: usize, +) -> ( + Stream<(K, V), Tick, Bounded, Order>, + Stream<(K, E), Tick, Bounded, Order>, +) { + let (not_all_complete_cycle, not_all) = tick.cycle::>(); + + let current_responses = not_all.union(responses.clone()); + + let count_per_key = current_responses.clone().fold_keyed_commutative( + q!(move || (0, 0)), + q!(move |accum, value| { + if value.is_ok() { + accum.0 += 1; + } else { + accum.1 += 1; + } + }), + ); + + let not_reached_min_count = + count_per_key + .clone() + .filter_map(q!(move |(key, (success, _error))| if success < min { + Some(key) + } else { + None + })); + + let reached_min_count = + count_per_key + .clone() + .filter_map(q!(move |(key, (success, _error))| if success >= min { + Some(key) + } else { + None + })); + + let just_reached_quorum = if max == min { + not_all_complete_cycle + .complete_next_tick(current_responses.clone().anti_join(reached_min_count)); + + current_responses.anti_join(not_reached_min_count) + } else { + let (min_but_not_max_complete_cycle, min_but_not_max) = tick.cycle(); + + let received_from_all = + count_per_key.filter_map(q!( + move |(key, (success, error))| if (success + error) >= max { + Some(key) + } else { + None + } + )); + + min_but_not_max_complete_cycle + .complete_next_tick(reached_min_count.filter_not_in(received_from_all.clone())); + + not_all_complete_cycle.complete_next_tick( + current_responses + .clone() + .anti_join(received_from_all.clone()), + ); + + current_responses + .anti_join(not_reached_min_count) + .anti_join(min_but_not_max) + }; + + ( + just_reached_quorum.filter_map(q!(move |(key, res)| match res { + Ok(v) => Some((key, v)), + Err(_) => None, + })), + responses.filter_map(q!(move |(key, res)| match res { + Ok(_) => None, + Err(e) => Some((key, e)), + })), + ) +} diff --git a/hydroflow_plus_test/src/cluster/request_response.rs b/hydroflow_plus_test/src/cluster/request_response.rs new file mode 100644 index 000000000000..4c8e71af1207 --- /dev/null +++ b/hydroflow_plus_test/src/cluster/request_response.rs @@ -0,0 +1,35 @@ +use std::hash::Hash; + +use hydroflow_plus::*; +use location::NoTick; +use stream::NoOrder; + +type JoinResponses = Stream<(K, (M, V)), Tick, Bounded, NoOrder>; + +/// Given an incoming stream of request-response responses, joins with metadata generated +/// at request time that is stored in-memory. +/// +/// Only one response element should be produced with a given key, same for the metadata stream. +pub fn join_responses<'a, K: Clone + Eq + Hash, M: Clone, V: Clone, L: Location<'a> + NoTick>( + tick: &Tick, + responses: Stream<(K, V), Tick, Bounded, NoOrder>, + metadata: Stream<(K, M), Tick, Bounded, NoOrder>, +) -> JoinResponses { + let (remaining_to_join_complete_cycle, remaining_to_join) = + tick.cycle::>(); + + let remaining_and_new: Stream<(K, M), Tick, Bounded, _> = remaining_to_join.union(metadata); + + // TODO(shadaj): we should have a "split-join" operator + // that returns both join and anti-join without cloning + let joined_this_tick = + remaining_and_new + .clone() + .join(responses.clone()) + .map(q!(|(key, (meta, resp))| (key, (meta, resp)))); + + remaining_to_join_complete_cycle + .complete_next_tick(remaining_and_new.anti_join(responses.map(q!(|(key, _)| key)))); + + joined_this_tick +} diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 23c5ab44165d..392e8bfc87fc 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -46,27 +46,21 @@ expression: built.ir() input: Persist( Chain( Chain( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . max_ballot }), - input: CycleSource { - ident: Ident { - sym: cycle_1, - }, - location_kind: Cluster( - 0, - ), + CycleSource { + ident: Ident { + sym: cycle_1, }, + location_kind: Cluster( + 0, + ), }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | p2b . max_ballot }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 0, - ), + CycleSource { + ident: Ident { + sym: cycle_0, }, + location_kind: Cluster( + 0, + ), }, ), CycleSource { @@ -178,7 +172,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { inner: : CycleSource { ident: Ident { @@ -215,198 +209,252 @@ expression: built.ir() }, CycleSink { ident: Ident { - sym: cycle_1, + sym: cycle_6, }, - location_kind: Cluster( - 0, + location_kind: Tick( + 2, + Cluster( + 0, + ), ), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 1, - ), - from_key: None, - to_location: Cluster( - 0, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((p1a , max_ballot) , log) | (p1a . ballot . proposer_id , P1b { ballot : p1a . ballot , max_ballot , accepted : log }) }), - input: CrossSingleton( - CrossSingleton( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P1a) , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( + input: DeferTick( + Difference( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , (usize , usize)) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: Ballot > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success >= min__free { Some (key) } else { None } }), + input: Tee { + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , usize) > ({ use crate :: __staged :: cluster :: quorum :: * ; move | | (0 , 0) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , usize) , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot > , () > ({ use crate :: __staged :: cluster :: quorum :: * ; move | accum , value | { if value . is_ok () { accum . 0 += 1 ; } else { accum . 1 += 1 ; } } }), + input: Tee { + inner: : Chain( + CycleSource { + ident: Ident { + sym: cycle_5, + }, + location_kind: Tick( + 2, + Cluster( 0, ), - from_key: None, - to_location: Cluster( - 1, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P1a) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P1a > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), + ), + }, + Tee { + inner: : Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >)) , (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 1, + ), + from_key: None, + to_location: Cluster( + 0, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >)) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >) > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >) > (& b) . unwrap ()) }", + ], + }, + ), + ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: P1a > ({ use crate :: __staged :: cluster :: paxos :: * ; | ballot | P1a { ballot } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , ()) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((ballot , max_ballot) , log) | (ballot . proposer_id , (ballot , if ballot == max_ballot { Ok (log) } else { Err (max_ballot) })) }), + input: CrossSingleton( + CrossSingleton( Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout__free = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout__free) } else { true } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), - input: Persist( - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 0, + ), + from_key: None, + to_location: Cluster( + 1, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: Ballot) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: Ballot > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | println ! ("Proposer leader expired, sending P1a") }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , ()) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout__free = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout__free) } else { true } } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), + input: Persist( + Tee { + inner: , + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, + }, + }, + }, + ), + }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), + input: Source { + source: Stream( + { use hydroflow_plus :: __staged :: location :: * ; let delay__free = { use crate :: __staged :: cluster :: paxos :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let i_am_leader_check_timeout_delay_multiplier__free = 1usize ; Duration :: from_secs ((CLUSTER_SELF_ID__free . raw_id * i_am_leader_check_timeout_delay_multiplier__free as u32) . into ()) } ; let interval__free = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout__free = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout__free) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay__free , interval__free)) }, + ), + location_kind: Cluster( + 0, + ), + }, + }, + ), }, }, - }, - ), + ), + }, }, }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), - input: Source { - source: Stream( - { use hydroflow_plus :: __staged :: location :: * ; let delay__free = { use crate :: __staged :: cluster :: paxos :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (__hydroflow_plus_cluster_self_id_0) ; let i_am_leader_check_timeout_delay_multiplier__free = 1usize ; Duration :: from_secs ((CLUSTER_SELF_ID__free . raw_id * i_am_leader_check_timeout_delay_multiplier__free as u32) . into ()) } ; let interval__free = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout__free = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout__free) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval_at (tokio :: time :: Instant :: now () + delay__free , interval__free)) }, - ), - location_kind: Cluster( - 0, - ), + }, + }, + }, + Tee { + inner: : Chain( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( + Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), + input: Tee { + inner: , + }, }, + ), + }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e__free = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e__free] }, + ), + location_kind: Cluster( + 1, + ), }, ), - }, + ), }, ), - }, + CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Tick( + 3, + Cluster( + 1, + ), + ), + }, + ), }, }, }, }, }, - }, - Tee { - inner: : Chain( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | p1a . ballot }), - input: Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1a , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1a | println ! ("Acceptor received P1a: {:?}" , p1a) }), - input: Tee { - inner: , - }, - }, - }, - ), - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e__free = { use crate :: __staged :: cluster :: paxos :: * ; Ballot { num : 0 , proposer_id : ClusterId :: from_raw (0) } } ; [e__free] }, - ), - location_kind: Cluster( - 1, - ), - }, - ), - ), - }, - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log . clone () }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Tick( - 3, - Cluster( - 1, - ), - ), - }, + ), }, - ), + }, }, }, - }, + Tee { + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , (usize , usize)) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: Ballot > > ({ use crate :: __staged :: cluster :: quorum :: * ; let max__free = 3usize ; move | (key , (success , error)) | if (success + error) >= max__free { Some (key) } else { None } }), + input: Tee { + inner: , + }, + }, + }, + ), + ), + }, + CycleSink { + ident: Ident { + sym: cycle_5, }, + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), + input: DeferTick( + AntiJoin( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + ), }, CycleSink { ident: Ident { @@ -419,41 +467,62 @@ expression: built.ir() ), ), input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (() , ()) , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | num_received | if num_received > f__free { Some (true) } else { None } }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p1b , ballot) | p1b . ballot == * ballot }), - input: CrossSingleton( - Persist( - Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | println ! ("Proposer received P1b: {:?}" , p1b) }), - input: Tee { - inner: , - }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | () }), + input: Tee { + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > >) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; move | ((quorum_ballot , quorum_accepted) , my_ballot) | if quorum_ballot == my_ballot { Some (quorum_accepted) } else { None } }), + input: CrossSingleton( + Reduce { + f: { let key_fn = stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > >) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | t | t . 0 }) ; move | curr , new | { if key_fn (& new) > key_fn (& * curr) { * curr = new ; } } }, + input: FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | vec ! [] }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | logs , log | { logs . push (log) ; } }), + input: Persist( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >) , core :: option :: Option < (hydroflow_plus_test :: cluster :: paxos :: Ballot , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) > > ({ use crate :: __staged :: cluster :: quorum :: * ; move | (key , res) | match res { Ok (v) => Some ((key , v)) , Err (_) => None , } }), + input: AntiJoin( + AntiJoin( + Tee { + inner: , + }, + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , (usize , usize)) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: Ballot > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success < min__free { Some (key) } else { None } }), + input: Tee { + inner: , + }, + }, + ), + CycleSource { + ident: Ident { + sym: cycle_6, + }, + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), + }, + ), }, ), - Tee { - inner: , - }, - ), + }, }, - }, + Tee { + inner: , + }, + ), }, }, }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), input: Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot) , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | _ | () }), input: Filter { f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot) , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; | (received_max_ballot , cur_ballot) | * received_max_ballot <= * cur_ballot }), @@ -475,7 +544,24 @@ expression: built.ir() }, CycleSink { ident: Ident { - sym: cycle_5, + sym: cycle_1, + }, + location_kind: Cluster( + 0, + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ , ballot) | ballot }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: Ballot , core :: result :: Result < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot >) , core :: option :: Option < (hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; move | (key , res) | match res { Ok (_) => None , Err (e) => Some ((key , e)) , } }), + input: Tee { + inner: , + }, + }, + }, + }, + CycleSink { + ident: Ident { + sym: cycle_7, }, location_kind: Tick( 2, @@ -485,186 +571,166 @@ expression: built.ir() ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , base_slot) | base_slot + num_payloads }), input: CrossSingleton( - Chain( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , ()) , usize > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Chain( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | max_slot + 1 }), - input: Tee { - inner: : Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), - input: Tee { - inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P1b < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > , std :: collections :: hash_map :: IntoIter < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p1b | p1b . accepted . into_iter () }), - input: Tee { - inner: , - }, + Tee { + inner: : Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) , usize) , (usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((index , payload) , base_slot) | (base_slot + index , payload) }), + input: CrossSingleton( + Enumerate { + is_static: false, + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , ()) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 2, + ), + from_key: None, + to_location: Cluster( + 0, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | ((key , value) , leader_id) | (leader_id , KvPayload { key , value : (CLUSTER_SELF_ID__free , value) }) }), + input: CrossSingleton( + CycleSource { + ident: Ident { + sym: cycle_1, + }, + location_kind: Cluster( + 2, + ), + }, + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot : Ballot | ballot . proposer_id }), + input: Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), + input: Persist( + Inspect { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), + input: CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 2, + ), + }, + }, + ), + }, + }, + }, + ), }, }, }, - }, - }, - }, - }, - Persist( - Source { - source: Iter( - { use hydroflow_plus :: __staged :: location :: * ; let e__free = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e__free] }, - ), - location_kind: Cluster( - 0, - ), - }, - ), - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: : CycleSource { - ident: Ident { - sym: cycle_5, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Tee { + inner: , + }, }, - location_kind: Tick( - 2, - Cluster( - 0, - ), - ), - }, + ), }, }, - }, - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (num_payloads , next_slot) | next_slot + num_payloads }), - input: CrossSingleton( - Tee { - inner: : Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , usize) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (((index , payload) , next_slot) , ballot) | P2a { ballot , slot : next_slot + index , value : Some (payload) } }), - input: CrossSingleton( - CrossSingleton( - Enumerate { - is_static: false, + Tee { + inner: : Chain( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | max_slot + 1 }), + input: Tee { + inner: : Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 2, - ), - from_key: None, - to_location: Cluster( - 0, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer >) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; move | (key , leader_id) | (leader_id , KvPayload { key , value : CLUSTER_SELF_ID__free }) }), - input: CrossSingleton( - CycleSource { - ident: Ident { - sym: cycle_1, - }, - location_kind: Cluster( - 2, - ), - }, - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot : Ballot | ballot . proposer_id }), - input: Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), - input: Persist( - Inspect { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | ballot | println ! ("Client notified that leader was elected: {:?}" , ballot) }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 2, - ), - }, - }, - ), - }, + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | d | d }), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > , std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > > ({ use hydroflow_plus :: __staged :: optional :: * ; | v | v }), + input: Tee { + inner: , }, }, - ), + }, }, }, }, }, - Tee { - inner: , + }, + }, + Chain( + CycleSource { + ident: Ident { + sym: cycle_7, }, - ), - Tee { - inner: , + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), }, + Persist( + Source { + source: Iter( + { use hydroflow_plus :: __staged :: location :: * ; let e__free = { use crate :: __staged :: cluster :: paxos :: * ; 0 } ; [e__free] }, + ), + location_kind: Cluster( + 0, + ), + }, + ), ), - }, + ), }, - }, - }, - Tee { - inner: , + ), }, - ), - }, - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), - input: Tee { - inner: , + }, }, }, + Tee { + inner: , + }, ), }, ), }, CycleSink { ident: Ident { - sym: cycle_6, + sym: cycle_9, }, location_kind: Tick( 2, @@ -674,213 +740,187 @@ expression: built.ir() ), input: DeferTick( Difference( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _value) | slot }), + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , usize)) , core :: option :: Option < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success >= min__free { Some (key) } else { None } }), input: Tee { - inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , _ballot) , (count , value)) | if count > f__free { Some ((slot , value)) } else { None } }), + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , usize) > ({ use crate :: __staged :: cluster :: quorum :: * ; move | | (0 , 0) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , usize) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot > , () > ({ use crate :: __staged :: cluster :: quorum :: * ; move | accum , value | { if value . is_ok () { accum . 0 += 1 ; } else { accum . 1 += 1 ; } } }), input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (k , (count , v)) | (k , (count , v . unwrap ())) }), - input: FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | accum , value | { accum . 0 += 1 ; accum . 1 = Some (value) ; } }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | if p2b . ballot == p2b . max_ballot { Some (((p2b . slot , p2b . ballot) , p2b . value)) } else { None } }), - input: Tee { - inner: : Chain( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 1, - ), - from_key: None, - to_location: Cluster( - 0, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , P2b { ballot : p2a . ballot , max_ballot , slot : p2a . slot , value : p2a . value }) }), - input: CrossSingleton( - Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), - input: Network { - from_location: Cluster( - 0, - ), - from_key: None, - to_location: Cluster( - 1, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& data) . unwrap () . into ()) }", - ], - }, - ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > (& b) . unwrap ()) }", - ], - }, - ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Chain( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , ()) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Chain( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , (count , entry)) , ballot) | { let entry = entry . unwrap () ; if count <= f__free { Some (P2a { ballot , slot , value : entry . value , }) } else { None } } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), + inner: : Chain( + CycleSource { + ident: Ident { + sym: cycle_8, + }, + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), + }, + Tee { + inner: : Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 1, + ), + from_key: None, + to_location: Cluster( + 0, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >)) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >) > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Acceptor > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >) > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | (p2a . ballot . proposer_id , ((p2a . slot , p2a . ballot) , if p2a . ballot == max_ballot { Ok (()) } else { Err (max_ballot) })) }), + input: CrossSingleton( + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + input: Network { + from_location: Cluster( + 0, + ), + from_key: None, + to_location: Cluster( + 1, + ), + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > (& b) . unwrap ()) }", + ], + }, + ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((slot , ballot) , value) | P2a { ballot , slot , value } }), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) , ()) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Chain( + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((slot , payload) , ballot) | ((slot , ballot) , Some (payload)) }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2a | ((p2a . slot , p2a . ballot) , p2a . value) }), + input: Chain( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >)) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , (count , entry)) , ballot) | { let entry = entry . unwrap () ; if count <= f__free { Some (P2a { ballot , slot , value : entry . value , }) } else { None } } }), + input: CrossSingleton( + Tee { + inner: , }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , ballot) | P2a { ballot , slot , value : None } }), - input: CrossSingleton( - Difference( - FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: ops :: Range < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), - input: Tee { - inner: , - }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), - input: Tee { - inner: , - }, - }, - ), - Tee { - inner: , - }, - ), + Tee { + inner: , }, ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (bool , ()) , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: , - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: DeferTick( - Tee { - inner: , - }, - ), - }, - }, - }, - ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , ballot) | P2a { ballot , slot , value : None } }), + input: CrossSingleton( + Difference( + FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: ops :: Range < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; | max_slot | 0 .. max_slot }), + input: Tee { + inner: , + }, }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + input: Tee { + inner: , + }, + }, + ), + Tee { + inner: , }, - }, - ), - }, - Tee { - inner: , - }, - ), - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: Tee { - inner: , - }, + ), + }, + ), }, ), - }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: Tee { + inner: , + }, + }, + ), }, }, }, }, - Tee { - inner: , - }, - ), + }, }, }, - }, - }, - CycleSource { - ident: Ident { - sym: cycle_7, - }, - location_kind: Tick( - 2, - Cluster( - 0, - ), + Tee { + inner: , + }, ), }, - ), + }, }, }, }, - }, + ), }, }, }, }, Tee { - inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >)) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , _ballot) , (count , _p2b)) | if count == 2 * f__free + 1 { Some (slot) } else { None } }), + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , usize)) , core :: option :: Option < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let max__free = 3usize ; move | (key , (success , error)) | if (success + error) >= max__free { Some (key) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -889,7 +929,7 @@ expression: built.ir() }, CycleSink { ident: Ident { - sym: cycle_7, + sym: cycle_8, }, location_kind: Tick( 2, @@ -898,20 +938,89 @@ expression: built.ir() ), ), input: DeferTick( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_slot , p2b) | p2b }), - input: AntiJoin( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , (usize , hydroflow_plus_test :: cluster :: paxos :: P2b < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2b | (p2b . slot , p2b) }), - input: Tee { - inner: , + AntiJoin( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + ), + }, + CycleSink { + ident: Ident { + sym: cycle_10, + }, + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), + input: DeferTick( + AntiJoin( + Tee { + inner: : Chain( + CycleSource { + ident: Ident { + sym: cycle_10, + }, + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), + }, + Tee { + inner: , + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ()) , (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > ({ use crate :: __staged :: cluster :: request_response :: * ; | (key , _) | key }), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ()) > ({ use crate :: __staged :: cluster :: paxos :: * ; | k | (k , ()) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ()) , (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (k , _) | k }), + input: FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < () > ({ use hydroflow_plus :: __staged :: stream :: * ; | | () }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < () , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _ , _ | { } }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >) , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ()) > > ({ use crate :: __staged :: cluster :: quorum :: * ; move | (key , res) | match res { Ok (v) => Some ((key , v)) , Err (_) => None , } }), + input: AntiJoin( + AntiJoin( + Tee { + inner: , + }, + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , usize)) , core :: option :: Option < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success < min__free { Some (key) } else { None } }), + input: Tee { + inner: , + }, + }, + ), + CycleSource { + ident: Ident { + sym: cycle_9, + }, + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), + }, + ), + }, + }, + }, }, }, - Tee { - inner: , - }, - ), - }, + }, + ), ), }, CycleSink { @@ -924,102 +1033,105 @@ expression: built.ir() 1, ), ), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { for slot in (prev_checkpoint . unwrap_or (0)) .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = Some (new_checkpoint) ; } CheckpointOrP2a :: P2a (p2a) => { if prev_checkpoint . map (| prev | p2a . slot > prev) . unwrap_or (true) && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), - input: Persist( - Chain( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), - input: CrossSingleton( - Tee { - inner: , - }, - Tee { - inner: , - }, - ), - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), - input: Delta( - Reduce { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ckpnt , log) | log }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (None , HashMap :: new ()) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (core :: option :: Option < usize > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | (prev_checkpoint , log) , checkpoint_or_p2a | { match checkpoint_or_p2a { CheckpointOrP2a :: Checkpoint (new_checkpoint) => { if prev_checkpoint . map (| prev | new_checkpoint > prev) . unwrap_or (true) { for slot in (prev_checkpoint . unwrap_or (0)) .. new_checkpoint { log . remove (& slot) ; } * prev_checkpoint = Some (new_checkpoint) ; } } CheckpointOrP2a :: P2a (p2a) => { if prev_checkpoint . map (| prev | p2a . slot > prev) . unwrap_or (true) && log . get (& p2a . slot) . map (| prev_p2a : & LogValue < _ > | p2a . ballot > prev_p2a . ballot) . unwrap_or (true) { log . insert (p2a . slot , LogValue { ballot : p2a . ballot , value : p2a . value , } ,) ; } } } } }), + input: Persist( + Chain( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | (p2a , max_ballot) | if p2a . ballot >= max_ballot { Some (CheckpointOrP2a :: P2a (p2a)) } else { None } }), + input: CrossSingleton( + Tee { + inner: , + }, + Tee { + inner: , + }, + ), + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , hydroflow_plus_test :: cluster :: paxos :: CheckpointOrP2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > ({ use crate :: __staged :: cluster :: paxos :: * ; | min_seq | CheckpointOrP2a :: Checkpoint (min_seq) }), + input: Delta( + Reduce { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new < * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , ()) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Tee { - inner: : ReduceKeyed { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), - input: Persist( - Network { - from_location: Cluster( - 3, - ), - from_key: None, - to_location: Cluster( - 1, - ), - to_key: None, - serialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , usize) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < usize > (& data) . unwrap () . into ()) }", - ], - }, + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_sender , seq) | seq }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , ()) , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: : ReduceKeyed { + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_seq , seq | { if seq > * curr_seq { * curr_seq = seq ; } } }), + input: Persist( + Network { + from_location: Cluster( + 3, ), - ), - instantiate_fn: , - deserialize_pipeline: Some( - Operator( - Operator { - path: "map", - args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < usize > (& b) . unwrap ()) }", - ], - }, + from_key: None, + to_location: Cluster( + 1, ), - ), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), - input: CycleSource { - ident: Ident { - sym: cycle_0, - }, - location_kind: Cluster( - 3, + to_key: None, + serialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , usize) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < usize > (& data) . unwrap () . into ()) }", + ], + }, + ), + ), + instantiate_fn: , + deserialize_pipeline: Some( + Operator( + Operator { + path: "map", + args: [ + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < usize > (& b) . unwrap ()) }", + ], + }, ), + ), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > > > (__hydroflow_plus_cluster_ids_1) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + input: CycleSource { + ident: Ident { + sym: cycle_0, + }, + location_kind: Cluster( + 3, + ), + }, }, }, - }, - ), + ), + }, }, - }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | num_received | if num_received == f__free + 1 { Some (true) } else { None } }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _u | () }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , core :: option :: Option < bool > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | num_received | if num_received == f__free + 1 { Some (true) } else { None } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , usize) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , + }, }, }, }, - }, - ), + ), + }, }, }, - }, - ), - }, + ), + }, + ), ), - ), + }, }, }, CycleSink { @@ -1029,8 +1141,14 @@ expression: built.ir() location_kind: Cluster( 0, ), - input: Tee { - inner: , + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot > ({ use crate :: __staged :: cluster :: paxos :: * ; | (_ , ballot) | ballot }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >) , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; move | (key , res) | match res { Ok (_) => None , Err (e) => Some ((key , e)) , } }), + input: Tee { + inner: , + }, + }, }, }, CycleSink { @@ -1038,22 +1156,22 @@ expression: built.ir() sym: cycle_1, }, location_kind: Tick( - 6, + 7, Cluster( 3, ), ), input: DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq > * highest_seq }), input: CrossSingleton( Tee { - inner: : Sort( + inner: : Sort( Chain( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 0, @@ -1068,7 +1186,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > (& data) . unwrap () . into ()) }", ], }, ), @@ -1079,31 +1197,29 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos :: Proposer > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > (& b) . unwrap ()) }", ], }, ), ), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , std :: iter :: Map < std :: slice :: Iter < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > , _ > > ({ use hydroflow_plus :: __staged :: stream :: * ; let ids__free = unsafe { :: std :: mem :: transmute :: < _ , & :: std :: vec :: Vec < hydroflow_plus :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > > > (__hydroflow_plus_cluster_ids_3) } ; | b | ids__free . iter () . map (move | id | (:: std :: clone :: Clone :: clone (id) , :: std :: clone :: Clone :: clone (& b))) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), - input: AntiJoin( - Tee { - inner: , - }, - CycleSource { - ident: Ident { - sym: cycle_6, - }, - location_kind: Tick( - 2, - Cluster( - 0, - ), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (slot , kv) | SequencedKv { seq : slot , kv } }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , ())) , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | ((slot , _ballot) , (value , _)) | (slot , value) }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , ())) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , ())) > ({ use crate :: __staged :: cluster :: request_response :: * ; | (key , (meta , resp)) | (key , (meta , resp)) }), + input: Join( + Tee { + inner: , + }, + Tee { + inner: , + }, ), }, - ), + }, }, }, }, @@ -1113,7 +1229,7 @@ expression: built.ir() sym: cycle_1, }, location_kind: Tick( - 6, + 7, Cluster( 3, ), @@ -1123,14 +1239,14 @@ expression: built.ir() ), }, Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < usize > , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | v | v }), input: Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < usize > , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let expected_next_slot = std :: cmp :: max (filled_slot . map (| v | v + 1) . unwrap_or (0) , highest_seq . map (| v | v + 1) . unwrap_or (0) ,) ; if sorted_payload . seq == expected_next_slot { * filled_slot = Some (sorted_payload . seq) ; } } }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < usize > , (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , core :: option :: Option < usize >) , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | filled_slot , (sorted_payload , highest_seq) | { let expected_next_slot = std :: cmp :: max (filled_slot . map (| v | v + 1) . unwrap_or (0) , highest_seq . map (| v | v + 1) . unwrap_or (0) ,) ; if sorted_payload . seq == expected_next_slot { * filled_slot = Some (sorted_payload . seq) ; } } }), input: CrossSingleton( Tee { - inner: , + inner: , }, Chain( Map { @@ -1140,7 +1256,7 @@ expression: built.ir() sym: cycle_2, }, location_kind: Tick( - 6, + 7, Cluster( 3, ), @@ -1172,30 +1288,30 @@ expression: built.ir() sym: cycle_2, }, location_kind: Tick( - 6, + 7, Cluster( 3, ), ), input: DeferTick( Tee { - inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , core :: option :: Option < usize >) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (_kv_store , highest_seq) | highest_seq }), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < usize >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == (last_seq . map (| s | s + 1) . unwrap_or (0)) , "Hole in log between seq {:?} and {}" , * last_seq , payload . seq) ; * last_seq = Some (payload . seq) ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , core :: option :: Option < usize >) > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | | (HashMap :: new () , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: collections :: hash_map :: HashMap < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , core :: option :: Option < usize >) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , () > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (kv_store , last_seq) , payload | { if let Some (kv) = payload . kv { kv_store . insert (kv . key , kv . value) ; } debug_assert ! (payload . seq == (last_seq . map (| s | s + 1) . unwrap_or (0)) , "Hole in log between seq {:?} and {}" , * last_seq , payload . seq) ; * last_seq = Some (payload . seq) ; } }), input: Persist( Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , usize) , hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , _) | { sorted_payload } }), input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < (hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , usize) , bool > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | (sorted_payload , highest_seq) | sorted_payload . seq <= * highest_seq }), input: CrossSingleton( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1212,14 +1328,14 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 6, + 7, Cluster( 3, ), ), input: DeferTick( Tee { - inner: : FilterMap { + inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < usize > , usize) , core :: option :: Option < usize > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; let checkpoint_frequency__free = 1usize ; move | (max_checkpointed_seq , new_highest_seq) | if max_checkpointed_seq . map (| m | new_highest_seq - m >= checkpoint_frequency__free) . unwrap_or (true) { Some (new_highest_seq) } else { None } }), input: CrossSingleton( Chain( @@ -1233,7 +1349,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 6, + 7, Cluster( 3, ), @@ -1254,7 +1370,7 @@ expression: built.ir() ), ), Tee { - inner: , + inner: , }, ), }, @@ -1269,7 +1385,7 @@ expression: built.ir() 3, ), input: Tee { - inner: , + inner: , }, }, CycleSink { @@ -1320,9 +1436,29 @@ expression: built.ir() inner: , }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < bool , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Tee { - inner: , + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + input: Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (() , ()) , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), + input: CrossSingleton( + Tee { + inner: , + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: DeferTick( + Tee { + inner: , + }, + ), + }, + }, + }, + ), }, }, ), @@ -1344,11 +1480,21 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: : Chain( - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (u32 , ()) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | replica_payload | (replica_payload . key , ()) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), + inner: : Chain( + CycleSource { + ident: Ident { + sym: cycle_2, + }, + location_kind: Tick( + 0, + Cluster( + 2, + ), + ), + }, + Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > , ((u32 , u32) , core :: result :: Result < () , () >)) , ((u32 , u32) , core :: result :: Result < () , () >) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { from_location: Cluster( 3, @@ -1363,7 +1509,7 @@ expression: built.ir() Operator { path: "map", args: [ - "| (id , data) : (hydroflow_plus :: ClusterId < _ > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& data) . unwrap () . into ()) }", + "| (id , data) : (hydroflow_plus :: ClusterId < _ > , ((u32 , u32) , core :: result :: Result < () , () >)) | { (id . raw_id , hydroflow_plus :: runtime_support :: bincode :: serialize :: < ((u32 , u32) , core :: result :: Result < () , () >) > (& data) . unwrap () . into ()) }", ], }, ), @@ -1374,44 +1520,33 @@ expression: built.ir() Operator { path: "map", args: [ - "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > (& b) . unwrap ()) }", + "| res | { let (id , b) = res . unwrap () ; (hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_kv :: Replica > :: from_raw (id) , hydroflow_plus :: runtime_support :: bincode :: deserialize :: < ((u32 , u32) , core :: result :: Result < () , () >) > (& b) . unwrap ()) }", ], }, ), ), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > >) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value , payload) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , ((u32 , u32) , core :: result :: Result < () , () >)) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . value . 0 , ((payload . key , payload . value . 1) , Ok (()))) }), input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), + f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos_kv :: SequencedKv < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > ({ use crate :: __staged :: cluster :: paxos_kv :: * ; | payload | payload . kv }), input: Tee { - inner: , + inner: , }, }, }, }, }, }, - CycleSource { - ident: Ident { - sym: cycle_2, - }, - location_kind: Tick( - 0, - Cluster( - 2, - ), - ), - }, ), }, - Tee { - inner: : FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , usize) , core :: option :: Option < u32 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let f__free = 1usize ; move | (key , count) | { if count == f__free + 1 { Some (key) } else { None } } }), - input: FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | | 0 }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_count , _sender | { * curr_count += 1 ; } }), + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , (usize , usize)) , core :: option :: Option < (u32 , u32) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success >= min__free { Some (key) } else { None } }), + input: Tee { + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , usize) > ({ use crate :: __staged :: cluster :: quorum :: * ; move | | (0 , 0) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , usize) , core :: result :: Result < () , () > , () > ({ use crate :: __staged :: cluster :: quorum :: * ; move | accum , value | { if value . is_ok () { accum . 0 += 1 ; } else { accum . 1 += 1 ; } } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1428,9 +1563,9 @@ expression: built.ir() ), input: Chain( FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node__free = 1usize ; move | _ | (0 .. num_clients_per_node__free) . map (move | i | i as u32) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node__free = 1usize ; move | _ | (0 .. num_clients_per_node__free) . map (move | i | ((CLUSTER_SELF_ID__free . raw_id * (num_clients_per_node__free as u32)) + i as u32 , 0)) }), input: Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < () , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | * curr = new }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | () }), @@ -1443,9 +1578,30 @@ expression: built.ir() }, }, }, - Tee { - inner: : Tee { - inner: , + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , u32) , (u32 , u32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . 0 , payload . 1 + 1) }), + input: Tee { + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , ()) , (u32 , u32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (k , _) | k }), + input: FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < () > ({ use hydroflow_plus :: __staged :: stream :: * ; | | () }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < () , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _ , _ | { } }), + input: FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , core :: result :: Result < () , () >) , core :: option :: Option < ((u32 , u32) , ()) > > ({ use crate :: __staged :: cluster :: quorum :: * ; move | (key , res) | match res { Ok (v) => Some ((key , v)) , Err (_) => None , } }), + input: AntiJoin( + Tee { + inner: , + }, + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , (usize , usize)) , core :: option :: Option < (u32 , u32) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success < min__free { Some (key) } else { None } }), + input: Tee { + inner: , + }, + }, + ), + }, + }, + }, }, }, ), @@ -1466,7 +1622,7 @@ expression: built.ir() input: Chain( Chain( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_3, }, @@ -1483,16 +1639,16 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: , + inner: , }, }, }, ), Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < u32 , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | key | (key as usize , SystemTime :: now ()) }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , u32) , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (key , _prev_count) | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1519,10 +1675,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1530,7 +1686,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: : Source { + inner: : Source { source: Stream( { use hydroflow_plus :: __staged :: location :: * ; let interval__free = { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval__free)) }, ), @@ -1558,9 +1714,9 @@ expression: built.ir() input: CrossSingleton( Fold { init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , u32 , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (u32 , u32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1571,7 +1727,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1583,7 +1739,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1594,7 +1750,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index 8dfea4fb1f8f..0d50a88f51ae 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -19,13 +19,13 @@ pub fn graph_reachability<'a>( let reachability_tick = process.tick(); let (set_reached_cycle, reached_cycle) = reachability_tick.cycle::>(); - let reached = roots.tick_batch(&reachability_tick).chain(reached_cycle); + let reached = roots.tick_batch(&reachability_tick).union(reached_cycle); let reachable = reached .clone() .map(q!(|r| (r, ()))) .join(edges.tick_batch(&reachability_tick).persist()) .map(q!(|(_from, (_, to))| to)); - set_reached_cycle.complete_next_tick(reached.clone().chain(reachable)); + set_reached_cycle.complete_next_tick(reached.clone().union(reachable)); reached.all_ticks().unique().for_each(q!(|v| { reached_out.send(v).unwrap(); diff --git a/stageleft/src/type_name.rs b/stageleft/src/type_name.rs index 1077e54dde30..486fb866f0a5 100644 --- a/stageleft/src/type_name.rs +++ b/stageleft/src/type_name.rs @@ -93,6 +93,24 @@ impl VisitMut for RewriteAlloc { .chain(i.segments.iter().skip(4).cloned()), ), }; + } else if i.segments.iter().take(3).collect::>() + == vec![ + &syn::PathSegment::from(syn::Ident::new("std", Span::call_site())), + &syn::PathSegment::from(syn::Ident::new("vec", Span::call_site())), + &syn::PathSegment::from(syn::Ident::new("into_iter", Span::call_site())), + ] + { + *i = syn::Path { + leading_colon: i.leading_colon, + segments: syn::punctuated::Punctuated::from_iter( + vec![ + syn::PathSegment::from(syn::Ident::new("std", Span::call_site())), + syn::PathSegment::from(syn::Ident::new("vec", Span::call_site())), + ] + .into_iter() + .chain(i.segments.iter().skip(3).cloned()), + ), + }; } else if i.segments.iter().take(3).collect::>() == vec![ &syn::PathSegment::from(syn::Ident::new("tokio", Span::call_site())), @@ -134,10 +152,16 @@ impl VisitMut for RewriteAlloc { syn::parse2(get_final_crate_name(final_name)).unwrap(); i.segments.insert(1, parse_quote!(__staged)); + } else { + syn::visit_mut::visit_path_mut(self, i); + return; } + } else { + syn::visit_mut::visit_path_mut(self, i); + return; } - syn::visit_mut::visit_path_mut(self, i); + self.visit_path_mut(i); } } From 939389953875bf5f94ea84503a7a35efd7342282 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 27 Nov 2024 14:26:31 -0800 Subject: [PATCH 70/74] feat(hydroflow_plus)!: mark non-deterministic operators as unsafe and introduce timestamped streams (#1584) Big PR. First big change is we introduce a `Timestamped` location. This is a bit of a hybrid between top-level locations and `Tick` locations. The idea is that you choose where timestamps are generated, and then have a guarantee that everything after that will be atomically computed (useful for making sure we add payloads to the log before ack-ing). The contract is that an operator or module that takes a `Timestamped` input must still be deterministic regardless of the stamps on messages (which are hidden unless you `tick_batch`). But unlike a top-level stream (which has the same constraints), you have the atomicity guarantee. Right now the guarantee is trivial since we have one global tick for everything. But in the future when we want to apply @davidchuyaya's optimizations this will be helpful to know when there are causal dependencies on when data can be sent to others. Second change is we mark every non-deterministic operator (modulo explicit annotations such as `NoOrder`) with Rust's `unsafe` keyword. This makes it super clear where non-determinism is taking place. I've used this to put `unsafe` blocks throughout our example code and add `SAFETY` annotations that argue why the non-determinism is safe (or point out that we've explicitly documented / expect non-determinism). I also added `#![warn(unsafe_op_in_unsafe_fn)]` to the examples and the template, since this forces good hygiene of annotating sources of non-determinism even inside a module that is intentionally non-deterministic. Paxos changes are mostly refactors, and I verified that the performance is the same as before. --- .vscode/settings.json | 9 + Cargo.toml | 1 + hydro_deploy/hydro_cli/src/lib.rs | 1 + hydroflow_plus/src/cycle.rs | 10 +- hydroflow_plus/src/location/cluster/mod.rs | 12 +- hydroflow_plus/src/location/mod.rs | 26 +- hydroflow_plus/src/location/tick.rs | 97 ++++- hydroflow_plus/src/optional.rs | 167 ++++++-- hydroflow_plus/src/rewrites/persist_pullup.rs | 8 +- hydroflow_plus/src/rewrites/properties.rs | 17 +- hydroflow_plus/src/singleton.rs | 257 ++++++++++-- hydroflow_plus/src/stream.rs | 315 ++++++++++---- hydroflow_plus_test/src/cluster/compute_pi.rs | 22 +- hydroflow_plus_test/src/cluster/map_reduce.rs | 40 +- hydroflow_plus_test/src/cluster/paxos.rs | 393 ++++++++++-------- .../src/cluster/paxos_bench.rs | 168 +++++--- hydroflow_plus_test/src/cluster/paxos_kv.rs | 44 +- hydroflow_plus_test/src/cluster/quorum.rs | 110 ++++- .../src/cluster/request_response.rs | 17 +- .../src/cluster/simple_cluster.rs | 2 - ...cluster__paxos_bench__tests__paxos_ir.snap | 216 ++++------ hydroflow_plus_test/src/cluster/two_pc.rs | 63 +-- .../src/local/chat_app.rs | 24 +- .../src/local/compute_pi.rs | 34 +- .../src/local/count_elems.rs | 11 +- .../src/local/graph_reachability.rs | 13 +- .../src/local/negation.rs | 36 +- .../src/local/teed_join.rs | 8 +- stageleft/src/lib.rs | 1 - template/hydroflow_plus/Cargo.toml | 3 + 30 files changed, 1481 insertions(+), 644 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 80aead1799ae..d650a5fd0e7c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,6 +13,15 @@ } } ], + "editor.semanticTokenColorCustomizations": { + "enabled": true, + "rules": { + "*.unsafe:rust": { + "foreground": "#ea1708", + "fontStyle": "bold" + } + } + }, "files.watcherExclude": { "**/target": true }, diff --git a/Cargo.toml b/Cargo.toml index ab8fef76bc85..224bad277975 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ opt-level = "s" [workspace.lints.rust] unused_qualifications = "warn" +unsafe_op_in_unsafe_fn = "warn" [workspace.lints.clippy] allow_attributes = "warn" diff --git a/hydro_deploy/hydro_cli/src/lib.rs b/hydro_deploy/hydro_cli/src/lib.rs index 2c1de84faff7..663abe4cc907 100644 --- a/hydro_deploy/hydro_cli/src/lib.rs +++ b/hydro_deploy/hydro_cli/src/lib.rs @@ -1,6 +1,7 @@ #![expect( unused_qualifications, non_local_definitions, + unsafe_op_in_unsafe_fn, reason = "for pyo3 generated code" )] diff --git a/hydroflow_plus/src/cycle.rs b/hydroflow_plus/src/cycle.rs index 4d61cc1d30ba..1ac45600f7b2 100644 --- a/hydroflow_plus/src/cycle.rs +++ b/hydroflow_plus/src/cycle.rs @@ -1,4 +1,4 @@ -use crate::location::Location; +use crate::location::{Location, LocationId}; use crate::staging_util::Invariant; pub enum ForwardRefMarker {} @@ -9,7 +9,7 @@ pub trait DeferTick { } pub trait CycleComplete<'a, T> { - fn complete(self, ident: syn::Ident); + fn complete(self, ident: syn::Ident, expected_location: LocationId); } pub trait CycleCollection<'a, T>: CycleComplete<'a, T> { @@ -30,24 +30,26 @@ pub trait CycleCollectionWithInitial<'a, T>: CycleComplete<'a, T> { /// See [`crate::FlowBuilder`] for an explainer on the type parameters. pub struct ForwardRef<'a, S: CycleComplete<'a, ForwardRefMarker>> { pub(crate) ident: syn::Ident, + pub(crate) expected_location: LocationId, pub(crate) _phantom: Invariant<'a, S>, } impl<'a, S: CycleComplete<'a, ForwardRefMarker>> ForwardRef<'a, S> { pub fn complete(self, stream: S) { let ident = self.ident; - S::complete(stream, ident) + S::complete(stream, ident, self.expected_location) } } pub struct TickCycle<'a, S: CycleComplete<'a, TickCycleMarker> + DeferTick> { pub(crate) ident: syn::Ident, + pub(crate) expected_location: LocationId, pub(crate) _phantom: Invariant<'a, S>, } impl<'a, S: CycleComplete<'a, TickCycleMarker> + DeferTick> TickCycle<'a, S> { pub fn complete_next_tick(self, stream: S) { let ident = self.ident; - S::complete(stream.defer_tick(), ident) + S::complete(stream.defer_tick(), ident, self.expected_location) } } diff --git a/hydroflow_plus/src/location/cluster/mod.rs b/hydroflow_plus/src/location/cluster/mod.rs index cfd83e195d8d..6d2b71cc3622 100644 --- a/hydroflow_plus/src/location/cluster/mod.rs +++ b/hydroflow_plus/src/location/cluster/mod.rs @@ -20,14 +20,10 @@ pub struct Cluster<'a, C> { pub trait IsCluster { type Tag; - fn id(&self) -> usize; } impl IsCluster for Cluster<'_, C> { type Tag = C; - fn id(&self) -> usize { - self.id - } } impl<'a, C> Cluster<'a, C> { @@ -125,8 +121,14 @@ where where Self: Sized, { + let cluster_id = if let LocationId::Cluster(id) = ctx.root().id() { + id + } else { + unreachable!() + }; + let ident = syn::Ident::new( - &format!("__hydroflow_plus_cluster_self_id_{}", ctx.root().id()), + &format!("__hydroflow_plus_cluster_self_id_{}", cluster_id), Span::call_site(), ); let root = get_this_crate(); diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index 61693cb7c66f..70e39d36a353 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -60,7 +60,7 @@ pub fn check_matching_location<'a, L: Location<'a>>(l1: &L, l2: &L) { } pub trait Location<'a>: Clone { - type Root; + type Root: Location<'a>; fn root(&self) -> Self::Root; @@ -160,7 +160,16 @@ pub trait Location<'a>: Clone { ) } - fn source_interval( + /// Generates a stream with values emitted at a fixed interval, with + /// each value being the current time (as an [`tokio::time::Instant`]). + /// + /// The clock source used is monotonic, so elements will be emitted in + /// increasing order. + /// + /// # Safety + /// Because this stream is generated by an OS timer, it will be + /// non-deterministic because each timestamp will be arbitrary. + unsafe fn source_interval( &self, interval: impl QuotedWithContext<'a, Duration, Self> + Copy + 'a, ) -> Stream @@ -172,7 +181,17 @@ pub trait Location<'a>: Clone { ))) } - fn source_interval_delayed( + /// Generates a stream with values emitted at a fixed interval (with an + /// initial delay), with each value being the current time + /// (as an [`tokio::time::Instant`]). + /// + /// The clock source used is monotonic, so elements will be emitted in + /// increasing order. + /// + /// # Safety + /// Because this stream is generated by an OS timer, it will be + /// non-deterministic because each timestamp will be arbitrary. + unsafe fn source_interval_delayed( &self, delay: impl QuotedWithContext<'a, Duration, Self> + Copy + 'a, interval: impl QuotedWithContext<'a, Duration, Self> + Copy + 'a, @@ -212,6 +231,7 @@ pub trait Location<'a>: Clone { ( ForwardRef { ident: ident.clone(), + expected_location: self.id(), _phantom: PhantomData, }, S::create_source(ident, self.clone()), diff --git a/hydroflow_plus/src/location/tick.rs b/hydroflow_plus/src/location/tick.rs index 809a750c9bca..7d23bec7fe08 100644 --- a/hydroflow_plus/src/location/tick.rs +++ b/hydroflow_plus/src/location/tick.rs @@ -1,6 +1,7 @@ use std::marker::PhantomData; use proc_macro2::Span; +use sealed::sealed; use stageleft::{q, QuotedWithContext}; use super::{Cluster, Location, LocationId, Process}; @@ -12,10 +13,50 @@ use crate::cycle::{ use crate::ir::{HfPlusNode, HfPlusSource}; use crate::{Bounded, Optional, Singleton, Stream}; +#[sealed] pub trait NoTick {} +#[sealed] impl NoTick for Process<'_, T> {} +#[sealed] impl NoTick for Cluster<'_, T> {} +#[sealed] +pub trait NoTimestamp {} +#[sealed] +impl NoTimestamp for Process<'_, T> {} +#[sealed] +impl NoTimestamp for Cluster<'_, T> {} +#[sealed] +impl<'a, L: Location<'a>> NoTimestamp for Tick {} + +#[derive(Clone)] +pub struct Timestamped { + pub(crate) tick: Tick, +} + +impl<'a, L: Location<'a>> Location<'a> for Timestamped { + type Root = L::Root; + + fn root(&self) -> Self::Root { + self.tick.root() + } + + fn id(&self) -> LocationId { + self.tick.id() + } + + fn flow_state(&self) -> &FlowState { + self.tick.flow_state() + } + + fn is_top_level() -> bool { + L::is_top_level() + } +} + +#[sealed] +impl NoTick for Timestamped {} + /// Marks the stream as being inside the single global clock domain. #[derive(Clone)] pub struct Tick { @@ -53,13 +94,20 @@ impl<'a, L: Location<'a>> Tick { batch_size: impl QuotedWithContext<'a, usize, L> + Copy + 'a, ) -> Stream<(), Self, Bounded> where - L: NoTick, + L: NoTick + NoTimestamp, { - self.l + let out = self + .l .spin() - .flat_map(q!(move |_| 0..batch_size)) + .flat_map_ordered(q!(move |_| 0..batch_size)) .map(q!(|_| ())) - .tick_batch(self) + .timestamped(self); + + unsafe { + // SAFETY: at runtime, `spin` produces a single value per tick, + // so each batch is guaranteed to be the same size. + out.tick_batch() + } } pub fn singleton( @@ -69,7 +117,10 @@ impl<'a, L: Location<'a>> Tick { where L: NoTick, { - self.outer().singleton(e).latest_tick(self) + unsafe { + // SAFETY: a top-level singleton produces the same value each tick + self.outer().singleton(e).timestamped(self).latest_tick() + } } pub fn singleton_first_tick( @@ -118,12 +169,46 @@ impl<'a, L: Location<'a>> Tick { ( ForwardRef { ident: ident.clone(), + expected_location: self.id(), _phantom: PhantomData, }, S::create_source(ident, self.clone()), ) } + pub fn forward_ref_timestamped< + S: CycleCollection<'a, ForwardRefMarker, Location = Timestamped>, + >( + &self, + ) -> (ForwardRef<'a, S>, S) { + let next_id = { + let on_id = match self.l.id() { + LocationId::Process(id) => id, + LocationId::Cluster(id) => id, + LocationId::Tick(_, _) => panic!(), + LocationId::ExternalProcess(_) => panic!(), + }; + + let mut flow_state = self.flow_state().borrow_mut(); + let next_id_entry = flow_state.cycle_counts.entry(on_id).or_default(); + + let id = *next_id_entry; + *next_id_entry += 1; + id + }; + + let ident = syn::Ident::new(&format!("cycle_{}", next_id), Span::call_site()); + + ( + ForwardRef { + ident: ident.clone(), + expected_location: self.id(), + _phantom: PhantomData, + }, + S::create_source(ident, Timestamped { tick: self.clone() }), + ) + } + pub fn cycle + DeferTick>( &self, ) -> (TickCycle<'a, S>, S) @@ -151,6 +236,7 @@ impl<'a, L: Location<'a>> Tick { ( TickCycle { ident: ident.clone(), + expected_location: self.id(), _phantom: PhantomData, }, S::create_source(ident, self.clone()), @@ -187,6 +273,7 @@ impl<'a, L: Location<'a>> Tick { ( TickCycle { ident: ident.clone(), + expected_location: self.id(), _phantom: PhantomData, }, S::create_source(ident, initial, self.clone()), diff --git a/hydroflow_plus/src/optional.rs b/hydroflow_plus/src/optional.rs index bfde8c2ef5e1..6d422d14d5bd 100644 --- a/hydroflow_plus/src/optional.rs +++ b/hydroflow_plus/src/optional.rs @@ -9,7 +9,10 @@ use syn::parse_quote; use crate::builder::FLOW_USED_MESSAGE; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRefMarker, TickCycleMarker}; use crate::ir::{HfPlusLeaf, HfPlusNode, HfPlusSource, TeeNode}; +use crate::location::tick::{NoTimestamp, Timestamped}; use crate::location::{check_matching_location, LocationId, NoTick}; +use crate::singleton::ZipResult; +use crate::stream::NoOrder; use crate::{Bounded, Location, Singleton, Stream, Tick, Unbounded}; pub struct Optional { @@ -61,7 +64,12 @@ impl<'a, T, L: Location<'a>> CycleCollection<'a, TickCycleMarker> } impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycleMarker> for Optional, Bounded> { - fn complete(self, ident: syn::Ident) { + fn complete(self, ident: syn::Ident, expected_location: LocationId) { + assert_eq!( + self.location.id(), + expected_location, + "locations do not match" + ); self.location .flow_state() .borrow_mut() @@ -94,7 +102,12 @@ impl<'a, T, L: Location<'a>> CycleCollection<'a, ForwardRefMarker> } impl<'a, T, L: Location<'a>> CycleComplete<'a, ForwardRefMarker> for Optional, Bounded> { - fn complete(self, ident: syn::Ident) { + fn complete(self, ident: syn::Ident, expected_location: LocationId) { + assert_eq!( + self.location.id(), + expected_location, + "locations do not match" + ); self.location .flow_state() .borrow_mut() @@ -127,7 +140,12 @@ impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRefMarker> } impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRefMarker> for Optional { - fn complete(self, ident: syn::Ident) { + fn complete(self, ident: syn::Ident, expected_location: LocationId) { + assert_eq!( + self.location.id(), + expected_location, + "locations do not match" + ); self.location .flow_state() .borrow_mut() @@ -179,15 +197,6 @@ impl<'a, T: Clone, L: Location<'a>, B> Clone for Optional { } impl<'a, T, L: Location<'a>, B> Optional { - // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream - pub fn into_stream(self) -> Stream { - if L::is_top_level() { - panic!("Converting an optional to a stream is not yet supported at the top level"); - } - - Stream::new(self.location, self.ir_node.into_inner()) - } - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F, L>) -> Optional { let f = f.splice_fn1_ctx(&self.location).into(); Optional::new( @@ -199,7 +208,7 @@ impl<'a, T, L: Location<'a>, B> Optional { ) } - pub fn flat_map, F: Fn(T) -> I + 'a>( + pub fn flat_map_ordered, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { @@ -213,11 +222,32 @@ impl<'a, T, L: Location<'a>, B> Optional { ) } - pub fn flatten(self) -> Stream + pub fn flat_map_unordered, F: Fn(T) -> I + 'a>( + self, + f: impl IntoQuotedMut<'a, F, L>, + ) -> Stream { + let f = f.splice_fn1_ctx(&self.location).into(); + Stream::new( + self.location, + HfPlusNode::FlatMap { + f, + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + + pub fn flatten_ordered(self) -> Stream where T: IntoIterator, { - self.flat_map(q!(|v| v)) + self.flat_map_ordered(q!(|v| v)) + } + + pub fn flatten_unordered(self) -> Stream + where + T: IntoIterator, + { + self.flat_map_unordered(q!(|v| v)) } pub fn filter bool + 'a>( @@ -350,48 +380,123 @@ impl<'a, T, L: Location<'a>> Optional { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } - pub fn then(self, value: Singleton) -> Optional { + pub fn then(self, value: Singleton) -> Optional + where + Singleton: ZipResult< + 'a, + Optional<(), L, Bounded>, + Location = L, + Out = Optional<(U, ()), L, Bounded>, + >, + { value.continue_if(self) } + + pub fn into_stream(self) -> Stream { + if L::is_top_level() { + panic!("Converting an optional to a stream is not yet supported at the top level"); + } + + Stream::new(self.location, self.ir_node.into_inner()) + } } -impl<'a, T, L: Location<'a> + NoTick, B> Optional { - pub fn latest_tick(self, tick: &Tick) -> Optional, Bounded> { +impl<'a, T, L: Location<'a> + NoTick, B> Optional, B> { + /// Given a tick, returns a optional value corresponding to a snapshot of the optional + /// as of that tick. The snapshot at tick `t + 1` is guaranteed to include at least all + /// relevant data that contributed to the snapshot at tick `t`. + /// + /// # Safety + /// Because this picks a snapshot of a optional whose value is continuously changing, + /// the output optional has a non-deterministic value since the snapshot can be at an + /// arbitrary point in time. + pub unsafe fn latest_tick(self) -> Optional, Bounded> { Optional::new( - tick.clone(), + self.location.tick, HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } - pub fn tick_samples(self) -> Stream { + pub fn drop_timestamp(self) -> Optional { + Optional::new(self.location.tick.l, self.ir_node.into_inner()) + } +} + +impl<'a, T, L: Location<'a> + NoTick, B> Optional { + pub fn timestamped(self, tick: &Tick) -> Optional, B> { + Optional::new( + Timestamped { tick: tick.clone() }, + self.ir_node.into_inner(), + ) + } + + /// Eagerly samples the optional as fast as possible, returning a stream of snapshots + /// with order corresponding to increasing prefixes of data contributing to the optional. + /// + /// # Safety + /// At runtime, the optional will be arbitrarily sampled as fast as possible, but due + /// to non-deterministic batching and arrival of inputs, the output stream is + /// non-deterministic. + pub unsafe fn sample_eager(self) -> Stream { let tick = self.location.tick(); - self.latest_tick(&tick).all_ticks() + + unsafe { + // SAFETY: source of intentional non-determinism + self.timestamped(&tick) + .latest_tick() + .all_ticks() + .drop_timestamp() + } } - pub fn sample_every( + /// Given a time interval, returns a stream corresponding to snapshots of the optional + /// value taken at various points in time. Because the input optional may be + /// [`Unbounded`], there are no guarantees on what these snapshots are other than they + /// represent the value of the optional given some prefix of the streams leading up to + /// it. + /// + /// # Safety + /// The output stream is non-deterministic in which elements are sampled, since this + /// is controlled by a clock. + pub unsafe fn sample_every( self, interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a, - ) -> Stream { - let samples = self.location.source_interval(interval); + ) -> Stream + where + L: NoTimestamp, + { + let samples = unsafe { + // SAFETY: source of intentional non-determinism + self.location.source_interval(interval) + }; let tick = self.location.tick(); - self.latest_tick(&tick) - .continue_if(samples.tick_batch(&tick).first()) - .all_ticks() + unsafe { + // SAFETY: source of intentional non-determinism + self.timestamped(&tick) + .latest_tick() + .continue_if(samples.timestamped(&tick).tick_batch().first()) + .all_ticks() + .drop_timestamp() + } } } impl<'a, T, L: Location<'a>> Optional, Bounded> { - pub fn all_ticks(self) -> Stream { + pub fn all_ticks(self) -> Stream, Unbounded> { Stream::new( - self.location.outer().clone(), + Timestamped { + tick: self.location, + }, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn latest(self) -> Optional { + pub fn latest(self) -> Optional, Unbounded> { Optional::new( - self.location.outer().clone(), + Timestamped { + tick: self.location, + }, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } diff --git a/hydroflow_plus/src/rewrites/persist_pullup.rs b/hydroflow_plus/src/rewrites/persist_pullup.rs index 21c2a130eb99..7176dfde8998 100644 --- a/hydroflow_plus/src/rewrites/persist_pullup.rs +++ b/hydroflow_plus/src/rewrites/persist_pullup.rs @@ -168,7 +168,13 @@ mod tests { let process = flow.process::<()>(); let tick = process.tick(); - let before_tee = process.source_iter(q!(0..10)).tick_batch(&tick).persist(); + let before_tee = unsafe { + process + .source_iter(q!(0..10)) + .timestamped(&tick) + .tick_batch() + .persist() + }; before_tee .clone() diff --git a/hydroflow_plus/src/rewrites/properties.rs b/hydroflow_plus/src/rewrites/properties.rs index f09b8a119266..2f0323ab7792 100644 --- a/hydroflow_plus/src/rewrites/properties.rs +++ b/hydroflow_plus/src/rewrites/properties.rs @@ -121,13 +121,16 @@ mod tests { let counter_func = q!(|count: &mut i32, _| *count += 1); let _ = database.add_commutative_tag(counter_func, &tick); - process - .source_iter(q!(vec![])) - .map(q!(|string: String| (string, ()))) - .tick_batch(&tick) - .fold_keyed(q!(|| 0), counter_func) - .all_ticks() - .for_each(q!(|(string, count)| println!("{}: {}", string, count))); + unsafe { + process + .source_iter(q!(vec![])) + .map(q!(|string: String| (string, ()))) + .timestamped(&tick) + .tick_batch() + } + .fold_keyed(q!(|| 0), counter_func) + .all_ticks() + .for_each(q!(|(string, count)| println!("{}: {}", string, count))); let built = flow .optimize_with(|ir| properties_optimize(ir, &database)) diff --git a/hydroflow_plus/src/singleton.rs b/hydroflow_plus/src/singleton.rs index f89957e51dc2..819b3c06dfc7 100644 --- a/hydroflow_plus/src/singleton.rs +++ b/hydroflow_plus/src/singleton.rs @@ -11,6 +11,7 @@ use crate::cycle::{ TickCycleMarker, }; use crate::ir::{HfPlusLeaf, HfPlusNode, TeeNode}; +use crate::location::tick::{NoTimestamp, Timestamped}; use crate::location::{check_matching_location, Location, LocationId, NoTick, Tick}; use crate::{Bounded, Optional, Stream, Unbounded}; @@ -68,7 +69,12 @@ impl<'a, T, L: Location<'a>> CycleCollectionWithInitial<'a, TickCycleMarker> } impl<'a, T, L: Location<'a>> CycleComplete<'a, TickCycleMarker> for Singleton, Bounded> { - fn complete(self, ident: syn::Ident) { + fn complete(self, ident: syn::Ident, expected_location: LocationId) { + assert_eq!( + self.location.id(), + expected_location, + "locations do not match" + ); self.location .flow_state() .borrow_mut() @@ -103,7 +109,12 @@ impl<'a, T, L: Location<'a>> CycleCollection<'a, ForwardRefMarker> impl<'a, T, L: Location<'a>> CycleComplete<'a, ForwardRefMarker> for Singleton, Bounded> { - fn complete(self, ident: syn::Ident) { + fn complete(self, ident: syn::Ident, expected_location: LocationId) { + assert_eq!( + self.location.id(), + expected_location, + "locations do not match" + ); self.location .flow_state() .borrow_mut() @@ -118,6 +129,46 @@ impl<'a, T, L: Location<'a>> CycleComplete<'a, ForwardRefMarker> } } +impl<'a, T, L: Location<'a> + NoTick, B> CycleCollection<'a, ForwardRefMarker> + for Singleton +{ + type Location = L; + + fn create_source(ident: syn::Ident, location: L) -> Self { + let location_id = location.id(); + Singleton::new( + location, + HfPlusNode::Persist(Box::new(HfPlusNode::CycleSource { + ident, + location_kind: location_id, + })), + ) + } +} + +impl<'a, T, L: Location<'a> + NoTick, B> CycleComplete<'a, ForwardRefMarker> + for Singleton +{ + fn complete(self, ident: syn::Ident, expected_location: LocationId) { + assert_eq!( + self.location.id(), + expected_location, + "locations do not match" + ); + self.location + .flow_state() + .borrow_mut() + .leaves + .as_mut() + .expect(FLOW_USED_MESSAGE) + .push(HfPlusLeaf::CycleSink { + ident, + location_kind: self.location_kind(), + input: Box::new(HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner()))), + }); + } +} + impl<'a, T: Clone, L: Location<'a>, B> Clone for Singleton { fn clone(&self) -> Self { if !matches!(self.ir_node.borrow().deref(), HfPlusNode::Tee { .. }) { @@ -143,11 +194,6 @@ impl<'a, T: Clone, L: Location<'a>, B> Clone for Singleton { } impl<'a, T, L: Location<'a>, B> Singleton { - // TODO(shadaj): this is technically incorrect; we should only return the first element of the stream - pub fn into_stream(self) -> Stream { - Stream::new(self.location, self.ir_node.into_inner()) - } - pub fn map U + 'a>(self, f: impl IntoQuotedMut<'a, F, L>) -> Singleton { let f = f.splice_fn1_ctx(&self.location).into(); Singleton::new( @@ -159,7 +205,21 @@ impl<'a, T, L: Location<'a>, B> Singleton { ) } - pub fn flat_map, F: Fn(T) -> I + 'a>( + pub fn flat_map_ordered, F: Fn(T) -> I + 'a>( + self, + f: impl IntoQuotedMut<'a, F, L>, + ) -> Stream { + let f = f.splice_fn1_ctx(&self.location).into(); + Stream::new( + self.location, + HfPlusNode::FlatMap { + f, + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + + pub fn flat_map_unordered, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { @@ -225,55 +285,128 @@ impl<'a, T, L: Location<'a>, B> Singleton { ) } } -} -impl<'a, T, L: Location<'a>> Singleton { - pub fn continue_if(self, signal: Optional) -> Optional { + pub fn continue_if(self, signal: Optional) -> Optional + where + Self: ZipResult< + 'a, + Optional<(), L, Bounded>, + Location = L, + Out = Optional<(T, ()), L, Bounded>, + >, + { self.zip(signal.map(q!(|_u| ()))).map(q!(|(d, _signal)| d)) } - pub fn continue_unless(self, other: Optional) -> Optional { + pub fn continue_unless(self, other: Optional) -> Optional + where + Singleton: ZipResult< + 'a, + Optional<(), L, Bounded>, + Location = L, + Out = Optional<(T, ()), L, Bounded>, + >, + { self.continue_if(other.into_stream().count().filter(q!(|c| *c == 0))) } } -impl<'a, T, L: Location<'a> + NoTick, B> Singleton { - pub fn latest_tick(self, tick: &Tick) -> Singleton, Bounded> { +impl<'a, T, L: Location<'a> + NoTick, B> Singleton, B> { + /// Given a tick, returns a singleton value corresponding to a snapshot of the singleton + /// as of that tick. The snapshot at tick `t + 1` is guaranteed to include at least all + /// relevant data that contributed to the snapshot at tick `t`. + /// + /// # Safety + /// Because this picks a snapshot of a singleton whose value is continuously changing, + /// the output singleton has a non-deterministic value since the snapshot can be at an + /// arbitrary point in time. + pub unsafe fn latest_tick(self) -> Singleton, Bounded> { Singleton::new( - tick.clone(), + self.location.tick, HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } - pub fn tick_samples(self) -> Stream { + pub fn drop_timestamp(self) -> Optional { + Optional::new(self.location.tick.l, self.ir_node.into_inner()) + } +} + +impl<'a, T, L: Location<'a> + NoTick, B> Singleton { + pub fn timestamped(self, tick: &Tick) -> Singleton, B> { + Singleton::new( + Timestamped { tick: tick.clone() }, + self.ir_node.into_inner(), + ) + } + + /// Eagerly samples the singleton as fast as possible, returning a stream of snapshots + /// with order corresponding to increasing prefixes of data contributing to the singleton. + /// + /// # Safety + /// At runtime, the singleton will be arbitrarily sampled as fast as possible, but due + /// to non-deterministic batching and arrival of inputs, the output stream is + /// non-deterministic. + pub unsafe fn sample_eager(self) -> Stream { let tick = self.location.tick(); - self.latest_tick(&tick).all_ticks() + + unsafe { + // SAFETY: source of intentional non-determinism + self.timestamped(&tick) + .latest_tick() + .all_ticks() + .drop_timestamp() + } } - pub fn sample_every( + /// Given a time interval, returns a stream corresponding to snapshots of the singleton + /// value taken at various points in time. Because the input singleton may be + /// [`Unbounded`], there are no guarantees on what these snapshots are other than they + /// represent the value of the singleton given some prefix of the streams leading up to + /// it. + /// + /// # Safety + /// The output stream is non-deterministic in which elements are sampled, since this + /// is controlled by a clock. + pub unsafe fn sample_every( self, interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a, - ) -> Stream { - let samples = self.location.source_interval(interval); + ) -> Stream + where + L: NoTimestamp, + { + let samples = unsafe { + // SAFETY: source of intentional non-determinism + self.location.source_interval(interval) + }; let tick = self.location.tick(); - self.latest_tick(&tick) - .continue_if(samples.tick_batch(&tick).first()) - .all_ticks() + unsafe { + // SAFETY: source of intentional non-determinism + self.timestamped(&tick) + .latest_tick() + .continue_if(samples.timestamped(&tick).tick_batch().first()) + .all_ticks() + .drop_timestamp() + } } } impl<'a, T, L: Location<'a>> Singleton, Bounded> { - pub fn all_ticks(self) -> Stream { + pub fn all_ticks(self) -> Stream, Unbounded> { Stream::new( - self.location.outer().clone(), + Timestamped { + tick: self.location, + }, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } - pub fn latest(self) -> Singleton { + pub fn latest(self) -> Singleton, Unbounded> { Singleton::new( - self.location.outer().clone(), + Timestamped { + tick: self.location, + }, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } @@ -298,6 +431,10 @@ impl<'a, T, L: Location<'a>> Singleton, Bounded> { HfPlusNode::Delta(Box::new(self.ir_node.into_inner())), ) } + + pub fn into_stream(self) -> Stream, Bounded> { + Stream::new(self.location, self.ir_node.into_inner()) + } } pub trait ZipResult<'a, Other> { @@ -310,36 +447,78 @@ pub trait ZipResult<'a, Other> { fn make(location: Self::Location, ir_node: HfPlusNode) -> Self::Out; } -impl<'a, T, U: Clone, L: Location<'a>, B> ZipResult<'a, Singleton> for Singleton { - type Out = Singleton<(T, U), L, B>; - type Location = L; +impl<'a, T, U: Clone, L: Location<'a>, B> ZipResult<'a, Singleton, B>> + for Singleton, B> +{ + type Out = Singleton<(T, U), Timestamped, B>; + type Location = Timestamped; - fn other_location(other: &Singleton) -> L { + fn other_location(other: &Singleton, B>) -> Timestamped { other.location.clone() } - fn other_ir_node(other: Singleton) -> HfPlusNode { + fn other_ir_node(other: Singleton, B>) -> HfPlusNode { other.ir_node.into_inner() } - fn make(location: L, ir_node: HfPlusNode) -> Self::Out { + fn make(location: Timestamped, ir_node: HfPlusNode) -> Self::Out { Singleton::new(location, ir_node) } } -impl<'a, T, U: Clone, L: Location<'a>, B> ZipResult<'a, Optional> for Singleton { - type Out = Optional<(T, U), L, B>; - type Location = L; +impl<'a, T, U: Clone, L: Location<'a>, B> ZipResult<'a, Optional, B>> + for Singleton, B> +{ + type Out = Optional<(T, U), Timestamped, B>; + type Location = Timestamped; + + fn other_location(other: &Optional, B>) -> Timestamped { + other.location.clone() + } + + fn other_ir_node(other: Optional, B>) -> HfPlusNode { + other.ir_node.into_inner() + } + + fn make(location: Timestamped, ir_node: HfPlusNode) -> Self::Out { + Optional::new(location, ir_node) + } +} + +impl<'a, T, U: Clone, L: Location<'a>, B> ZipResult<'a, Singleton, B>> + for Singleton, B> +{ + type Out = Singleton<(T, U), Tick, B>; + type Location = Tick; + + fn other_location(other: &Singleton, B>) -> Tick { + other.location.clone() + } + + fn other_ir_node(other: Singleton, B>) -> HfPlusNode { + other.ir_node.into_inner() + } + + fn make(location: Tick, ir_node: HfPlusNode) -> Self::Out { + Singleton::new(location, ir_node) + } +} + +impl<'a, T, U: Clone, L: Location<'a>, B> ZipResult<'a, Optional, B>> + for Singleton, B> +{ + type Out = Optional<(T, U), Tick, B>; + type Location = Tick; - fn other_location(other: &Optional) -> L { + fn other_location(other: &Optional, B>) -> Tick { other.location.clone() } - fn other_ir_node(other: Optional) -> HfPlusNode { + fn other_ir_node(other: Optional, B>) -> HfPlusNode { other.ir_node.into_inner() } - fn make(location: L, ir_node: HfPlusNode) -> Self::Out { + fn make(location: Tick, ir_node: HfPlusNode) -> Self::Out { Optional::new(location, ir_node) } } diff --git a/hydroflow_plus/src/stream.rs b/hydroflow_plus/src/stream.rs index 2da21bf8e47f..06db898941a1 100644 --- a/hydroflow_plus/src/stream.rs +++ b/hydroflow_plus/src/stream.rs @@ -11,12 +11,14 @@ use serde::de::DeserializeOwned; use serde::Serialize; use stageleft::{q, IntoQuotedMut, QuotedWithContext}; use syn::parse_quote; +use tokio::time::Instant; use crate::builder::FLOW_USED_MESSAGE; use crate::cycle::{CycleCollection, CycleComplete, DeferTick, ForwardRefMarker, TickCycleMarker}; use crate::ir::{DebugInstantiate, HfPlusLeaf, HfPlusNode, TeeNode}; use crate::location::cluster::CLUSTER_SELF_ID; use crate::location::external_process::{ExternalBincodeStream, ExternalBytesPort}; +use crate::location::tick::{NoTimestamp, Timestamped}; use crate::location::{ check_matching_location, CanSend, ExternalProcess, Location, LocationId, NoTick, Tick, }; @@ -115,7 +117,12 @@ impl<'a, T, L: Location<'a>, Order> CycleCollection<'a, TickCycleMarker> impl<'a, T, L: Location<'a>, Order> CycleComplete<'a, TickCycleMarker> for Stream, Bounded, Order> { - fn complete(self, ident: syn::Ident) { + fn complete(self, ident: syn::Ident, expected_location: LocationId) { + assert_eq!( + self.location.id(), + expected_location, + "locations do not match" + ); self.location .flow_state() .borrow_mut() @@ -150,7 +157,12 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> CycleCollection<'a, ForwardRefMa impl<'a, T, L: Location<'a> + NoTick, B, Order> CycleComplete<'a, ForwardRefMarker> for Stream { - fn complete(self, ident: syn::Ident) { + fn complete(self, ident: syn::Ident, expected_location: LocationId) { + assert_eq!( + self.location.id(), + expected_location, + "locations do not match" + ); self.location .flow_state() .borrow_mut() @@ -221,7 +233,7 @@ impl<'a, T, L: Location<'a>, B, Order> Stream { self.map(q!(|d| d.clone())) } - pub fn flat_map, F: Fn(T) -> I + 'a>( + pub fn flat_map_ordered, F: Fn(T) -> I + 'a>( self, f: impl IntoQuotedMut<'a, F, L>, ) -> Stream { @@ -235,11 +247,32 @@ impl<'a, T, L: Location<'a>, B, Order> Stream { ) } - pub fn flatten(self) -> Stream + pub fn flat_map_unordered, F: Fn(T) -> I + 'a>( + self, + f: impl IntoQuotedMut<'a, F, L>, + ) -> Stream { + let f = f.splice_fn1_ctx(&self.location).into(); + Stream::new( + self.location, + HfPlusNode::FlatMap { + f, + input: Box::new(self.ir_node.into_inner()), + }, + ) + } + + pub fn flatten_ordered(self) -> Stream + where + T: IntoIterator, + { + self.flat_map_ordered(q!(|d| d)) + } + + pub fn flatten_unordered(self) -> Stream where T: IntoIterator, { - self.flat_map(q!(|d| d)) + self.flat_map_unordered(q!(|d| d)) } pub fn filter bool + 'a>( @@ -366,7 +399,15 @@ impl<'a, T, L: Location<'a>, B, Order> Stream { } } - pub fn assume_ordering(self) -> Stream { + /// Explicitly "casts" the stream to a type with a different ordering + /// guarantee. Useful in unsafe code where the ordering cannot be proven + /// by the type-system. + /// + /// # Safety + /// This function is used as an escape hatch, and any mistakes in the + /// provided ordering guarantee will propogate into the guarantees + /// for the rest of the program. + pub unsafe fn assume_ordering(self) -> Stream { Stream::new(self.location, self.ir_node.into_inner()) } } @@ -558,15 +599,21 @@ impl<'a, T, L: Location<'a>> Stream { } } -impl<'a, T, L: Location<'a> + NoTick> Stream { +impl<'a, T, L: Location<'a> + NoTick + NoTimestamp> Stream { pub fn union( self, other: Stream, ) -> Stream { let tick = self.location.tick(); - self.tick_batch(&tick) - .union(other.tick_batch(&tick)) - .all_ticks() + unsafe { + // SAFETY: Because the inputs and outputs are unordered, + // we can interleave batches from both streams. + self.timestamped(&tick) + .tick_batch() + .union(other.timestamped(&tick).tick_batch()) + .all_ticks() + .drop_timestamp() + } } } @@ -704,32 +751,112 @@ impl<'a, K: Eq + Hash, V, L: Location<'a>, Order> Stream<(K, V), Tick, Bounde } } -impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { - pub fn tick_batch(self, tick: &Tick) -> Stream, Bounded, Order> { +impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream, B, Order> { + /// Given a tick, returns a stream corresponding to a batch of elements for that tick. + /// These batches are guaranteed to be contiguous across ticks and preserve the order + /// of the input. + /// + /// # Safety + /// The batch boundaries are non-deterministic and may change across executions. + pub unsafe fn tick_batch(self) -> Stream, Bounded, Order> { Stream::new( - tick.clone(), + self.location.tick, HfPlusNode::Unpersist(Box::new(self.ir_node.into_inner())), ) } - pub fn tick_prefix(self, tick: &Tick) -> Stream, Bounded, Order> - where - T: Clone, - { - self.tick_batch(tick).persist() + pub fn drop_timestamp(self) -> Stream { + Stream::new(self.location.tick.l, self.ir_node.into_inner()) } - pub fn sample_every( + pub fn timestamp_source(&self) -> Tick { + self.location.tick.clone() + } +} + +impl<'a, T, L: Location<'a> + NoTick + NoTimestamp, B, Order> Stream { + pub fn timestamped(self, tick: &Tick) -> Stream, B, Order> { + Stream::new( + Timestamped { tick: tick.clone() }, + self.ir_node.into_inner(), + ) + } + + /// Given a time interval, returns a stream corresponding to samples taken from the + /// stream roughly at that interval. The output will have elements in the same order + /// as the input, but with arbitrary elements skipped between samples. There is also + /// no guarantee on the exact timing of the samples. + /// + /// # Safety + /// The output stream is non-deterministic in which elements are sampled, since this + /// is controlled by a clock. + pub unsafe fn sample_every( self, interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a, ) -> Stream { - let samples = self.location.source_interval(interval); + let samples = unsafe { + // SAFETY: source of intentional non-determinism + self.location.source_interval(interval) + }; + let tick = self.location.tick(); - self.tick_batch(&tick) - .continue_if(samples.tick_batch(&tick).first()) - .all_ticks() + unsafe { + // SAFETY: source of intentional non-determinism + self.timestamped(&tick) + .tick_batch() + .continue_if(samples.timestamped(&tick).tick_batch().first()) + .all_ticks() + .drop_timestamp() + } } + /// Given a timeout duration, returns an [`Optional`] which will have a value if the + /// stream has not emitted a value since that duration. + /// + /// # Safety + /// Timeout relies on non-deterministic sampling of the stream, so depending on when + /// samples take place, timeouts may be non-deterministically generated or missed, + /// and the notification of the timeout may be delayed as well. There is also no + /// guarantee on how long the [`Optional`] will have a value after the timeout is + /// detected based on when the next sample is taken. + pub unsafe fn timeout( + self, + duration: impl QuotedWithContext<'a, std::time::Duration, Tick> + Copy + 'a, + ) -> Optional<(), L, Unbounded> + where + Order: MinOrder, + { + let tick = self.location.tick(); + + let latest_received = self.fold_commutative( + q!(|| None), + q!(|latest, _| { + // Note: May want to check received ballot against our own? + *latest = Some(Instant::now()); + }), + ); + + unsafe { + // SAFETY: Non-deterministic delay in detecting a timeout is expected. + latest_received.timestamped(&tick).latest_tick() + } + .filter_map(q!(move |latest_received| { + if let Some(latest_received) = latest_received { + if Instant::now().duration_since(latest_received) > duration { + Some(()) + } else { + None + } + } else { + Some(()) + } + })) + .latest() + .drop_timestamp() + } +} + +impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { pub fn for_each(self, f: impl IntoQuotedMut<'a, F, L>) { let f = f.splice_fn1_ctx(&self.location).into(); self.location @@ -762,9 +889,11 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { } impl<'a, T, L: Location<'a>, Order> Stream, Bounded, Order> { - pub fn all_ticks(self) -> Stream { + pub fn all_ticks(self) -> Stream, Unbounded, Order> { Stream::new( - self.location.outer().clone(), + Timestamped { + tick: self.location.clone(), + }, HfPlusNode::Persist(Box::new(self.ir_node.into_inner())), ) } @@ -848,12 +977,17 @@ impl<'a, T, C1, B, Order> Stream, B, Order> { Order: MinOrder< as CanSend<'a, Cluster<'a, C2>>>::OutStrongestOrder>, { - self.map(q!(move |b| ( - ClusterId::from_raw(CLUSTER_SELF_ID.raw_id), - b.clone() - ))) - .send_bincode_interleaved(other) - .assume_ordering() // this is safe because we are mapping clusters 1:1 + let sent = self + .map(q!(move |b| ( + ClusterId::from_raw(CLUSTER_SELF_ID.raw_id), + b.clone() + ))) + .send_bincode_interleaved(other); + + unsafe { + // SAFETY: this is safe because we are mapping clusters 1:1 + sent.assume_ordering() + } } } @@ -863,9 +997,12 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { other: &Process<'a, P2>, ) -> Stream, Unbounded, Order> where - L: CanSend<'a, Process<'a, P2>, In = T, Out = T>, + L::Root: CanSend<'a, Process<'a, P2>, In = T, Out = T>, T: Clone + Serialize + DeserializeOwned, - Order: MinOrder, Min = Order>, + Order: MinOrder< + >>::OutStrongestOrder, + Min = Order, + >, { self.send_bincode::, T>(other) } @@ -873,20 +1010,20 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { pub fn send_bincode, CoreType>( self, other: &L2, - ) -> Stream, L2, Unbounded, Order::Min> + ) -> Stream<>::Out, L2, Unbounded, Order::Min> where - L: CanSend<'a, L2, In = T>, + L::Root: CanSend<'a, L2, In = T>, CoreType: Serialize + DeserializeOwned, - Order: MinOrder>, + Order: MinOrder<>::OutStrongestOrder>, { - let serialize_pipeline = Some(serialize_bincode::(L::is_demux())); + let serialize_pipeline = Some(serialize_bincode::(L::Root::is_demux())); - let deserialize_pipeline = Some(deserialize_bincode::(L::tagged_type())); + let deserialize_pipeline = Some(deserialize_bincode::(L::Root::tagged_type())); Stream::new( other.clone(), HfPlusNode::Network { - from_location: self.location_kind(), + from_location: self.location.root().id(), from_key: None, to_location: other.id(), to_key: None, @@ -921,7 +1058,7 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { leaves.push(HfPlusLeaf::ForEach { f: dummy_f.into(), input: Box::new(HfPlusNode::Network { - from_location: self.location_kind(), + from_location: self.location.root().id(), from_key: None, to_location: other.id(), to_key: Some(external_key), @@ -942,22 +1079,22 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { pub fn send_bytes>( self, other: &L2, - ) -> Stream, L2, Unbounded, Order::Min> + ) -> Stream<>::Out, L2, Unbounded, Order::Min> where - L: CanSend<'a, L2, In = T>, - Order: MinOrder>, + L::Root: CanSend<'a, L2, In = T>, + Order: MinOrder<>::OutStrongestOrder>, { let root = get_this_crate(); Stream::new( other.clone(), HfPlusNode::Network { - from_location: self.location_kind(), + from_location: self.location.root().id(), from_key: None, to_location: other.id(), to_key: None, serialize_pipeline: None, instantiate_fn: DebugInstantiate::Building(), - deserialize_pipeline: if let Some(c_type) = L::tagged_type() { + deserialize_pipeline: if let Some(c_type) = L::Root::tagged_type() { Some( parse_quote!(map(|(id, b)| (#root::ClusterId<#c_type>::from_raw(id), b.unwrap().freeze()))), ) @@ -971,7 +1108,7 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { pub fn send_bytes_external(self, other: &ExternalProcess) -> ExternalBytesPort where - L: CanSend<'a, ExternalProcess<'a, L2>, In = T, Out = Bytes>, + L::Root: CanSend<'a, ExternalProcess<'a, L2>, In = T, Out = Bytes>, { let mut flow_state_borrow = self.location.flow_state().borrow_mut(); let external_key = flow_state_borrow.next_external_out; @@ -984,7 +1121,7 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { leaves.push(HfPlusLeaf::ForEach { f: dummy_f.into(), input: Box::new(HfPlusNode::Network { - from_location: self.location_kind(), + from_location: self.location.root().id(), from_key: None, to_location: other.id(), to_key: Some(external_key), @@ -1006,9 +1143,9 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { other: &L2, ) -> Stream where - L: CanSend<'a, L2, In = T, Out = (Tag, CoreType)>, + L::Root: CanSend<'a, L2, In = T, Out = (Tag, CoreType)>, CoreType: Serialize + DeserializeOwned, - Order: MinOrder>, + Order: MinOrder<>::OutStrongestOrder>, { self.send_bincode::(other).map(q!(|(_, b)| b)) } @@ -1018,24 +1155,30 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { other: &L2, ) -> Stream where - L: CanSend<'a, L2, In = T, Out = (Tag, Bytes)>, - Order: MinOrder>, + L::Root: CanSend<'a, L2, In = T, Out = (Tag, Bytes)>, + Order: MinOrder<>::OutStrongestOrder>, { self.send_bytes::(other).map(q!(|(_, b)| b)) } + #[expect(clippy::type_complexity, reason = "ordering semantics for broadcast")] pub fn broadcast_bincode( self, other: &Cluster<'a, C2>, - ) -> Stream, Cluster<'a, C2>, Unbounded, Order::Min> + ) -> Stream< + >>::Out, + Cluster<'a, C2>, + Unbounded, + Order::Min, + > where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, + L::Root: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, T: Clone + Serialize + DeserializeOwned, - Order: MinOrder>, + Order: MinOrder<>>::OutStrongestOrder>, { let ids = other.members(); - self.flat_map(q!(|b| ids.iter().map(move |id| ( + self.flat_map_ordered(q!(|b| ids.iter().map(move |id| ( ::std::clone::Clone::clone(id), ::std::clone::Clone::clone(&b) )))) @@ -1047,25 +1190,31 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { other: &Cluster<'a, C2>, ) -> Stream, Unbounded, Order::Min> where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, + L::Root: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, T: Clone + Serialize + DeserializeOwned, - Order: MinOrder>, + Order: MinOrder<>>::OutStrongestOrder>, { self.broadcast_bincode(other).map(q!(|(_, b)| b)) } + #[expect(clippy::type_complexity, reason = "ordering semantics for broadcast")] pub fn broadcast_bytes( self, other: &Cluster<'a, C2>, - ) -> Stream, Cluster<'a, C2>, Unbounded, Order::Min> + ) -> Stream< + >>::Out, + Cluster<'a, C2>, + Unbounded, + Order::Min, + > where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, + L::Root: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, T: Clone, - Order: MinOrder>, + Order: MinOrder<>>::OutStrongestOrder>, { let ids = other.members(); - self.flat_map(q!(|b| ids.iter().map(move |id| ( + self.flat_map_ordered(q!(|b| ids.iter().map(move |id| ( ::std::clone::Clone::clone(id), ::std::clone::Clone::clone(&b) )))) @@ -1077,10 +1226,10 @@ impl<'a, T, L: Location<'a> + NoTick, B, Order> Stream { other: &Cluster<'a, C2>, ) -> Stream, Unbounded, Order::Min> where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + L::Root: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + 'a, T: Clone, - Order: MinOrder>, + Order: MinOrder<>>::OutStrongestOrder>, { self.broadcast_bytes(other).map(q!(|(_, b)| b)) } @@ -1092,15 +1241,18 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { self, other: &Cluster<'a, C2>, ) -> Stream< - L::Out, + >>::Out, Cluster<'a, C2>, Unbounded, - >>::Min, + >>::OutStrongestOrder, + >>::Min, > where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, + L::Root: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)>, T: Clone + Serialize + DeserializeOwned, - TotalOrder: MinOrder>, + TotalOrder: + MinOrder<>>::OutStrongestOrder>, { let ids = other.members(); @@ -1116,12 +1268,15 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { T, Cluster<'a, C2>, Unbounded, - >>::Min, + >>::OutStrongestOrder, + >>::Min, > where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, + L::Root: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, T)> + 'a, T: Clone + Serialize + DeserializeOwned, - TotalOrder: MinOrder>, + TotalOrder: + MinOrder<>>::OutStrongestOrder>, { self.round_robin_bincode(other).map(q!(|(_, b)| b)) } @@ -1130,15 +1285,18 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { self, other: &Cluster<'a, C2>, ) -> Stream< - L::Out, + >>::Out, Cluster<'a, C2>, Unbounded, - >>::Min, + >>::OutStrongestOrder, + >>::Min, > where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, + L::Root: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T)> + 'a, T: Clone, - TotalOrder: MinOrder>, + TotalOrder: + MinOrder<>>::OutStrongestOrder>, { let ids = other.members(); @@ -1154,13 +1312,16 @@ impl<'a, T, L: Location<'a> + NoTick, B> Stream { Bytes, Cluster<'a, C2>, Unbounded, - >>::Min, + >>::OutStrongestOrder, + >>::Min, > where - L: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + L::Root: CanSend<'a, Cluster<'a, C2>, In = (ClusterId, T), Out = (Tag, Bytes)> + 'a, T: Clone, - TotalOrder: MinOrder>, + TotalOrder: + MinOrder<>>::OutStrongestOrder>, { self.round_robin_bytes(other).map(q!(|(_, b)| b)) } diff --git a/hydroflow_plus_test/src/cluster/compute_pi.rs b/hydroflow_plus_test/src/cluster/compute_pi.rs index 00bf90b5348a..0eff334e7160 100644 --- a/hydroflow_plus_test/src/cluster/compute_pi.rs +++ b/hydroflow_plus_test/src/cluster/compute_pi.rs @@ -29,21 +29,25 @@ pub fn compute_pi<'a>( ) .all_ticks(); - trials + let estimate = trials .send_bincode_interleaved(&process) .reduce_commutative(q!(|(inside, total), (inside_batch, total_batch)| { *inside += inside_batch; *total += total_batch; - })) - .sample_every(q!(Duration::from_secs(1))) - .for_each(q!(|(inside, total)| { - println!( - "pi: {} ({} trials)", - 4.0 * inside as f64 / total as f64, - total - ); })); + unsafe { + // SAFETY: intentional non-determinism + estimate.sample_every(q!(Duration::from_secs(1))) + } + .for_each(q!(|(inside, total)| { + println!( + "pi: {} ({} trials)", + 4.0 * inside as f64 / total as f64, + total + ); + })); + (cluster, process) } diff --git a/hydroflow_plus_test/src/cluster/map_reduce.rs b/hydroflow_plus_test/src/cluster/map_reduce.rs index a46e1d0d401a..42d72a221881 100644 --- a/hydroflow_plus_test/src/cluster/map_reduce.rs +++ b/hydroflow_plus_test/src/cluster/map_reduce.rs @@ -11,22 +11,32 @@ pub fn map_reduce<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, Leader>, Cluster<' .source_iter(q!(vec!["abc", "abc", "xyz", "abc"])) .map(q!(|s| s.to_string())); - words + let partitioned_words = words .round_robin_bincode(&cluster) - .map(q!(|string| (string, ()))) - .tick_batch(&cluster.tick()) - .fold_keyed(q!(|| 0), q!(|count, _| *count += 1)) - .inspect(q!(|(string, count)| println!( - "partition count: {} - {}", - string, count - ))) - .all_ticks() - .send_bincode_interleaved(&process) - .tick_batch(&process.tick()) - .persist() - .reduce_keyed_commutative(q!(|total, count| *total += count)) - .all_ticks() - .for_each(q!(|(string, count)| println!("{}: {}", string, count))); + .map(q!(|string| (string, ()))); + + let batches = unsafe { + // SAFETY: addition is associative so we can batch reduce + partitioned_words.timestamped(&cluster.tick()).tick_batch() + } + .fold_keyed(q!(|| 0), q!(|count, _| *count += 1)) + .inspect(q!(|(string, count)| println!( + "partition count: {} - {}", + string, count + ))) + .all_ticks() + .send_bincode_interleaved(&process); + + unsafe { + // SAFETY: addition is associative so we can batch reduce + batches + .timestamped(&process.tick()) + .tick_batch() + .persist() + .reduce_keyed_commutative(q!(|total, count| *total += count)) + } + .all_ticks() + .for_each(q!(|(string, count)| println!("{}: {}", string, count))); (process, cluster) } diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index 875d3626dd7f..e06628c28841 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -4,12 +4,12 @@ use std::hash::Hash; use std::time::Duration; use hydroflow_plus::*; +use location::tick::Timestamped; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use stream::NoOrder; -use tokio::time::Instant; -use super::quorum::collect_quorum; +use super::quorum::{collect_quorum, collect_quorum_with_response}; use super::request_response::join_responses; pub struct Proposer {} @@ -51,12 +51,28 @@ struct P2a

    { value: Option

    , // might be a re-committed hole } +/// Implements the core Paxos algorithm, which uses a cluster of propsers and acceptors +/// to sequence payloads being sent to the proposers. +/// +/// Proposers that currently are the leader will work with acceptors to sequence incoming +/// payloads, but may drop payloads if they are not the lader or lose leadership during +/// the commit process. +/// +/// Returns a stream of ballots, where new values are emitted when a new leader is elected, +/// and a stream of sequenced payloads with an index and optional payload (in the case of +/// holes in the log). +/// +/// # Safety +/// When the leader is stable, the algorithm will commit incoming payloads to the leader +/// in deterministic order. However, when the leader is changing, payloads may be +/// non-deterministically dropped. The stream of ballots is also non-deterministic because +/// leaders are elected in a non-deterministic process. #[expect( clippy::too_many_arguments, clippy::type_complexity, reason = "internal paxos code // TODO" )] -pub fn paxos_core<'a, P: PaxosPayload, R>( +pub unsafe fn paxos_core<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, r_to_acceptors_checkpoint: Stream< @@ -90,43 +106,64 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( let (a_log_complete_cycle, a_log_forward_reference) = acceptor_tick.forward_ref::>(); - let (p_ballot, p_is_leader, p_relevant_p1bs, a_max_ballot) = leader_election( - proposers, - acceptors, - &proposer_tick, - &acceptor_tick, - f, - i_am_leader_send_timeout, - i_am_leader_check_timeout, - i_am_leader_check_timeout_delay_multiplier, - sequencing_max_ballot_forward_reference, - a_log_forward_reference, - ); + let (p_ballot, p_is_leader, p_relevant_p1bs, a_max_ballot) = unsafe { + // SAFETY: The primary non-determinism exposed by leader election algorithm lies in which leader + // is elected, which affects both the ballot at each proposer and the leader flag. But using a stale ballot + // or leader flag will only lead to failure in sequencing rather than commiting the wrong value. Because + // ballots are non-deterministic, the acceptor max ballot is also non-deterministic, although we are + // guaranteed that the max ballot will match the current ballot of a proposer who believes they are the leader. + leader_election( + proposers, + acceptors, + &proposer_tick, + &acceptor_tick, + f, + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + sequencing_max_ballot_forward_reference, + a_log_forward_reference, + ) + }; let just_became_leader = p_is_leader .clone() .continue_unless(p_is_leader.clone().defer_tick()); - let (p_to_replicas, a_log, sequencing_max_ballots) = sequence_payload( - proposers, - acceptors, - &proposer_tick, - &acceptor_tick, - c_to_proposers, - r_to_acceptors_checkpoint, - p_ballot.clone(), - p_is_leader, - p_relevant_p1bs, - f, - a_max_ballot, - ); - - a_log_complete_cycle.complete(a_log); + let (p_to_replicas, a_log, sequencing_max_ballots) = unsafe { + // SAFETY: The relevant p1bs are non-deterministic because they come from a arbitrary quorum, but because + // we use a quorum, if we remain the leader there are no missing committed values when we combine the logs. + // The remaining non-determinism is in when incoming payloads are batched versus the leader flag and state + // of acceptors, which in the worst case will lead to dropped payloads as documented. + sequence_payload( + proposers, + acceptors, + &proposer_tick, + &acceptor_tick, + c_to_proposers, + r_to_acceptors_checkpoint, + p_ballot.clone(), + p_is_leader, + p_relevant_p1bs, + f, + a_max_ballot, + ) + }; + + a_log_complete_cycle.complete(unsafe { + // SAFETY: We will always write payloads to the log before acknowledging them to the proposers, + // which guarantees that if the leader changes the quorum overlap between sequencing and leader + // election will include the committed value. + a_log.latest_tick() + }); sequencing_max_ballot_complete_cycle.complete(sequencing_max_ballots); ( // Only tell the clients once when leader election concludes - just_became_leader.then(p_ballot).all_ticks(), + just_became_leader + .then(p_ballot) + .all_ticks() + .drop_timestamp(), p_to_replicas, ) } @@ -136,7 +173,7 @@ pub fn paxos_core<'a, P: PaxosPayload, R>( clippy::too_many_arguments, reason = "internal paxos code // TODO" )] -fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( +unsafe fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, proposer_tick: &Tick>, @@ -172,20 +209,27 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( proposer_id: ClusterId::from_raw(0) }))); - let (p_ballot, p_has_largest_ballot) = p_ballot_calc( - proposer_tick, - p_received_max_ballot.latest_tick(proposer_tick), - ); - - let (p_to_proposers_i_am_leader, p_trigger_election) = p_leader_heartbeat( - proposers, - proposer_tick, - p_is_leader_forward_ref, - p_ballot.clone(), - i_am_leader_send_timeout, - i_am_leader_check_timeout, - i_am_leader_check_timeout_delay_multiplier, - ); + let (p_ballot, p_has_largest_ballot) = p_ballot_calc(proposer_tick, unsafe { + // SAFETY: A stale max ballot might result in us failing to become the leader, but which proposer + // becomes the leader is non-deterministic anyway. + p_received_max_ballot + .timestamped(proposer_tick) + .latest_tick() + }); + + let (p_to_proposers_i_am_leader, p_trigger_election) = unsafe { + // SAFETY: non-determinism in heartbeats may lead to additional leader election attempts, which + // is propagated to the non-determinism of which leader is elected. + p_leader_heartbeat( + proposers, + proposer_tick, + p_is_leader_forward_ref, + p_ballot.clone(), + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + ) + }; p_to_proposers_i_am_leader_complete_cycle.complete(p_to_proposers_i_am_leader); @@ -195,8 +239,17 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( .inspect(q!(|_| println!("Proposer leader expired, sending P1a"))) .broadcast_bincode_interleaved(acceptors); - let (a_max_ballot, a_to_proposers_p1b) = - acceptor_p1(acceptor_tick, p_to_acceptors_p1a, a_log, proposers); + let (a_max_ballot, a_to_proposers_p1b) = acceptor_p1( + acceptor_tick, + unsafe { + // SAFETY: Non-deterministic batching may result in different payloads being rejected + // by an acceptor if the payload is batched with another payload with larger ballot. + // But as documented, payloads may be non-deterministically dropped during leader election. + p_to_acceptors_p1a.timestamped(acceptor_tick).tick_batch() + }, + a_log, + proposers, + ); let (p_is_leader, p_accepted_values, fail_ballots) = p_p1b( proposer_tick, @@ -206,7 +259,7 @@ fn leader_election<'a, L: Clone + Debug + Serialize + DeserializeOwned>( f, ); p_is_leader_complete_cycle.complete(p_is_leader.clone()); - p1b_fail_complete.complete(fail_ballots.all_ticks()); + p1b_fail_complete.complete(fail_ballots.drop_timestamp()); (p_ballot, p_is_leader, p_accepted_values, a_max_ballot) } @@ -257,35 +310,8 @@ fn p_ballot_calc<'a>( (p_ballot, p_has_largest_ballot) } -fn p_leader_expired<'a>( - proposer_tick: &Tick>, - p_to_proposers_i_am_leader: Stream, Unbounded, NoOrder>, - p_is_leader: Optional<(), Tick>, Bounded>, - i_am_leader_check_timeout: u64, // How often to check if heartbeat expired -) -> Optional, Tick>, Bounded> { - let p_latest_received_i_am_leader = p_to_proposers_i_am_leader.clone().fold_commutative( - q!(|| None), - q!(|latest, _| { - // Note: May want to check received ballot against our own? - *latest = Some(Instant::now()); - }), - ); - - p_latest_received_i_am_leader - .latest_tick(proposer_tick) - .continue_unless(p_is_leader) - .filter(q!(move |latest_received_i_am_leader| { - if let Some(latest_received_i_am_leader) = latest_received_i_am_leader { - (Instant::now().duration_since(*latest_received_i_am_leader)) - > Duration::from_secs(i_am_leader_check_timeout) - } else { - true - } - })) -} - #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -fn p_leader_heartbeat<'a>( +unsafe fn p_leader_heartbeat<'a>( proposers: &Cluster<'a, Proposer>, proposer_tick: &Tick>, p_is_leader: Optional<(), Tick>, Bounded>, @@ -295,49 +321,66 @@ fn p_leader_heartbeat<'a>( i_am_leader_check_timeout_delay_multiplier: usize, /* Initial delay, multiplied by proposer pid, to stagger proposers checking for timeouts */ ) -> ( Stream, Unbounded, NoOrder>, - Optional, Tick>, Bounded>, + Optional<(), Tick>, Bounded>, ) { - let p_to_proposers_i_am_leader = p_is_leader - .clone() - .then(p_ballot) - .latest() - .sample_every(q!(Duration::from_secs(i_am_leader_send_timeout))) - .broadcast_bincode_interleaved(proposers); + let p_to_proposers_i_am_leader = unsafe { + // SAFETY: Delays in heartbeats may lead to leader election attempts even + // if the leader is alive. This will result in the previous leader receiving + // larger ballots from its peers and it will drop its leadership. + p_is_leader + .clone() + .then(p_ballot) + .latest() + .drop_timestamp() + .sample_every(q!(Duration::from_secs(i_am_leader_send_timeout))) + } + .broadcast_bincode_interleaved(proposers); - let p_leader_expired = p_leader_expired( - proposer_tick, - p_to_proposers_i_am_leader.clone(), - p_is_leader, - i_am_leader_check_timeout, - ); + let p_leader_expired = unsafe { + // Delayed timeouts only affect which leader wins re-election. If the leadership flag + // is gained after timeout correctly ignore the timeout. If the flag is lost after + // timeout we correctly attempt to become the leader. + p_to_proposers_i_am_leader + .clone() + .timeout(q!(Duration::from_secs(i_am_leader_check_timeout))) + .timestamped(proposer_tick) + .latest_tick() + .continue_unless(p_is_leader) + }; // Add random delay depending on node ID so not everyone sends p1a at the same time - let p_trigger_election = p_leader_expired.continue_if( - proposers - .source_interval_delayed( - q!(Duration::from_secs( - (CLUSTER_SELF_ID.raw_id * i_am_leader_check_timeout_delay_multiplier as u32) - .into() - )), - q!(Duration::from_secs(i_am_leader_check_timeout)), - ) - .tick_batch(proposer_tick) - .first(), - ); + let p_trigger_election = unsafe { + // SAFETY: If the leader "un-expires" due to non-determinstic delay, we return + // to a stable leader state. If the leader remains expired, non-deterministic + // delay is propagated to the non-determinism of which leader is elected. + p_leader_expired.continue_if( + proposers + .source_interval_delayed( + q!(Duration::from_secs( + (CLUSTER_SELF_ID.raw_id + * i_am_leader_check_timeout_delay_multiplier as u32) + .into() + )), + q!(Duration::from_secs(i_am_leader_check_timeout)), + ) + .timestamped(proposer_tick) + .tick_batch() + .first(), + ) + }; (p_to_proposers_i_am_leader, p_trigger_election) } #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] fn acceptor_p1<'a, L: Serialize + DeserializeOwned + Clone>( acceptor_tick: &Tick>, - p_to_acceptors_p1a: Stream, Unbounded, NoOrder>, + p_to_acceptors_p1a: Stream>, Bounded, NoOrder>, a_log: Singleton>, Bounded>, proposers: &Cluster<'a, Proposer>, ) -> ( Singleton>, Bounded>, Stream<(Ballot, Result), Cluster<'a, Proposer>, Unbounded, NoOrder>, ) { - let p_to_acceptors_p1a = p_to_acceptors_p1a.tick_batch(acceptor_tick); let a_max_ballot = p_to_acceptors_p1a .clone() .inspect(q!(|p1a| println!("Acceptor received P1a: {:?}", p1a))) @@ -385,32 +428,36 @@ fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( ) -> ( Optional<(), Tick>, Bounded>, Stream>, Bounded, NoOrder>, - Stream>, Bounded, NoOrder>, + Stream>, Unbounded, NoOrder>, ) { - let (quorums, fails) = collect_quorum( - proposer_tick, - a_to_proposers_p1b.tick_batch(proposer_tick), + let (quorums, fails) = collect_quorum_with_response( + a_to_proposers_p1b.timestamped(proposer_tick), f + 1, 2 * f + 1, ); - let p_received_quorum_of_p1bs = quorums - .persist() - .fold_keyed_commutative( - q!(|| vec![]), - q!(|logs, log| { - logs.push(log); - }), - ) - .max_by_key(q!(|t| t.0)) - .zip(p_ballot.clone()) - .filter_map(q!( - move |((quorum_ballot, quorum_accepted), my_ballot)| if quorum_ballot == my_ballot { - Some(quorum_accepted) - } else { - None - } - )); + let p_received_quorum_of_p1bs = unsafe { + // SAFETY: All the values for a quorum will be emitted in a single batch, + // so we will not split up the quorum. + quorums.tick_batch() + } + .persist() + .fold_keyed_commutative( + q!(|| vec![]), + q!(|logs, log| { + // even though this is non-commutative, we use `flatten_unordered` later + logs.push(log); + }), + ) + .max_by_key(q!(|t| t.0)) + .zip(p_ballot.clone()) + .filter_map(q!( + move |((quorum_ballot, quorum_accepted), my_ballot)| if quorum_ballot == my_ballot { + Some(quorum_accepted) + } else { + None + } + )); let p_is_leader = p_received_quorum_of_p1bs .clone() @@ -419,8 +466,8 @@ fn p_p1b<'a, P: Clone + Serialize + DeserializeOwned>( ( p_is_leader, - // The fold was not commutative, so this is unordered. - p_received_quorum_of_p1bs.flatten().assume_ordering(), + // we used an unordered accumulator, so flattened has no order + p_received_quorum_of_p1bs.flatten_unordered(), fails.map(q!(|(_, ballot)| ballot)), ) } @@ -440,7 +487,7 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( Optional>, Bounded>, ) { let p_p1b_highest_entries_and_count = accepted_logs - .flatten() // Convert HashMap log back to stream + .flatten_unordered() // Convert HashMap log back to stream .fold_keyed_commutative::<(usize, Option>), _, _>(q!(|| (0, None)), q!(|curr_entry, new_entry| { if let Some(curr_entry_payload) = &mut curr_entry.1 { let same_values = new_entry.value == curr_entry_payload.value; @@ -486,7 +533,7 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( .map(q!(|(slot, _)| slot)); let p_log_holes = p_max_slot .clone() - .flat_map(q!(|max_slot| 0..max_slot)) + .flat_map_ordered(q!(|max_slot| 0..max_slot)) .filter_not_in(p_proposed_slots) .cross_singleton(p_ballot.clone()) .map(q!(|(slot, ballot)| P2a { @@ -503,7 +550,7 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( clippy::too_many_arguments, reason = "internal paxos code // TODO" )] -fn sequence_payload<'a, P: PaxosPayload, R>( +unsafe fn sequence_payload<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, proposer_tick: &Tick>, @@ -530,18 +577,23 @@ fn sequence_payload<'a, P: PaxosPayload, R>( a_max_ballot: Singleton>, Bounded>, ) -> ( Stream<(usize, Option

    ), Cluster<'a, Proposer>, Unbounded, NoOrder>, - Singleton>, Tick>, Bounded>, + Singleton>, Timestamped>, Unbounded>, Stream, Unbounded, NoOrder>, ) { let (p_log_to_recommit, p_max_slot) = recommit_after_leader_election(p_relevant_p1bs, p_ballot.clone(), f); - let indexed_payloads = index_payloads( - proposer_tick, - p_max_slot, - c_to_proposers, - p_is_leader.clone(), - ); + let indexed_payloads = index_payloads(proposer_tick, p_max_slot, unsafe { + // SAFETY: We batch payloads so that we can compute the correct slot based on + // base slot. In the case of a leader re-election, the base slot is updated which + // affects the computed payload slots. This non-determinism can lead to non-determinism + // in which payloads are committed when the leader is changing, which is documented at + // the function level. + c_to_proposers + .timestamped(proposer_tick) + .tick_batch() + .continue_if(p_is_leader.clone()) + }); let payloads_to_send = indexed_payloads .cross_singleton(p_ballot.clone()) @@ -550,14 +602,14 @@ fn sequence_payload<'a, P: PaxosPayload, R>( Some(payload) ))) .union(p_log_to_recommit.map(q!(|p2a| ((p2a.slot, p2a.ballot), p2a.value)))) - .continue_if(p_is_leader); + .continue_if(p_is_leader) + .all_ticks(); let (a_log, a_to_proposers_p2b) = acceptor_p2( acceptor_tick, a_max_ballot.clone(), payloads_to_send .clone() - .all_ticks() .map(q!(|((slot, ballot), value)| P2a { ballot, slot, @@ -571,23 +623,23 @@ fn sequence_payload<'a, P: PaxosPayload, R>( // TOOD: only persist if we are the leader let (quorums, fails) = collect_quorum( - proposer_tick, - a_to_proposers_p2b.clone().tick_batch(proposer_tick), + a_to_proposers_p2b.timestamped(proposer_tick), f + 1, 2 * f + 1, ); - let p_to_replicas = join_responses( - proposer_tick, - quorums.keys().map(q!(|k| (k, ()))), - payloads_to_send, - ) - .all_ticks(); + let p_to_replicas = join_responses(proposer_tick, quorums.map(q!(|k| (k, ()))), unsafe { + // SAFETY: The metadata will always be generated before we get a quorum + // because `payloads_to_send` is used to send the payloads to acceptors. + payloads_to_send.tick_batch() + }); ( - p_to_replicas.map(q!(|((slot, _ballot), (value, _))| (slot, value))), + p_to_replicas + .map(q!(|((slot, _ballot), (value, _))| (slot, value))) + .drop_timestamp(), a_log.map(q!(|(_ckpnt, log)| log)), - fails.map(q!(|(_, ballot)| ballot)).all_ticks(), + fails.map(q!(|(_, ballot)| ballot)).drop_timestamp(), ) } @@ -601,8 +653,7 @@ enum CheckpointOrP2a

    { fn index_payloads<'a, P: PaxosPayload>( proposer_tick: &Tick>, p_max_slot: Optional>, Bounded>, - c_to_proposers: Stream, Unbounded>, - p_is_leader: Optional<(), Tick>, Bounded>, + c_to_proposers: Stream>, Bounded>, ) -> Stream<(usize, P), Tick>, Bounded> { let (p_next_slot_complete_cycle, p_next_slot) = proposer_tick.cycle_with_initial::>(proposer_tick.singleton(q!(0))); @@ -611,8 +662,6 @@ fn index_payloads<'a, P: PaxosPayload>( let base_slot = p_next_slot_after_reconciling_p1bs.unwrap_or(p_next_slot); let p_indexed_payloads = c_to_proposers - .tick_batch(proposer_tick) - .continue_if(p_is_leader) .enumerate() .cross_singleton(base_slot.clone()) .map(q!(|((index, payload), base_slot)| ( @@ -645,19 +694,37 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( proposers: &Cluster<'a, Proposer>, f: usize, ) -> ( - Singleton<(Option, HashMap>), Tick>, Bounded>, + Singleton< + (Option, HashMap>), + Timestamped>, + Unbounded, + >, Stream<((usize, Ballot), Result<(), Ballot>), Cluster<'a, Proposer>, Unbounded, NoOrder>, ) { - let p_to_acceptors_p2a_batch = p_to_acceptors_p2a.tick_batch(acceptor_tick); + let p_to_acceptors_p2a_batch = unsafe { + // SAFETY: we use batches to ensure that the log is updated before sending + // a confirmation to the proposer. Because we use `persist()` on these + // messages before folding into the log, non-deterministic batch boundaries + // will not affect the eventual log state. + p_to_acceptors_p2a.timestamped(acceptor_tick).tick_batch() + }; // Get the latest checkpoint sequence per replica - let a_checkpoint_largest_seqs = r_to_acceptors_checkpoint - .tick_prefix(acceptor_tick) - .reduce_keyed_commutative(q!(|curr_seq, seq| { - if seq > *curr_seq { - *curr_seq = seq; - } - })); + let a_checkpoint_largest_seqs = unsafe { + // SAFETY: if a checkpoint is delayed, its effect is that the log may contain slots + // that do not need to be saved (because the data is at all replicas). This affects + // the logs that will be collected during a leader re-election, but eventually the + // same checkpoint will arrive at acceptors and those slots will be eventually deleted. + r_to_acceptors_checkpoint + .timestamped(acceptor_tick) + .tick_batch() + } + .persist() + .reduce_keyed_commutative(q!(|curr_seq, seq| { + if seq > *curr_seq { + *curr_seq = seq; + } + })); let a_checkpoints_quorum_reached = a_checkpoint_largest_seqs.clone().count().filter_map(q!( move |num_received| if num_received == f + 1 { Some(true) @@ -686,7 +753,7 @@ fn acceptor_p2<'a, P: PaxosPayload, R>( )); let a_log = a_p2as_to_place_in_log .union(a_new_checkpoint.into_stream()) - .persist() + .all_ticks() .fold_commutative( q!(|| (None, HashMap::new())), q!(|(prev_checkpoint, log), checkpoint_or_p2a| { diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index f8b8d9625d09..89ac8583b1ff 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -44,41 +44,70 @@ pub fn paxos_bench<'a>( ballot ))) .max() - .map(q!(|ballot: Ballot| ballot.proposer_id)) - .latest_tick(&client_tick); + .map(q!(|ballot: Ballot| ballot.proposer_id)); - let leader_changed = cur_leader_id.clone().delta().map(q!(|_| ())).all_ticks(); + let leader_changed = unsafe { + // SAFETY: we are okay if we miss a transient leader ID, because we + // will eventually get the latest one and can restart requests then + cur_leader_id + .clone() + .timestamped(&client_tick) + .latest_tick() + .delta() + .map(q!(|_| ())) + .all_ticks() + .drop_timestamp() + }; bench_client( &clients, leader_changed, |c_to_proposers| { - let (new_leader_elected, processed_payloads) = paxos_kv( - &proposers, - &acceptors, - &replicas, + let to_proposers = unsafe { + // SAFETY: the risk here is that we send a batch of requests + // with a stale leader ID, but because the leader ID comes from the + // network there is no way to guarantee that it is up to date + + // TODO(shadaj): we should retry if we get an error due to sending + // to a stale leader c_to_proposers - .tick_batch(&client_tick) - .cross_singleton(cur_leader_id) + .timestamped(&client_tick) + .tick_batch() + .cross_singleton(cur_leader_id.timestamped(&client_tick).latest_tick()) .all_ticks() - .map(q!(move |((key, value), leader_id)| (leader_id, KvPayload { - key, - // we use our ID as part of the value and use that so the replica only notifies us - value: ( - CLUSTER_SELF_ID, - value - ) - }))) - .send_bincode_interleaved(&proposers) - // clients "own" certain keys, so interleaving elements from clients will not affect - // the order of writes to the same key - .assume_ordering(), - f, - i_am_leader_send_timeout, - i_am_leader_check_timeout, - i_am_leader_check_timeout_delay_multiplier, - checkpoint_frequency, - ); + } + .map(q!(move |((key, value), leader_id)| ( + leader_id, + KvPayload { + key, + // we use our ID as part of the value and use that so the replica only notifies us + value: (CLUSTER_SELF_ID, value) + } + ))) + .send_bincode_interleaved(&proposers); + + let to_proposers = unsafe { + // SAFETY: clients "own" certain keys, so interleaving elements from clients will not affect + // the order of writes to the same key + to_proposers.assume_ordering() + }; + + let (new_leader_elected, processed_payloads) = unsafe { + // SAFETY: Non-deterministic leader notifications are handled in `to_proposers`. We do not + // care about the order in which key writes are processed, which is the non-determinism in + // `processed_payloads`. + paxos_kv( + &proposers, + &acceptors, + &replicas, + to_proposers, + f, + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + checkpoint_frequency, + ) + }; new_leader_elected_complete .complete(new_leader_elected.broadcast_bincode_interleaved(&clients)); @@ -91,14 +120,13 @@ pub fn paxos_bench<'a>( .send_bincode_interleaved(&clients); // we only mark a transaction as committed when all replicas have applied it - let (c_quorum_payloads, _) = collect_quorum::<_, _, _, _, ()>( - &client_tick, - c_received_payloads.tick_batch(&client_tick), + let (c_quorum_payloads, _) = collect_quorum::<_, _, _, ()>( + c_received_payloads.timestamped(&client_tick), f + 1, f + 1, ); - c_quorum_payloads.keys().all_ticks() + c_quorum_payloads.drop_timestamp() }, num_clients_per_node, median_latency_window_size, @@ -120,9 +148,17 @@ fn bench_client<'a>( // r_to_clients_payload_applied.clone().inspect(q!(|payload: &(u32, ReplicaPayload)| println!("Client received payload: {:?}", payload))); // Whenever the leader changes, make all clients send a message - let restart_this_tick = trigger_restart.tick_batch(&client_tick).last(); + let restart_this_tick = unsafe { + // SAFETY: non-deterministic delay in restarting requests + // is okay because once it is restarted statistics should reach + // steady state regardless of when the restart happes + trigger_restart + .timestamped(&client_tick) + .tick_batch() + .last() + }; - let c_new_payloads_when_restart = restart_this_tick.clone().flat_map(q!(move |_| (0 + let c_new_payloads_when_restart = restart_this_tick.clone().flat_map_ordered(q!(move |_| (0 ..num_clients_per_node) .map(move |i| ( (CLUSTER_SELF_ID.raw_id * (num_clients_per_node as u32)) + i as u32, @@ -131,19 +167,30 @@ fn bench_client<'a>( let (c_to_proposers_complete_cycle, c_to_proposers) = clients.forward_ref::>(); - let c_received_quorum_payloads = transaction_cycle(c_to_proposers).tick_batch(&client_tick); + let c_received_quorum_payloads = unsafe { + // SAFETY: because the transaction processor is required to handle arbitrary reordering + // across *different* keys, we are safe because delaying a transaction result for a key + // will only affect when the next request for that key is emitted with respect to other + // keys + transaction_cycle(c_to_proposers) + .timestamped(&client_tick) + .tick_batch() + }; // Whenever all replicas confirm that a payload was committed, send another payload let c_new_payloads_when_committed = c_received_quorum_payloads .clone() .map(q!(|payload| (payload.0, payload.1 + 1))); c_to_proposers_complete_cycle.complete( - // we don't send a new write for the same key until the previous one is committed, - // so writes to the same key are ordered c_new_payloads_when_restart - .union(c_new_payloads_when_committed) + .chain(unsafe { + // SAFETY: we don't send a new write for the same key until the previous one is committed, + // so this contains only a single write per key, and we don't care about order + // across keys + c_new_payloads_when_committed.assume_ordering() + }) .all_ticks() - .assume_ordering(), + .drop_timestamp(), ); // Track statistics @@ -151,7 +198,7 @@ fn bench_client<'a>( client_tick.cycle::>(); let c_new_timers_when_leader_elected = restart_this_tick .map(q!(|_| SystemTime::now())) - .flat_map(q!( + .flat_map_ordered(q!( move |now| (0..num_clients_per_node).map(move |virtual_id| (virtual_id, now)) )); let c_updated_timers = c_received_quorum_payloads @@ -168,10 +215,14 @@ fn bench_client<'a>( })); c_timers_complete_cycle.complete_next_tick(c_new_timers); - let c_stats_output_timer = clients - .source_interval(q!(Duration::from_secs(1))) - .tick_batch(&client_tick) - .first(); + let c_stats_output_timer = unsafe { + // SAFETY: intentionally sampling statistics + clients + .source_interval(q!(Duration::from_secs(1))) + .timestamped(&client_tick) + .tick_batch() + } + .first(); let c_latency_reset = c_stats_output_timer.clone().map(q!(|_| None)).defer_tick(); @@ -182,7 +233,7 @@ fn bench_client<'a>( ))) .union(c_latency_reset.into_stream()) .all_ticks() - .flatten() + .flatten_ordered() .fold_commutative( // Create window with ring buffer using vec + wraparound index // TODO: Would be nice if I could use vec![] instead, but that doesn't work in HF+ with RuntimeData *median_latency_window_size @@ -230,21 +281,22 @@ fn bench_client<'a>( }), ); - c_latencies - .zip(c_throughput) - .latest_tick(&client_tick) - .continue_if(c_stats_output_timer) - .all_ticks() - .for_each(q!(move |(latencies, throughput)| { - let mut latencies_mut = latencies.borrow_mut(); - if latencies_mut.len() > 0 { - let middle_idx = latencies_mut.len() / 2; - let (_, median, _) = latencies_mut.select_nth_unstable(middle_idx); - println!("Median latency: {}ms", (*median) as f64 / 1000.0); - } + unsafe { + // SAFETY: intentionally sampling statistics + c_latencies.zip(c_throughput).latest_tick() + } + .continue_if(c_stats_output_timer) + .all_ticks() + .for_each(q!(move |(latencies, throughput)| { + let mut latencies_mut = latencies.borrow_mut(); + if latencies_mut.len() > 0 { + let middle_idx = latencies_mut.len() / 2; + let (_, median, _) = latencies_mut.select_nth_unstable(middle_idx); + println!("Median latency: {}ms", (*median) as f64 / 1000.0); + } - println!("Throughput: {} requests/s", throughput); - })); + println!("Throughput: {} requests/s", throughput); + })); // End track statistics } diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 83d1ff0bfd88..3683abc2d061 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -42,12 +42,17 @@ impl PartialOrd for SequencedKv { } } +/// Sets up a linearizable key-value store using Paxos. +/// +/// # Safety +/// Notifications for leader election are non-deterministic. When the leader is changing, +/// writes may be dropped by the old leader. #[expect( clippy::type_complexity, clippy::too_many_arguments, reason = "internal paxos code // TODO" )] -pub fn paxos_kv<'a, K: KvKey, V: KvValue>( +pub unsafe fn paxos_kv<'a, K: KvKey, V: KvValue>( proposers: &Cluster<'a, Proposer>, acceptors: &Cluster<'a, Acceptor>, replicas: &Cluster<'a, Replica>, @@ -64,16 +69,19 @@ pub fn paxos_kv<'a, K: KvKey, V: KvValue>( let (r_to_acceptors_checkpoint_complete_cycle, r_to_acceptors_checkpoint) = replicas.forward_ref::>(); - let (p_to_clients_new_leader_elected, p_to_replicas) = paxos_core( - proposers, - acceptors, - r_to_acceptors_checkpoint.broadcast_bincode(acceptors), - c_to_proposers, - f, - i_am_leader_send_timeout, - i_am_leader_check_timeout, - i_am_leader_check_timeout_delay_multiplier, - ); + let (p_to_clients_new_leader_elected, p_to_replicas) = unsafe { + // SAFETY: Leader election non-determinism and non-deterministic dropping of writes is documented. + paxos_core( + proposers, + acceptors, + r_to_acceptors_checkpoint.broadcast_bincode(acceptors), + c_to_proposers, + f, + i_am_leader_send_timeout, + i_am_leader_check_timeout, + i_am_leader_check_timeout_delay_multiplier, + ) + }; let (r_to_acceptors_checkpoint_new, r_new_processed_payloads) = replica( replicas, @@ -102,8 +110,13 @@ pub fn replica<'a, K: KvKey, V: KvValue>( let (r_buffered_payloads_complete_cycle, r_buffered_payloads) = replica_tick.cycle(); // p_to_replicas.inspect(q!(|payload: ReplicaPayload| println!("Replica received payload: {:?}", payload))); - let r_sorted_payloads = p_to_replicas - .tick_batch(&replica_tick) + let r_sorted_payloads = unsafe { + // SAFETY: because we fill slots one-by-one, we can safely batch + // because non-determinism is resolved when we sort by slots + p_to_replicas + .timestamped(&replica_tick) + .tick_batch() + } .union(r_buffered_payloads) // Combine with all payloads that we've received and not processed yet .sort(); // Create a cycle since we'll use this seq before we define it @@ -183,5 +196,8 @@ pub fn replica<'a, K: KvKey, V: KvValue>( let r_to_clients = r_processable_payloads .filter_map(q!(|payload| payload.kv)) .all_ticks(); - (r_checkpoint_seq_new.all_ticks(), r_to_clients) + ( + r_checkpoint_seq_new.all_ticks().drop_timestamp(), + r_to_clients.drop_timestamp(), + ) } diff --git a/hydroflow_plus_test/src/cluster/quorum.rs b/hydroflow_plus_test/src/cluster/quorum.rs index 7990cba7c233..b0fa2c8c3695 100644 --- a/hydroflow_plus_test/src/cluster/quorum.rs +++ b/hydroflow_plus_test/src/cluster/quorum.rs @@ -1,10 +1,11 @@ use std::hash::Hash; use hydroflow_plus::*; +use location::tick::Timestamped; use location::NoTick; #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] -pub fn collect_quorum< +pub fn collect_quorum_with_response< 'a, L: Location<'a> + NoTick, Order, @@ -12,17 +13,21 @@ pub fn collect_quorum< V: Clone, E: Clone, >( - tick: &Tick, - responses: Stream<(K, Result), Tick, Bounded, Order>, + responses: Stream<(K, Result), Timestamped, Unbounded, Order>, min: usize, max: usize, ) -> ( - Stream<(K, V), Tick, Bounded, Order>, - Stream<(K, E), Tick, Bounded, Order>, + Stream<(K, V), Timestamped, Unbounded, Order>, + Stream<(K, E), Timestamped, Unbounded, Order>, ) { + let tick = responses.timestamp_source(); let (not_all_complete_cycle, not_all) = tick.cycle::>(); - let current_responses = not_all.union(responses.clone()); + let current_responses = not_all.union(unsafe { + // SAFETY: we always persist values that have not reached quorum, so even + // with arbitrary batching we always produce deterministic quorum results + responses.clone().tick_batch() + }); let count_per_key = current_responses.clone().fold_keyed_commutative( q!(move || (0, 0)), @@ -73,11 +78,8 @@ pub fn collect_quorum< min_but_not_max_complete_cycle .complete_next_tick(reached_min_count.filter_not_in(received_from_all.clone())); - not_all_complete_cycle.complete_next_tick( - current_responses - .clone() - .anti_join(received_from_all.clone()), - ); + not_all_complete_cycle + .complete_next_tick(current_responses.clone().anti_join(received_from_all)); current_responses .anti_join(not_reached_min_count) @@ -85,10 +87,90 @@ pub fn collect_quorum< }; ( - just_reached_quorum.filter_map(q!(move |(key, res)| match res { - Ok(v) => Some((key, v)), - Err(_) => None, + just_reached_quorum + .filter_map(q!(move |(key, res)| match res { + Ok(v) => Some((key, v)), + Err(_) => None, + })) + .all_ticks(), + responses.filter_map(q!(move |(key, res)| match res { + Ok(_) => None, + Err(e) => Some((key, e)), })), + ) +} + +#[expect(clippy::type_complexity, reason = "quorum types are complex")] +pub fn collect_quorum<'a, L: Location<'a> + NoTick, Order, K: Clone + Eq + Hash, E: Clone>( + responses: Stream<(K, Result<(), E>), Timestamped, Unbounded, Order>, + min: usize, + max: usize, +) -> ( + Stream, Unbounded, Order>, + Stream<(K, E), Timestamped, Unbounded, Order>, +) { + let tick = responses.timestamp_source(); + let (not_all_complete_cycle, not_all) = tick.cycle::>(); + + let current_responses = not_all.union(unsafe { + // SAFETY: we always persist values that have not reached quorum, so even + // with arbitrary batching we always produce deterministic quorum results + responses.clone().tick_batch() + }); + + let count_per_key = current_responses.clone().fold_keyed_commutative( + q!(move || (0, 0)), + q!(move |accum, value| { + if value.is_ok() { + accum.0 += 1; + } else { + accum.1 += 1; + } + }), + ); + + let reached_min_count = + count_per_key + .clone() + .filter_map(q!(move |(key, (success, _error))| if success >= min { + Some(key) + } else { + None + })); + + let just_reached_quorum = if max == min { + not_all_complete_cycle.complete_next_tick( + current_responses + .clone() + .anti_join(reached_min_count.clone()), + ); + + reached_min_count + } else { + let (min_but_not_max_complete_cycle, min_but_not_max) = tick.cycle(); + + let received_from_all = + count_per_key.filter_map(q!( + move |(key, (success, error))| if (success + error) >= max { + Some(key) + } else { + None + } + )); + + min_but_not_max_complete_cycle.complete_next_tick( + reached_min_count + .clone() + .filter_not_in(received_from_all.clone()), + ); + + not_all_complete_cycle.complete_next_tick(current_responses.anti_join(received_from_all)); + + reached_min_count.filter_not_in(min_but_not_max) + }; + + ( + just_reached_quorum.all_ticks(), responses.filter_map(q!(move |(key, res)| match res { Ok(_) => None, Err(e) => Some((key, e)), diff --git a/hydroflow_plus_test/src/cluster/request_response.rs b/hydroflow_plus_test/src/cluster/request_response.rs index 4c8e71af1207..945baa0e456c 100644 --- a/hydroflow_plus_test/src/cluster/request_response.rs +++ b/hydroflow_plus_test/src/cluster/request_response.rs @@ -1,18 +1,21 @@ use std::hash::Hash; use hydroflow_plus::*; +use location::tick::Timestamped; use location::NoTick; use stream::NoOrder; -type JoinResponses = Stream<(K, (M, V)), Tick, Bounded, NoOrder>; +type JoinResponses = Stream<(K, (M, V)), Timestamped, Unbounded, NoOrder>; /// Given an incoming stream of request-response responses, joins with metadata generated /// at request time that is stored in-memory. /// -/// Only one response element should be produced with a given key, same for the metadata stream. +/// The metadata must be generated in the same or a previous tick than the response, +/// typically at request time. Only one response element should be produced with a given +/// key, same for the metadata stream. pub fn join_responses<'a, K: Clone + Eq + Hash, M: Clone, V: Clone, L: Location<'a> + NoTick>( tick: &Tick, - responses: Stream<(K, V), Tick, Bounded, NoOrder>, + responses: Stream<(K, V), Timestamped, Unbounded, NoOrder>, metadata: Stream<(K, M), Tick, Bounded, NoOrder>, ) -> JoinResponses { let (remaining_to_join_complete_cycle, remaining_to_join) = @@ -20,6 +23,12 @@ pub fn join_responses<'a, K: Clone + Eq + Hash, M: Clone, V: Clone, L: Location< let remaining_and_new: Stream<(K, M), Tick, Bounded, _> = remaining_to_join.union(metadata); + let responses = unsafe { + // SAFETY: because we persist the metadata, delays resulting from + // batching boundaries do not affect the output contents. + responses.tick_batch() + }; + // TODO(shadaj): we should have a "split-join" operator // that returns both join and anti-join without cloning let joined_this_tick = @@ -31,5 +40,5 @@ pub fn join_responses<'a, K: Clone + Eq + Hash, M: Clone, V: Clone, L: Location< remaining_to_join_complete_cycle .complete_next_tick(remaining_and_new.anti_join(responses.map(q!(|(key, _)| key)))); - joined_this_tick + joined_this_tick.all_ticks() } diff --git a/hydroflow_plus_test/src/cluster/simple_cluster.rs b/hydroflow_plus_test/src/cluster/simple_cluster.rs index 0a65ae3e81af..64b7e5045f9b 100644 --- a/hydroflow_plus_test/src/cluster/simple_cluster.rs +++ b/hydroflow_plus_test/src/cluster/simple_cluster.rs @@ -35,12 +35,10 @@ pub fn simple_cluster<'a>(flow: &FlowBuilder<'a>) -> (Process<'a, ()>, Cluster<' ids.cross_product(numbers) .map(q!(|(id, n)| (id, (id, n)))) .send_bincode(&cluster) - .tick_batch(&cluster.tick()) .inspect(q!(move |n| println!( "cluster received: {:?} (self cluster id: {})", n, CLUSTER_SELF_ID ))) - .all_ticks() .send_bincode(&process) .for_each(q!(|(id, d)| println!("node received: ({}, {:?})", id, d))); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index 392e8bfc87fc..b960be7d85a7 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -321,39 +321,39 @@ expression: built.ir() inner: , }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (() , ()) , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), input: CrossSingleton( - Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , bool > ({ use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout__free = 1u64 ; move | latest_received_i_am_leader | { if let Some (latest_received_i_am_leader) = latest_received_i_am_leader { (Instant :: now () . duration_since (* latest_received_i_am_leader)) > Duration :: from_secs (i_am_leader_check_timeout__free) } else { true } } }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (core :: option :: Option < tokio :: time :: Instant > , ()) , core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), - input: CrossSingleton( - Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use crate :: __staged :: cluster :: paxos :: * ; | | None }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (() , ()) , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | (d , _signal) | d }), + input: CrossSingleton( + FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , core :: option :: Option < () > > ({ use hydroflow_plus :: __staged :: stream :: * ; let duration__free = { use crate :: __staged :: cluster :: paxos :: * ; let i_am_leader_check_timeout__free = 1u64 ; Duration :: from_secs (i_am_leader_check_timeout__free) } ; move | latest_received | { if let Some (latest_received) = latest_received { if Instant :: now () . duration_since (latest_received) > duration__free { Some (()) } else { None } } else { Some (()) } } }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < core :: option :: Option < tokio :: time :: Instant > > ({ use hydroflow_plus :: __staged :: stream :: * ; | | None }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < core :: option :: Option < tokio :: time :: Instant > , hydroflow_plus_test :: cluster :: paxos :: Ballot , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | latest , _ | { * latest = Some (Instant :: now ()) ; } }), input: Persist( Tee { inner: , }, ), }, - Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), - input: Filter { - f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: singleton :: * ; | c | * c == 0 }), - input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), - input: Tee { - inner: , - }, + }, + Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < usize , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), + input: Filter { + f: stageleft :: runtime_support :: fn1_borrow_type_hint :: < usize , bool > ({ use hydroflow_plus :: __staged :: optional :: * ; | c | * c == 0 }), + input: Fold { + init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), + input: Tee { + inner: , }, }, }, - ), - }, + }, + ), }, Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: optional :: * ; | _u | () }), @@ -740,27 +740,27 @@ expression: built.ir() ), input: DeferTick( Difference( - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , usize)) , core :: option :: Option < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success >= min__free { Some (key) } else { None } }), - input: Tee { - inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , usize) > ({ use crate :: __staged :: cluster :: quorum :: * ; move | | (0 , 0) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , usize) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot > , () > ({ use crate :: __staged :: cluster :: quorum :: * ; move | accum , value | { if value . is_ok () { accum . 0 += 1 ; } else { accum . 1 += 1 ; } } }), - input: Tee { - inner: : Chain( - CycleSource { - ident: Ident { - sym: cycle_8, - }, - location_kind: Tick( - 2, - Cluster( - 0, + Tee { + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , usize)) , core :: option :: Option < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success >= min__free { Some (key) } else { None } }), + input: Tee { + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , usize) > ({ use crate :: __staged :: cluster :: quorum :: * ; move | | (0 , 0) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , usize) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot > , () > ({ use crate :: __staged :: cluster :: quorum :: * ; move | accum , value | { if value . is_ok () { accum . 0 += 1 ; } else { accum . 1 += 1 ; } } }), + input: Tee { + inner: : Chain( + CycleSource { + ident: Ident { + sym: cycle_8, + }, + location_kind: Tick( + 2, + Cluster( + 0, + ), ), - ), - }, - Tee { - inner: : Tee { + }, + Tee { inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Acceptor > , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >)) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (_ , b) | b }), input: Network { @@ -910,8 +910,8 @@ expression: built.ir() }, }, }, - }, - ), + ), + }, }, }, }, @@ -920,7 +920,7 @@ expression: built.ir() inner: : FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , usize)) , core :: option :: Option < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let max__free = 3usize ; move | (key , (success , error)) | if (success + error) >= max__free { Some (key) } else { None } }), input: Tee { - inner: , + inner: , }, }, }, @@ -940,7 +940,7 @@ expression: built.ir() input: DeferTick( AntiJoin( Tee { - inner: , + inner: , }, Tee { inner: , @@ -983,40 +983,22 @@ expression: built.ir() input: Tee { inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ()) > ({ use crate :: __staged :: cluster :: paxos :: * ; | k | (k , ()) }), - input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ()) , (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (k , _) | k }), - input: FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < () > ({ use hydroflow_plus :: __staged :: stream :: * ; | | () }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < () , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _ , _ | { } }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >) , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , ()) > > ({ use crate :: __staged :: cluster :: quorum :: * ; move | (key , res) | match res { Ok (v) => Some ((key , v)) , Err (_) => None , } }), - input: AntiJoin( - AntiJoin( - Tee { - inner: , - }, - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , (usize , usize)) , core :: option :: Option < (usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success < min__free { Some (key) } else { None } }), - input: Tee { - inner: , - }, - }, - ), - CycleSource { - ident: Ident { - sym: cycle_9, - }, - location_kind: Tick( - 2, - Cluster( - 0, - ), - ), - }, - ), + input: Difference( + Tee { + inner: , + }, + CycleSource { + ident: Ident { + sym: cycle_9, }, + location_kind: Tick( + 2, + Cluster( + 0, + ), + ), }, - }, + ), }, }, }, @@ -1146,7 +1128,7 @@ expression: built.ir() input: FilterMap { f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: result :: Result < () , hydroflow_plus_test :: cluster :: paxos :: Ballot >) , core :: option :: Option < ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , hydroflow_plus_test :: cluster :: paxos :: Ballot) > > ({ use crate :: __staged :: cluster :: quorum :: * ; move | (key , res) | match res { Ok (_) => None , Err (e) => Some ((key , e)) , } }), input: Tee { - inner: , + inner: , }, }, }, @@ -1156,7 +1138,7 @@ expression: built.ir() sym: cycle_1, }, location_kind: Tick( - 7, + 8, Cluster( 3, ), @@ -1229,7 +1211,7 @@ expression: built.ir() sym: cycle_1, }, location_kind: Tick( - 7, + 8, Cluster( 3, ), @@ -1256,7 +1238,7 @@ expression: built.ir() sym: cycle_2, }, location_kind: Tick( - 7, + 8, Cluster( 3, ), @@ -1288,7 +1270,7 @@ expression: built.ir() sym: cycle_2, }, location_kind: Tick( - 7, + 8, Cluster( 3, ), @@ -1328,7 +1310,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 7, + 8, Cluster( 3, ), @@ -1349,7 +1331,7 @@ expression: built.ir() sym: cycle_3, }, location_kind: Tick( - 7, + 8, Cluster( 3, ), @@ -1539,14 +1521,16 @@ expression: built.ir() }, ), }, - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , (usize , usize)) , core :: option :: Option < (u32 , u32) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success >= min__free { Some (key) } else { None } }), - input: Tee { - inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , usize) > ({ use crate :: __staged :: cluster :: quorum :: * ; move | | (0 , 0) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , usize) , core :: result :: Result < () , () > , () > ({ use crate :: __staged :: cluster :: quorum :: * ; move | accum , value | { if value . is_ok () { accum . 0 += 1 ; } else { accum . 1 += 1 ; } } }), - input: Tee { - inner: , + Tee { + inner: : FilterMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , (usize , usize)) , core :: option :: Option < (u32 , u32) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success >= min__free { Some (key) } else { None } }), + input: Tee { + inner: : FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , usize) > ({ use crate :: __staged :: cluster :: quorum :: * ; move | | (0 , 0) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , usize) , core :: result :: Result < () , () > , () > ({ use crate :: __staged :: cluster :: quorum :: * ; move | accum , value | { if value . is_ok () { accum . 0 += 1 ; } else { accum . 1 += 1 ; } } }), + input: Tee { + inner: , + }, }, }, }, @@ -1565,7 +1549,7 @@ expression: built.ir() FlatMap { f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let CLUSTER_SELF_ID__free = hydroflow_plus :: ClusterId :: < hydroflow_plus_test :: cluster :: paxos_bench :: Client > :: from_raw (__hydroflow_plus_cluster_self_id_2) ; let num_clients_per_node__free = 1usize ; move | _ | (0 .. num_clients_per_node__free) . map (move | i | ((CLUSTER_SELF_ID__free . raw_id * (num_clients_per_node__free as u32)) + i as u32 , 0)) }), input: Tee { - inner: : Reduce { + inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < () , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | * curr = new }), input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos :: Proposer > , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | () }), @@ -1581,26 +1565,8 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , u32) , (u32 , u32) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | payload | (payload . 0 , payload . 1 + 1) }), input: Tee { - inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , ()) , (u32 , u32) > ({ use hydroflow_plus :: __staged :: stream :: * ; | (k , _) | k }), - input: FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < () > ({ use hydroflow_plus :: __staged :: stream :: * ; | | () }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < () , () , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | _ , _ | { } }), - input: FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , core :: result :: Result < () , () >) , core :: option :: Option < ((u32 , u32) , ()) > > ({ use crate :: __staged :: cluster :: quorum :: * ; move | (key , res) | match res { Ok (v) => Some ((key , v)) , Err (_) => None , } }), - input: AntiJoin( - Tee { - inner: , - }, - FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((u32 , u32) , (usize , usize)) , core :: option :: Option < (u32 , u32) > > ({ use crate :: __staged :: cluster :: quorum :: * ; let min__free = 2usize ; move | (key , (success , _error)) | if success < min__free { Some (key) } else { None } }), - input: Tee { - inner: , - }, - }, - ), - }, - }, + inner: : Tee { + inner: , }, }, }, @@ -1622,7 +1588,7 @@ expression: built.ir() input: Chain( Chain( Tee { - inner: : CycleSource { + inner: : CycleSource { ident: Ident { sym: cycle_3, }, @@ -1639,16 +1605,16 @@ expression: built.ir() input: Map { f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), input: Tee { - inner: , + inner: , }, }, }, ), Tee { - inner: : Map { + inner: : Map { f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , u32) , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (key , _prev_count) | (key as usize , SystemTime :: now ()) }), input: Tee { - inner: , + inner: , }, }, }, @@ -1675,10 +1641,10 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), input: Join( Tee { - inner: , + inner: , }, Tee { - inner: , + inner: , }, ), }, @@ -1686,7 +1652,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { - inner: : Source { + inner: : Source { source: Stream( { use hydroflow_plus :: __staged :: location :: * ; let interval__free = { use crate :: __staged :: cluster :: paxos_bench :: * ; Duration :: from_secs (1) } ; tokio_stream :: wrappers :: IntervalStream :: new (tokio :: time :: interval (interval__free)) }, ), @@ -1716,7 +1682,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , (u32 , u32) , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, Map { @@ -1727,7 +1693,7 @@ expression: built.ir() init: stageleft :: runtime_support :: fn0_type_hint :: < usize > ({ use hydroflow_plus :: __staged :: stream :: * ; | | 0usize }), acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | count , _ | * count += 1 }), input: Tee { - inner: , + inner: , }, }, }, @@ -1739,7 +1705,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , (usize , bool) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | (0 , true) }), input: Tee { - inner: , + inner: , }, }, ), @@ -1750,7 +1716,7 @@ expression: built.ir() Map { f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , () > ({ use hydroflow_plus :: __staged :: singleton :: * ; | _u | () }), input: Tee { - inner: , + inner: , }, }, ), diff --git a/hydroflow_plus_test/src/cluster/two_pc.rs b/hydroflow_plus_test/src/cluster/two_pc.rs index dd89438e0b08..4a5f8220cb60 100644 --- a/hydroflow_plus_test/src/cluster/two_pc.rs +++ b/hydroflow_plus_test/src/cluster/two_pc.rs @@ -1,5 +1,7 @@ use hydroflow_plus::*; +use super::quorum::collect_quorum; + // if the variable start with p, that means current work is at the participant side. if start with c, at coordinator side. // @@ -32,7 +34,7 @@ pub fn two_pc<'a>( let c_receive_client_transactions = client_transaction.send_bincode(&coordinator); c_receive_client_transactions .clone() - .inspect(q!(|t| println!( + .for_each(q!(|t| println!( "receive transaction {}, ready to broadcast", t ))); @@ -40,47 +42,46 @@ pub fn two_pc<'a>( // broadcast prepare message to participants. let p_receive_prepare = c_receive_client_transactions.broadcast_bincode(&participants); - // assume all participants reply commit - let p_ready_to_commit = p_receive_prepare.map(q!(|t| (t, String::from("commit")))); + // participant 1 aborts transaction 1 + let p_ready_to_commit = p_receive_prepare.map(q!(move |t| ( + t, + if t == 1 && CLUSTER_SELF_ID.raw_id == 1 { + "abort".to_string() + } else { + "commit".to_string() + } + ))); let c_received_reply = p_ready_to_commit.send_bincode(&coordinator); // c_received_reply.clone().inspect(q!(|(id, (t, reply))| println!("participant {id} said {reply} for transaction {t}"))); // collect votes from participant. - // aborted transactions. - let c_participant_voted_abort = c_received_reply - .clone() - .filter(q!(|(_id, (_t, reply))| reply == "abort")) - .map(q!(|(id, (t, _reply))| (t, id))); - let p_receive_abort = c_participant_voted_abort.broadcast_bincode(&participants); - p_receive_abort.clone().inspect(q!(|(t, id)| println!( - "{} vote abort for transaction {}", - id, t - ))); + let coordinator_tick = coordinator.tick(); + let (c_all_commit, c_participant_voted_abort) = collect_quorum( + c_received_reply + .map(q!(|(id, (t, reply))| ( + t, + if reply == "commit" { Ok(()) } else { Err(id) } + ))) + .timestamped(&coordinator_tick), + num_participants as usize, + num_participants as usize, + ); + + let p_receive_abort = c_participant_voted_abort + // TODO(shadaj): if multiple participants vote abort we should deduplicate + .inspect(q!(|(t, id)| println!( + "{} vote abort for transaction {}", + id, t + ))) + .broadcast_bincode(&participants); let c_receive_ack = p_receive_abort.send_bincode(&coordinator); c_receive_ack.for_each(q!(|(id, (t, _))| println!( "Coordinator receive participant {} abort for transaction {}", id, t ))); - // committed transactions - let c_participant_voted_commit = c_received_reply - .filter(q!(|(_id, (_t, reply))| reply == "commit")) - .map(q!(|(id, (t, _reply))| (t, id))) - // fold_keyed: 1 input stream of type (K, V1), 1 output stream of type (K, V2). - // The output will have one tuple for each distinct K, with an accumulated value of type V2. - .tick_batch(&coordinator.tick()).fold_keyed_commutative(q!(|| 0), q!(|old: &mut u32, _| *old += 1)).filter_map(q!(move |(t, count)| { - // here I set the participant to 3. If want more or less participant, fix line 26 of examples/broadcast.rs - if count == num_participants { - Some(t) - } else { - None - } - })); - // broadcast commit transactions to participants. - let p_receive_commit = c_participant_voted_commit - .all_ticks() - .broadcast_bincode(&participants); + let p_receive_commit = c_all_commit.broadcast_bincode(&participants); // p_receive_commit.clone().for_each(q!(|t| println!("commit for transaction {}", t))); let c_receive_ack = p_receive_commit.send_bincode(&coordinator); diff --git a/hydroflow_plus_test_local/src/local/chat_app.rs b/hydroflow_plus_test_local/src/local/chat_app.rs index 566b5effbe03..26c53a528ae6 100644 --- a/hydroflow_plus_test_local/src/local/chat_app.rs +++ b/hydroflow_plus_test_local/src/local/chat_app.rs @@ -14,15 +14,27 @@ pub fn chat_app<'a>( let process = flow.process::<()>(); let tick = process.tick(); - let users = process - .source_stream(users_stream) - .tick_batch(&tick) - .persist(); + let users = unsafe { + // SAFETY: intentionally non-deterministic to not send messaged + // to users that joined after the message was sent + process + .source_stream(users_stream) + .timestamped(&tick) + .tick_batch() + } + .persist(); let messages = process.source_stream(messages); let messages = if replay_messages { - messages.tick_batch(&tick).persist() + unsafe { + // SAFETY: see above + messages.timestamped(&tick).tick_batch() + } + .persist() } else { - messages.tick_batch(&tick) + unsafe { + // SAFETY: see above + messages.timestamped(&tick).tick_batch() + } }; // do this after the persist to test pullup diff --git a/hydroflow_plus_test_local/src/local/compute_pi.rs b/hydroflow_plus_test_local/src/local/compute_pi.rs index 131c529111bf..b790f8d931f5 100644 --- a/hydroflow_plus_test_local/src/local/compute_pi.rs +++ b/hydroflow_plus_test_local/src/local/compute_pi.rs @@ -21,21 +21,25 @@ pub fn compute_pi<'a>(flow: &FlowBuilder<'a>, batch_size: RuntimeData) -> *total += 1; }), ) - .all_ticks(); - - trials - .reduce(q!(|(inside, total), (inside_batch, total_batch)| { - *inside += inside_batch; - *total += total_batch; - })) - .sample_every(q!(Duration::from_secs(1))) - .for_each(q!(|(inside, total)| { - println!( - "pi: {} ({} trials)", - 4.0 * inside as f64 / total as f64, - total - ); - })); + .all_ticks() + .drop_timestamp(); + + let estimate = trials.reduce(q!(|(inside, total), (inside_batch, total_batch)| { + *inside += inside_batch; + *total += total_batch; + })); + + unsafe { + // SAFETY: intentional non-determinism + estimate.sample_every(q!(Duration::from_secs(1))) + } + .for_each(q!(|(inside, total)| { + println!( + "pi: {} ({} trials)", + 4.0 * inside as f64 / total as f64, + total + ); + })); process } diff --git a/hydroflow_plus_test_local/src/local/count_elems.rs b/hydroflow_plus_test_local/src/local/count_elems.rs index ee670a6bfd02..528bc24db2a0 100644 --- a/hydroflow_plus_test_local/src/local/count_elems.rs +++ b/hydroflow_plus_test_local/src/local/count_elems.rs @@ -12,11 +12,12 @@ pub fn count_elems_generic<'a, T: 'a>( let tick = process.tick(); let source = process.source_stream(input_stream); - let count = source - .map(q!(|_| 1)) - .tick_batch(&tick) - .fold(q!(|| 0), q!(|a, b| *a += b)) - .all_ticks(); + let count = unsafe { + // SAFETY: intentionally using ticks + source.map(q!(|_| 1)).timestamped(&tick).tick_batch() + } + .fold(q!(|| 0), q!(|a, b| *a += b)) + .all_ticks(); count.for_each(q!(|v| { output.send(v).unwrap(); diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index 0d50a88f51ae..84a6b4b0799b 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -19,11 +19,20 @@ pub fn graph_reachability<'a>( let reachability_tick = process.tick(); let (set_reached_cycle, reached_cycle) = reachability_tick.cycle::>(); - let reached = roots.tick_batch(&reachability_tick).union(reached_cycle); + let reached = unsafe { + // SAFETY: roots can be inserted on any tick because we are fixpointing + roots + .timestamped(&reachability_tick) + .tick_batch() + .union(reached_cycle) + }; let reachable = reached .clone() .map(q!(|r| (r, ()))) - .join(edges.tick_batch(&reachability_tick).persist()) + .join(unsafe { + // SAFETY: edges can be inserted on any tick because we are fixpointing + edges.timestamped(&reachability_tick).tick_batch().persist() + }) .map(q!(|(_from, (_, to))| to)); set_reached_cycle.complete_next_tick(reached.clone().union(reachable)); diff --git a/hydroflow_plus_test_local/src/local/negation.rs b/hydroflow_plus_test_local/src/local/negation.rs index a0924f70a93b..2999cfdf8cae 100644 --- a/hydroflow_plus_test_local/src/local/negation.rs +++ b/hydroflow_plus_test_local/src/local/negation.rs @@ -12,12 +12,24 @@ pub fn test_difference<'a>( let process = flow.process::<()>(); let tick = process.tick(); - let mut source = process.source_iter(q!(0..5)).tick_batch(&tick); + let mut source = unsafe { + // SAFETY: intentionally using ticks + process + .source_iter(q!(0..5)) + .timestamped(&tick) + .tick_batch() + }; if persist1 { source = source.persist(); } - let mut source2 = process.source_iter(q!(3..6)).tick_batch(&tick); + let mut source2 = unsafe { + // SAFETY: intentionally using ticks + process + .source_iter(q!(3..6)) + .timestamped(&tick) + .tick_batch() + }; if persist2 { source2 = source2.persist(); } @@ -39,15 +51,25 @@ pub fn test_anti_join<'a>( let process = flow.process::<()>(); let tick = process.tick(); - let mut source = process - .source_iter(q!(0..5)) - .map(q!(|v| (v, v))) - .tick_batch(&tick); + let mut source = unsafe { + // SAFETY: intentionally using ticks + process + .source_iter(q!(0..5)) + .map(q!(|v| (v, v))) + .timestamped(&tick) + .tick_batch() + }; if persist1 { source = source.persist(); } - let mut source2 = process.source_iter(q!(3..6)).tick_batch(&tick); + let mut source2 = unsafe { + // SAFETY: intentionally using ticks + process + .source_iter(q!(3..6)) + .timestamped(&tick) + .tick_batch() + }; if persist2 { source2 = source2.persist(); } diff --git a/hydroflow_plus_test_local/src/local/teed_join.rs b/hydroflow_plus_test_local/src/local/teed_join.rs index 36e575f4109e..96ab6789243a 100644 --- a/hydroflow_plus_test_local/src/local/teed_join.rs +++ b/hydroflow_plus_test_local/src/local/teed_join.rs @@ -19,7 +19,13 @@ pub fn teed_join<'a, S: Stream + Unpin + 'a>( let node_one = flow.process::(); let n0_tick = node_zero.tick(); - let source = node_zero.source_stream(input_stream).tick_batch(&n0_tick); + let source = unsafe { + // SAFETY: intentionally using ticks + node_zero + .source_stream(input_stream) + .timestamped(&n0_tick) + .tick_batch() + }; let map1 = source.clone().map(q!(|v| (v + 1, ()))); let map2 = source.map(q!(|v| (v - 1, ()))); diff --git a/stageleft/src/lib.rs b/stageleft/src/lib.rs index d38026335275..ee1cdabaad1a 100644 --- a/stageleft/src/lib.rs +++ b/stageleft/src/lib.rs @@ -469,7 +469,6 @@ impl<'a, T: 'a, Ctx> QuotedWithContext<'a, T, Ctx> for RuntimeData {} impl Copy for RuntimeData {} -// TODO(shadaj): relax this to allow for non-copy types impl Clone for RuntimeData { fn clone(&self) -> Self { *self diff --git a/template/hydroflow_plus/Cargo.toml b/template/hydroflow_plus/Cargo.toml index 2c521b6155d6..1544bce2112e 100644 --- a/template/hydroflow_plus/Cargo.toml +++ b/template/hydroflow_plus/Cargo.toml @@ -23,3 +23,6 @@ hydroflow_plus = { git = "{{ hydroflow_git | default: 'https://github.com/hydro- "deploy", ] } tokio-stream = { version = "0.1.3", default-features = false } + +[lints.rust] +unsafe_op_in_unsafe_fn = "warn" From 6688e9723151ff28ed29538a397a31bdd272d7a2 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Wed, 27 Nov 2024 16:49:27 -0800 Subject: [PATCH 71/74] fix(paxos): use `Instant` instead of `SystemTime` to ensure monotonic clock (#1588) --- hydroflow_plus_test/src/cluster/paxos.rs | 4 +- .../src/cluster/paxos_bench.rs | 15 +++--- ...cluster__paxos_bench__tests__paxos_ir.snap | 49 ++++++++++--------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index e06628c28841..a74304b4a48e 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -508,12 +508,12 @@ fn recommit_after_leader_election<'a, P: PaxosPayload>( } else { *curr_entry = (1, Some(new_entry)); } - })); + })) + .map(q!(|(slot, (count, entry))| (slot, (count, entry.unwrap())))); let p_log_to_try_commit = p_p1b_highest_entries_and_count .clone() .cross_singleton(p_ballot.clone()) .filter_map(q!(move |((slot, (count, entry)), ballot)| { - let entry = entry.unwrap(); if count <= f { Some(P2a { ballot, diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 89ac8583b1ff..2d11038b95c0 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -1,9 +1,10 @@ use std::cell::RefCell; use std::rc::Rc; -use std::time::{Duration, SystemTime}; +use std::time::Duration; use hydroflow_plus::*; use stream::{NoOrder, TotalOrder}; +use tokio::time::Instant; use super::paxos::{Acceptor, Ballot, Proposer}; use super::paxos_kv::{paxos_kv, KvPayload, Replica}; @@ -195,15 +196,15 @@ fn bench_client<'a>( // Track statistics let (c_timers_complete_cycle, c_timers) = - client_tick.cycle::>(); + client_tick.cycle::>(); let c_new_timers_when_leader_elected = restart_this_tick - .map(q!(|_| SystemTime::now())) + .map(q!(|_| Instant::now())) .flat_map_ordered(q!( move |now| (0..num_clients_per_node).map(move |virtual_id| (virtual_id, now)) )); let c_updated_timers = c_received_quorum_payloads .clone() - .map(q!(|(key, _prev_count)| (key as usize, SystemTime::now()))); + .map(q!(|(key, _prev_count)| (key as usize, Instant::now()))); let c_new_timers = c_timers .clone() // Update c_timers in tick+1 so we can record differences during this tick (to track latency) .union(c_new_timers_when_leader_elected) @@ -229,7 +230,7 @@ fn bench_client<'a>( let c_latencies = c_timers .join(c_updated_timers) .map(q!(|(_virtual_id, (prev_time, curr_time))| Some( - curr_time.duration_since(prev_time).unwrap().as_micros() + curr_time.duration_since(prev_time) ))) .union(c_latency_reset.into_stream()) .all_ticks() @@ -238,7 +239,7 @@ fn bench_client<'a>( // Create window with ring buffer using vec + wraparound index // TODO: Would be nice if I could use vec![] instead, but that doesn't work in HF+ with RuntimeData *median_latency_window_size q!(move || ( - Rc::new(RefCell::new(Vec::::with_capacity( + Rc::new(RefCell::new(Vec::::with_capacity( median_latency_window_size ))), 0usize, @@ -292,7 +293,7 @@ fn bench_client<'a>( if latencies_mut.len() > 0 { let middle_idx = latencies_mut.len() / 2; let (_, median, _) = latencies_mut.select_nth_unstable(middle_idx); - println!("Median latency: {}ms", (*median) as f64 / 1000.0); + println!("Median latency: {}ms", median.as_micros() as f64 / 1000.0); } println!("Throughput: {} requests/s", throughput); diff --git a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap index b960be7d85a7..30433787404a 100644 --- a/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap +++ b/hydroflow_plus_test/src/cluster/snapshots/hydroflow_plus_test__cluster__paxos_bench__tests__paxos_ir.snap @@ -671,17 +671,20 @@ expression: built.ir() inner: : Reduce { f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < usize , usize , () > ({ use hydroflow_plus :: __staged :: stream :: * ; | curr , new | { if new > * curr { * curr = new ; } } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { - inner: : FoldKeyed { - init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), - input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | d | d }), + inner: : Map { + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >)) , (usize , (usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >)) > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , (count , entry)) | (slot , (count , entry . unwrap ())) }), + input: FoldKeyed { + init: stageleft :: runtime_support :: fn0_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | | (0 , None) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >) , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , () > ({ use crate :: __staged :: cluster :: paxos :: * ; | curr_entry , new_entry | { if let Some (curr_entry_payload) = & mut curr_entry . 1 { let same_values = new_entry . value == curr_entry_payload . value ; let higher_ballot = new_entry . ballot > curr_entry_payload . ballot ; if same_values { curr_entry . 0 += 1 ; } if higher_ballot { curr_entry_payload . ballot = new_entry . ballot ; if ! same_values { curr_entry . 0 = 1 ; curr_entry_payload . value = new_entry . value ; } } } else { * curr_entry = (1 , Some (new_entry)) ; } } }), input: FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > , std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > > ({ use hydroflow_plus :: __staged :: optional :: * ; | v | v }), - input: Tee { - inner: , + f: stageleft :: runtime_support :: fn1_type_hint :: < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > , std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > ({ use hydroflow_plus :: __staged :: stream :: * ; | d | d }), + input: FlatMap { + f: stageleft :: runtime_support :: fn1_type_hint :: < std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > , std :: vec :: Vec < std :: collections :: hash_map :: HashMap < usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > > ({ use hydroflow_plus :: __staged :: optional :: * ; | v | v }), + input: Tee { + inner: , + }, }, }, }, @@ -853,7 +856,7 @@ expression: built.ir() f: stageleft :: runtime_support :: fn1_type_hint :: < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > , ((usize , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >) > ({ use crate :: __staged :: cluster :: paxos :: * ; | p2a | ((p2a . slot , p2a . ballot) , p2a . value) }), input: Chain( FilterMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >)) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , (count , entry)) , ballot) | { let entry = entry . unwrap () ; if count <= f__free { Some (P2a { ballot , slot , value : entry . value , }) } else { None } } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((usize , (usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >)) , hydroflow_plus_test :: cluster :: paxos :: Ballot) , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: P2a < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > > > ({ use crate :: __staged :: cluster :: paxos :: * ; let f__free = 1usize ; move | ((slot , (count , entry)) , ballot) | { if count <= f__free { Some (P2a { ballot , slot , value : entry . value , }) } else { None } } }), input: CrossSingleton( Tee { inner: , @@ -874,7 +877,7 @@ expression: built.ir() }, }, Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , core :: option :: Option < hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (usize , hydroflow_plus_test :: cluster :: paxos :: LogValue < hydroflow_plus_test :: cluster :: paxos_kv :: KvPayload < u32 , (hydroflow_plus :: location :: cluster :: cluster_id :: ClusterId < hydroflow_plus_test :: cluster :: paxos_bench :: Client > , u32) > >)) , usize > ({ use crate :: __staged :: cluster :: paxos :: * ; | (slot , _) | slot }), input: Tee { inner: , }, @@ -1584,7 +1587,7 @@ expression: built.ir() ), input: DeferTick( ReduceKeyed { - f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < std :: time :: SystemTime , std :: time :: SystemTime , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_time , new_time | { if new_time > * curr_time { * curr_time = new_time ; } } }), + f: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < tokio :: time :: Instant , tokio :: time :: Instant , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | curr_time , new_time | { if new_time > * curr_time { * curr_time = new_time ; } } }), input: Chain( Chain( Tee { @@ -1601,9 +1604,9 @@ expression: built.ir() }, }, FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < std :: time :: SystemTime , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node__free = 1usize ; move | now | (0 .. num_clients_per_node__free) . map (move | virtual_id | (virtual_id , now)) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , std :: iter :: Map < std :: ops :: Range < usize > , _ > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let num_clients_per_node__free = 1usize ; move | now | (0 .. num_clients_per_node__free) . map (move | virtual_id | (virtual_id , now)) }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < () , std :: time :: SystemTime > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | SystemTime :: now () }), + f: stageleft :: runtime_support :: fn1_type_hint :: < () , tokio :: time :: Instant > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | Instant :: now () }), input: Tee { inner: , }, @@ -1612,7 +1615,7 @@ expression: built.ir() ), Tee { inner: : Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , u32) , (usize , std :: time :: SystemTime) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (key , _prev_count) | (key as usize , SystemTime :: now ()) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (u32 , u32) , (usize , tokio :: time :: Instant) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (key , _prev_count) | (key as usize , Instant :: now ()) }), input: Tee { inner: , }, @@ -1623,22 +1626,22 @@ expression: built.ir() ), }, ForEach { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; move | (latencies , throughput) | { let mut latencies_mut = latencies . borrow_mut () ; if latencies_mut . len () > 0 { let middle_idx = latencies_mut . len () / 2 ; let (_ , median , _) = latencies_mut . select_nth_unstable (middle_idx) ; println ! ("Median latency: {}ms" , (* median) as f64 / 1000.0) ; } println ! ("Throughput: {} requests/s" , throughput) ; } }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < core :: time :: Duration > > > , usize) , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; move | (latencies , throughput) | { let mut latencies_mut = latencies . borrow_mut () ; if latencies_mut . len () > 0 { let middle_idx = latencies_mut . len () / 2 ; let (_ , median , _) = latencies_mut . select_nth_unstable (middle_idx) ; println ! ("Median latency: {}ms" , median . as_micros () as f64 / 1000.0) ; } println ! ("Throughput: {} requests/s" , throughput) ; } }), input: Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < ((std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , ()) , (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < ((std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < core :: time :: Duration > > > , usize) , ()) , (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < core :: time :: Duration > > > , usize) > ({ use hydroflow_plus :: __staged :: singleton :: * ; | (d , _signal) | d }), input: CrossSingleton( CrossSingleton( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (latencies , _) | latencies }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < core :: time :: Duration > > > , usize) , std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < core :: time :: Duration > > > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (latencies , _) | latencies }), input: Fold { - init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size__free = 1usize ; move | | (Rc :: new (RefCell :: new (Vec :: < u128 > :: with_capacity (median_latency_window_size__free))) , 0usize ,) }), - acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < u128 > > > , usize) , u128 , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size__free = 1usize ; move | (latencies , write_index) , latency | { let mut latencies_mut = latencies . borrow_mut () ; if * write_index < latencies_mut . len () { latencies_mut [* write_index] = latency ; } else { latencies_mut . push (latency) ; } * write_index = (* write_index + 1) % median_latency_window_size__free ; } }), + init: stageleft :: runtime_support :: fn0_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < core :: time :: Duration > > > , usize) > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size__free = 1usize ; move | | (Rc :: new (RefCell :: new (Vec :: < Duration > :: with_capacity (median_latency_window_size__free))) , 0usize ,) }), + acc: stageleft :: runtime_support :: fn2_borrow_mut_type_hint :: < (std :: rc :: Rc < core :: cell :: RefCell < std :: vec :: Vec < core :: time :: Duration > > > , usize) , core :: time :: Duration , () > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; let median_latency_window_size__free = 1usize ; move | (latencies , write_index) , latency | { let mut latencies_mut = latencies . borrow_mut () ; if * write_index < latencies_mut . len () { latencies_mut [* write_index] = latency ; } else { latencies_mut . push (latency) ; } * write_index = (* write_index + 1) % median_latency_window_size__free ; } }), input: Persist( FlatMap { - f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < u128 > , core :: option :: Option < u128 > > ({ use hydroflow_plus :: __staged :: stream :: * ; | d | d }), + f: stageleft :: runtime_support :: fn1_type_hint :: < core :: option :: Option < core :: time :: Duration > , core :: option :: Option < core :: time :: Duration > > ({ use hydroflow_plus :: __staged :: stream :: * ; | d | d }), input: Chain( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (std :: time :: SystemTime , std :: time :: SystemTime)) , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time) . unwrap () . as_micros ()) }), + f: stageleft :: runtime_support :: fn1_type_hint :: < (usize , (tokio :: time :: Instant , tokio :: time :: Instant)) , core :: option :: Option < core :: time :: Duration > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | (_virtual_id , (prev_time , curr_time)) | Some (curr_time . duration_since (prev_time)) }), input: Join( Tee { inner: , @@ -1650,7 +1653,7 @@ expression: built.ir() }, DeferTick( Map { - f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , core :: option :: Option < u128 > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), + f: stageleft :: runtime_support :: fn1_type_hint :: < tokio :: time :: Instant , core :: option :: Option < core :: time :: Duration > > ({ use crate :: __staged :: cluster :: paxos_bench :: * ; | _ | None }), input: Tee { inner: : Source { source: Stream( From 78f6a3299fff822abaea50841800a07f0e2ae128 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Sun, 1 Dec 2024 12:00:02 -0800 Subject: [PATCH 72/74] refactor(hydroflow_plus)!: further reduce namespace pollution (#1589) --- hydroflow_plus/src/deploy/trybuild.rs | 2 +- hydroflow_plus/src/lib.rs | 8 ++++---- hydroflow_plus/src/location/mod.rs | 2 +- hydroflow_plus_test/src/cluster/paxos.rs | 2 -- hydroflow_plus_test/src/cluster/paxos_bench.rs | 1 - hydroflow_plus_test/src/cluster/paxos_kv.rs | 1 - hydroflow_plus_test/src/cluster/quorum.rs | 1 - hydroflow_plus_test/src/cluster/request_response.rs | 2 -- hydroflow_plus_test/src/distributed/first_ten.rs | 1 - hydroflow_plus_test_local/src/local/chat_app.rs | 2 ++ hydroflow_plus_test_local/src/local/compute_pi.rs | 2 ++ hydroflow_plus_test_local/src/local/count_elems.rs | 4 +++- hydroflow_plus_test_local/src/local/first_ten.rs | 2 ++ hydroflow_plus_test_local/src/local/graph_reachability.rs | 3 ++- hydroflow_plus_test_local/src/local/negation.rs | 2 ++ hydroflow_plus_test_local/src/local/teed_join.rs | 2 ++ 16 files changed, 21 insertions(+), 16 deletions(-) diff --git a/hydroflow_plus/src/deploy/trybuild.rs b/hydroflow_plus/src/deploy/trybuild.rs index 479d25962e92..1d075b1205a9 100644 --- a/hydroflow_plus/src/deploy/trybuild.rs +++ b/hydroflow_plus/src/deploy/trybuild.rs @@ -107,7 +107,7 @@ pub fn compile_graph_trybuild(graph: HydroflowGraph, extra_stmts: Vec use hydroflow_plus::*; #[allow(unused)] - fn __hfplus_runtime<'a>(__hydroflow_plus_trybuild_cli: &'a hydroflow_plus::hydroflow::util::deploy::DeployPorts) -> hydroflow_plus::Hydroflow<'a> { + fn __hfplus_runtime<'a>(__hydroflow_plus_trybuild_cli: &'a hydroflow_plus::hydroflow::util::deploy::DeployPorts) -> hydroflow_plus::hydroflow::scheduled::graph::Hydroflow<'a> { #(#extra_stmts)* #tokens } diff --git a/hydroflow_plus/src/lib.rs b/hydroflow_plus/src/lib.rs index 758e71098a28..d9b25a8f5534 100644 --- a/hydroflow_plus/src/lib.rs +++ b/hydroflow_plus/src/lib.rs @@ -3,9 +3,9 @@ stageleft::stageleft_no_entry_crate!(); pub use hydroflow; -pub use hydroflow::scheduled::graph::Hydroflow; -pub use stageleft::*; +pub use stageleft::q; +#[doc(hidden)] pub mod runtime_support { pub use bincode; } @@ -17,7 +17,7 @@ pub mod boundedness; pub use boundedness::{Bounded, Unbounded}; pub mod stream; -pub use stream::Stream; +pub use stream::{NoOrder, Stream, TotalOrder}; pub mod singleton; pub use singleton::Singleton; @@ -27,7 +27,7 @@ pub use optional::Optional; pub mod location; pub use location::cluster::CLUSTER_SELF_ID; -pub use location::{Cluster, ClusterId, Location, Process, Tick}; +pub use location::{Cluster, ClusterId, ExternalProcess, Location, Process, Tick, Timestamped}; pub mod deploy; diff --git a/hydroflow_plus/src/location/mod.rs b/hydroflow_plus/src/location/mod.rs index 70e39d36a353..2cdf637ebcd9 100644 --- a/hydroflow_plus/src/location/mod.rs +++ b/hydroflow_plus/src/location/mod.rs @@ -25,7 +25,7 @@ pub mod can_send; pub use can_send::CanSend; pub mod tick; -pub use tick::{NoTick, Tick}; +pub use tick::{NoTick, Tick, Timestamped}; #[derive(PartialEq, Eq, Clone, Debug)] pub enum LocationId { diff --git a/hydroflow_plus_test/src/cluster/paxos.rs b/hydroflow_plus_test/src/cluster/paxos.rs index a74304b4a48e..af3a3ca04f3a 100644 --- a/hydroflow_plus_test/src/cluster/paxos.rs +++ b/hydroflow_plus_test/src/cluster/paxos.rs @@ -4,10 +4,8 @@ use std::hash::Hash; use std::time::Duration; use hydroflow_plus::*; -use location::tick::Timestamped; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use stream::NoOrder; use super::quorum::{collect_quorum, collect_quorum_with_response}; use super::request_response::join_responses; diff --git a/hydroflow_plus_test/src/cluster/paxos_bench.rs b/hydroflow_plus_test/src/cluster/paxos_bench.rs index 2d11038b95c0..4d87409fbde1 100644 --- a/hydroflow_plus_test/src/cluster/paxos_bench.rs +++ b/hydroflow_plus_test/src/cluster/paxos_bench.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use std::time::Duration; use hydroflow_plus::*; -use stream::{NoOrder, TotalOrder}; use tokio::time::Instant; use super::paxos::{Acceptor, Ballot, Proposer}; diff --git a/hydroflow_plus_test/src/cluster/paxos_kv.rs b/hydroflow_plus_test/src/cluster/paxos_kv.rs index 3683abc2d061..d157d67380d1 100644 --- a/hydroflow_plus_test/src/cluster/paxos_kv.rs +++ b/hydroflow_plus_test/src/cluster/paxos_kv.rs @@ -5,7 +5,6 @@ use std::hash::Hash; use hydroflow_plus::*; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use stream::NoOrder; use super::paxos::{paxos_core, Acceptor, Ballot, Proposer}; diff --git a/hydroflow_plus_test/src/cluster/quorum.rs b/hydroflow_plus_test/src/cluster/quorum.rs index b0fa2c8c3695..7a6547e17018 100644 --- a/hydroflow_plus_test/src/cluster/quorum.rs +++ b/hydroflow_plus_test/src/cluster/quorum.rs @@ -1,7 +1,6 @@ use std::hash::Hash; use hydroflow_plus::*; -use location::tick::Timestamped; use location::NoTick; #[expect(clippy::type_complexity, reason = "internal paxos code // TODO")] diff --git a/hydroflow_plus_test/src/cluster/request_response.rs b/hydroflow_plus_test/src/cluster/request_response.rs index 945baa0e456c..492c06da865e 100644 --- a/hydroflow_plus_test/src/cluster/request_response.rs +++ b/hydroflow_plus_test/src/cluster/request_response.rs @@ -1,9 +1,7 @@ use std::hash::Hash; use hydroflow_plus::*; -use location::tick::Timestamped; use location::NoTick; -use stream::NoOrder; type JoinResponses = Stream<(K, (M, V)), Timestamped, Unbounded, NoOrder>; diff --git a/hydroflow_plus_test/src/distributed/first_ten.rs b/hydroflow_plus_test/src/distributed/first_ten.rs index 5efc0c0b2790..5099e7d4e50e 100644 --- a/hydroflow_plus_test/src/distributed/first_ten.rs +++ b/hydroflow_plus_test/src/distributed/first_ten.rs @@ -1,6 +1,5 @@ use hydroflow_plus::*; use location::external_process::ExternalBincodeSink; -use location::ExternalProcess; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] diff --git a/hydroflow_plus_test_local/src/local/chat_app.rs b/hydroflow_plus_test_local/src/local/chat_app.rs index 26c53a528ae6..b34d8e8dcecc 100644 --- a/hydroflow_plus_test_local/src/local/chat_app.rs +++ b/hydroflow_plus_test_local/src/local/chat_app.rs @@ -1,7 +1,9 @@ use hydroflow::tokio::sync::mpsc::UnboundedSender; use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::SingleProcessGraph; +use hydroflow_plus::hydroflow::scheduled::graph::Hydroflow; use hydroflow_plus::*; +use stageleft::{Quoted, RuntimeData}; #[stageleft::entry] pub fn chat_app<'a>( diff --git a/hydroflow_plus_test_local/src/local/compute_pi.rs b/hydroflow_plus_test_local/src/local/compute_pi.rs index b790f8d931f5..79ef4e3ba939 100644 --- a/hydroflow_plus_test_local/src/local/compute_pi.rs +++ b/hydroflow_plus_test_local/src/local/compute_pi.rs @@ -1,7 +1,9 @@ use std::time::Duration; use hydroflow_plus::deploy::SingleProcessGraph; +use hydroflow_plus::hydroflow::scheduled::graph::Hydroflow; use hydroflow_plus::*; +use stageleft::{Quoted, RuntimeData}; pub fn compute_pi<'a>(flow: &FlowBuilder<'a>, batch_size: RuntimeData) -> Process<'a, ()> { let process = flow.process(); diff --git a/hydroflow_plus_test_local/src/local/count_elems.rs b/hydroflow_plus_test_local/src/local/count_elems.rs index 528bc24db2a0..06497ea964be 100644 --- a/hydroflow_plus_test_local/src/local/count_elems.rs +++ b/hydroflow_plus_test_local/src/local/count_elems.rs @@ -1,13 +1,15 @@ use hydroflow::tokio::sync::mpsc::UnboundedSender; use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::SingleProcessGraph; +use hydroflow_plus::hydroflow::scheduled::graph::Hydroflow; use hydroflow_plus::*; +use stageleft::{Quoted, RuntimeData}; pub fn count_elems_generic<'a, T: 'a>( flow: FlowBuilder<'a>, input_stream: RuntimeData>, output: RuntimeData<&'a UnboundedSender>, -) -> impl QuotedWithContext<'a, Hydroflow<'a>, ()> { +) -> impl Quoted<'a, Hydroflow<'a>> { let process = flow.process::<()>(); let tick = process.tick(); diff --git a/hydroflow_plus_test_local/src/local/first_ten.rs b/hydroflow_plus_test_local/src/local/first_ten.rs index 867df566ce38..2b027036e570 100644 --- a/hydroflow_plus_test_local/src/local/first_ten.rs +++ b/hydroflow_plus_test_local/src/local/first_ten.rs @@ -1,5 +1,7 @@ use hydroflow_plus::deploy::SingleProcessGraph; +use hydroflow_plus::hydroflow::scheduled::graph::Hydroflow; use hydroflow_plus::*; +use stageleft::Quoted; pub fn first_ten(flow: &FlowBuilder) { let process = flow.process::<()>(); diff --git a/hydroflow_plus_test_local/src/local/graph_reachability.rs b/hydroflow_plus_test_local/src/local/graph_reachability.rs index 84a6b4b0799b..c694b970a704 100644 --- a/hydroflow_plus_test_local/src/local/graph_reachability.rs +++ b/hydroflow_plus_test_local/src/local/graph_reachability.rs @@ -1,8 +1,9 @@ use hydroflow::tokio::sync::mpsc::UnboundedSender; use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::SingleProcessGraph; +use hydroflow_plus::hydroflow::scheduled::graph::Hydroflow; use hydroflow_plus::*; -use stream::NoOrder; +use stageleft::{Quoted, RuntimeData}; #[stageleft::entry] pub fn graph_reachability<'a>( diff --git a/hydroflow_plus_test_local/src/local/negation.rs b/hydroflow_plus_test_local/src/local/negation.rs index 2999cfdf8cae..a44dc6505de6 100644 --- a/hydroflow_plus_test_local/src/local/negation.rs +++ b/hydroflow_plus_test_local/src/local/negation.rs @@ -1,6 +1,8 @@ use hydroflow::tokio::sync::mpsc::UnboundedSender; use hydroflow_plus::deploy::SingleProcessGraph; +use hydroflow_plus::hydroflow::scheduled::graph::Hydroflow; use hydroflow_plus::*; +use stageleft::{Quoted, RuntimeData}; #[stageleft::entry] pub fn test_difference<'a>( diff --git a/hydroflow_plus_test_local/src/local/teed_join.rs b/hydroflow_plus_test_local/src/local/teed_join.rs index 96ab6789243a..979a5a149945 100644 --- a/hydroflow_plus_test_local/src/local/teed_join.rs +++ b/hydroflow_plus_test_local/src/local/teed_join.rs @@ -2,7 +2,9 @@ use hydroflow::futures::stream::Stream; use hydroflow::tokio::sync::mpsc::UnboundedSender; use hydroflow::tokio_stream::wrappers::UnboundedReceiverStream; use hydroflow_plus::deploy::MultiGraph; +use hydroflow_plus::hydroflow::scheduled::graph::Hydroflow; use hydroflow_plus::*; +use stageleft::{Quoted, RuntimeData}; struct N0 {} struct N1 {} From 0dc709ed5a53c723f47fa1d10063e57bb50a63c8 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Mon, 2 Dec 2024 12:44:19 -0800 Subject: [PATCH 73/74] chore(stageleft): use same hashing library everywhere (#1590) --- Cargo.lock | 23 ++--------------------- hydroflow_plus/src/deploy/trybuild.rs | 4 +--- stageleft_macro/Cargo.toml | 2 +- stageleft_macro/src/lib.rs | 4 +++- stageleft_tool/Cargo.toml | 2 +- stageleft_tool/src/lib.rs | 5 +++-- 6 files changed, 11 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17b2e0470e4a..396a800a5c20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1325,12 +1325,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "home" version = "0.5.9" @@ -3227,19 +3221,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha256" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" -dependencies = [ - "async-trait", - "bytes", - "hex", - "sha2", - "tokio", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -3364,7 +3345,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "sha256", + "sha2", "syn 2.0.75", ] @@ -3391,7 +3372,7 @@ version = "0.4.0" dependencies = [ "proc-macro2", "quote", - "sha256", + "sha2", "syn 2.0.75", "syn-inline-mod", ] diff --git a/hydroflow_plus/src/deploy/trybuild.rs b/hydroflow_plus/src/deploy/trybuild.rs index 1d075b1205a9..7922e342b91c 100644 --- a/hydroflow_plus/src/deploy/trybuild.rs +++ b/hydroflow_plus/src/deploy/trybuild.rs @@ -73,9 +73,7 @@ pub fn create_graph_trybuild( } }); - let mut hasher = Sha256::new(); - hasher.update(&source); - let hash = format!("{:X}", hasher.finalize()) + let hash = format!("{:X}", Sha256::digest(&source)) .chars() .take(8) .collect::(); diff --git a/stageleft_macro/Cargo.toml b/stageleft_macro/Cargo.toml index 8ef2802afba9..b9f1b3fba3f7 100644 --- a/stageleft_macro/Cargo.toml +++ b/stageleft_macro/Cargo.toml @@ -19,7 +19,7 @@ quote = "1.0.35" syn = { version = "2.0.46", features = [ "parsing", "extra-traits", "visit" ] } proc-macro2 = "1.0.74" proc-macro-crate = "1.0.0" -sha256 = "1.0.0" +sha2 = "0.10.0" [dev-dependencies] insta = "1.39" diff --git a/stageleft_macro/src/lib.rs b/stageleft_macro/src/lib.rs index 279a53a88c71..b36241554b9d 100644 --- a/stageleft_macro/src/lib.rs +++ b/stageleft_macro/src/lib.rs @@ -1,5 +1,6 @@ use proc_macro2::{Punct, Spacing, Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; +use sha2::{Digest, Sha256}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{AngleBracketedGenericArguments, Token, Type}; @@ -339,7 +340,7 @@ pub fn entry( .chars() .filter(|c| c.is_alphanumeric()) .collect::(); - let input_hash = "macro_".to_string() + &sha256::digest(input_contents); + let input_hash = "macro_".to_string() + &format!("{:X}", Sha256::digest(input_contents)); let input_hash_ident = syn::Ident::new(&input_hash, Span::call_site()); let input_hash_impl_ident = syn::Ident::new(&(input_hash + "_impl"), Span::call_site()); @@ -353,6 +354,7 @@ pub fn entry( #[cfg_attr(not(stageleft_macro), allow(unused))] #[cfg_attr(not(stageleft_macro), doc(hidden))] + #[allow(non_snake_case)] pub(crate) fn #input_hash_impl_ident(input: #root::internal::TokenStream) -> #root::internal::TokenStream { let input_parsed = #root::internal::syn::parse::Parser::parse( #root::internal::syn::punctuated::Punctuated::<#root::internal::syn::Expr, #root::internal::syn::Token![,]>::parse_terminated, diff --git a/stageleft_tool/Cargo.toml b/stageleft_tool/Cargo.toml index d8250cb5c3f3..cca49aa24caf 100644 --- a/stageleft_tool/Cargo.toml +++ b/stageleft_tool/Cargo.toml @@ -18,4 +18,4 @@ syn-inline-mod = "0.6.0" quote = "1.0.35" syn = { version = "2.0.46", features = [ "parsing", "extra-traits", "visit" ] } proc-macro2 = "1.0.74" -sha256 = "1.0.0" +sha2 = "0.10.0" diff --git a/stageleft_tool/src/lib.rs b/stageleft_tool/src/lib.rs index 6cc232eeb6b5..2af956a2e935 100644 --- a/stageleft_tool/src/lib.rs +++ b/stageleft_tool/src/lib.rs @@ -4,6 +4,7 @@ use std::{env, fs}; use proc_macro2::Span; use quote::ToTokens; +use sha2::{Digest, Sha256}; use syn::visit::Visit; use syn::visit_mut::VisitMut; use syn::{parse_quote, UsePath}; @@ -41,7 +42,7 @@ impl<'a> Visit<'a> for GenMacroVistor { .chars() .filter(|c| c.is_alphanumeric()) .collect::(); - let contents_hash = sha256::digest(contents); + let contents_hash = format!("{:X}", Sha256::digest(contents)); self.exported_macros .insert((contents_hash, cur_path.to_token_stream().to_string())); } @@ -72,7 +73,7 @@ pub fn gen_macro(staged_path: &Path, crate_name: &str) { let proc_macro_wrapper: syn::ItemFn = parse_quote!( #[proc_macro] - #[expect(unused_qualifications, reason = "generated code")] + #[expect(unused_qualifications, non_snake_case, reason = "generated code")] pub fn #underscored_path(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream { let input = ::stageleft::internal::TokenStream::from(input); let out = #exported_from_parsed::#underscored_path_impl(input); From b8acd843bdbcfb445bf942e697447f6bf58a10da Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Tue, 3 Dec 2024 13:03:25 -0800 Subject: [PATCH 74/74] feat(hydroflow_lang): initial flo implementation (#1585) Basic first pass implementation, without changing the current scheduler, no breaking behavior. Adds the flo syntax (`loop { ... }`), basic graph structure checks, nested loop blocks, and two basic windowing operators (`batch()` and `all_once()`). Next steps: - Needs scheduler changes: - Implement un-windowing operators - Implement windowing operator `repeat_n()` - - Pipeline flags: Needs checking of bounded vs unbounded for `batch()` vs `all_once()` - Needs checking that all inputs into a loop agree - State type negotiations (for `batch()` and handoffs (?) - don't always use `Vec`) - Dag performance optimizations (lots of things) --- .../tests/compile-fail/surface_loop_cycle.rs | 9 + .../compile-fail/surface_loop_cycle.stderr | 23 ++ .../surface_loop_missing_unwindowing.rs | 10 + .../surface_loop_missing_unwindowing.stderr | 5 + .../surface_loop_missing_windowing.rs | 9 + .../surface_loop_missing_windowing.stderr | 5 + .../surface_loop_multiple_window.rs | 11 + .../surface_loop_multiple_window.stderr | 5 + .../tests/compile-fail/surface_loop_source.rs | 8 + .../compile-fail/surface_loop_source.stderr | 5 + ...surface_loop__flo_nested@graphvis_dot.snap | 72 +++++++ ...ace_loop__flo_nested@graphvis_mermaid.snap | 59 ++++++ ...surface_loop__flo_syntax@graphvis_dot.snap | 63 ++++++ ...ace_loop__flo_syntax@graphvis_mermaid.snap | 53 +++++ ...scheduling__stratum_loop@graphvis_dot.snap | 42 ++-- ...duling__stratum_loop@graphvis_mermaid.snap | 36 ++-- hydroflow/tests/surface_loop.rs | 38 ++++ hydroflow/tests/surface_parser.rs | 13 ++ hydroflow/tests/surface_scheduling.rs | 4 +- ...egations_and_comments@datalog_program.snap | 2 +- ...tions_fold_keyed_expr@datalog_program.snap | 2 +- ...ore__tests__anti_join@datalog_program.snap | 2 +- ...e__tests__collect_vec@datalog_program.snap | 2 +- ..._core__tests__detuple@datalog_program.snap | 2 +- ...ts__detuple_then_flat@datalog_program.snap | 2 +- ...core__tests__expr_lhs@datalog_program.snap | 2 +- ...tests__expr_predicate@datalog_program.snap | 2 +- ...ts__flat_then_detuple@datalog_program.snap | 2 +- ..._core__tests__flatten@datalog_program.snap | 2 +- ...og_core__tests__index@datalog_program.snap | 2 +- ...ests__join_with_other@datalog_program.snap | 2 +- ...tests__join_with_self@datalog_program.snap | 2 +- ...__local_constraints@datalog_program-2.snap | 2 +- ...ts__local_constraints@datalog_program.snap | 2 +- ...alog_core__tests__max@datalog_program.snap | 2 +- ..._core__tests__max_all@datalog_program.snap | 2 +- ...ests__minimal_program@datalog_program.snap | 2 +- ..._tests__multi_detuple@datalog_program.snap | 2 +- ...multiple_contributors@datalog_program.snap | 2 +- ...s__non_copy_but_clone@datalog_program.snap | 2 +- ..._core__tests__persist@datalog_program.snap | 2 +- ...s__persist_uniqueness@datalog_program.snap | 2 +- ...__tests__send_to_node@datalog_program.snap | 2 +- ..._tests__simple_filter@datalog_program.snap | 2 +- ...single_column_program@datalog_program.snap | 2 +- ...s__transitive_closure@datalog_program.snap | 2 +- ..._triple_relation_join@datalog_program.snap | 2 +- ...ests__wildcard_fields@datalog_program.snap | 2 +- ...__wildcard_join_count@datalog_program.snap | 2 +- .../src/graph/flat_graph_builder.rs | 199 ++++++++++++++++-- .../src/graph/flat_to_partitioned.rs | 7 +- hydroflow_lang/src/graph/graph_algorithms.rs | 2 +- hydroflow_lang/src/graph/hydroflow_graph.rs | 68 +++++- hydroflow_lang/src/graph/mod.rs | 3 + .../src/graph/ops/_lattice_fold_batch.rs | 1 + .../src/graph/ops/_lattice_join_fused_join.rs | 1 + hydroflow_lang/src/graph/ops/all_once.rs | 9 + hydroflow_lang/src/graph/ops/anti_join.rs | 1 + .../src/graph/ops/anti_join_multiset.rs | 1 + hydroflow_lang/src/graph/ops/assert.rs | 1 + hydroflow_lang/src/graph/ops/assert_eq.rs | 1 + hydroflow_lang/src/graph/ops/batch.rs | 79 +++++++ hydroflow_lang/src/graph/ops/chain.rs | 1 + hydroflow_lang/src/graph/ops/cross_join.rs | 1 + .../src/graph/ops/cross_join_multiset.rs | 1 + .../src/graph/ops/cross_singleton.rs | 1 + hydroflow_lang/src/graph/ops/defer_signal.rs | 1 + hydroflow_lang/src/graph/ops/defer_tick.rs | 1 + .../src/graph/ops/defer_tick_lazy.rs | 1 + hydroflow_lang/src/graph/ops/demux.rs | 1 + hydroflow_lang/src/graph/ops/demux_enum.rs | 1 + hydroflow_lang/src/graph/ops/dest_file.rs | 1 + hydroflow_lang/src/graph/ops/dest_sink.rs | 1 + .../src/graph/ops/dest_sink_serde.rs | 1 + hydroflow_lang/src/graph/ops/difference.rs | 1 + .../src/graph/ops/difference_multiset.rs | 1 + hydroflow_lang/src/graph/ops/enumerate.rs | 1 + hydroflow_lang/src/graph/ops/filter.rs | 1 + hydroflow_lang/src/graph/ops/filter_map.rs | 1 + hydroflow_lang/src/graph/ops/flat_map.rs | 1 + hydroflow_lang/src/graph/ops/flatten.rs | 1 + hydroflow_lang/src/graph/ops/fold.rs | 1 + hydroflow_lang/src/graph/ops/fold_keyed.rs | 1 + hydroflow_lang/src/graph/ops/for_each.rs | 1 + hydroflow_lang/src/graph/ops/identity.rs | 1 + hydroflow_lang/src/graph/ops/initialize.rs | 1 + hydroflow_lang/src/graph/ops/inspect.rs | 1 + hydroflow_lang/src/graph/ops/join.rs | 1 + hydroflow_lang/src/graph/ops/join_fused.rs | 1 + .../src/graph/ops/join_fused_lhs.rs | 1 + .../src/graph/ops/join_fused_rhs.rs | 1 + hydroflow_lang/src/graph/ops/join_multiset.rs | 1 + .../src/graph/ops/lattice_bimorphism.rs | 1 + hydroflow_lang/src/graph/ops/lattice_fold.rs | 1 + .../src/graph/ops/lattice_reduce.rs | 1 + hydroflow_lang/src/graph/ops/map.rs | 1 + hydroflow_lang/src/graph/ops/mod.rs | 21 ++ .../src/graph/ops/multiset_delta.rs | 1 + hydroflow_lang/src/graph/ops/next_stratum.rs | 1 + hydroflow_lang/src/graph/ops/null.rs | 1 + hydroflow_lang/src/graph/ops/partition.rs | 1 + hydroflow_lang/src/graph/ops/persist.rs | 1 + hydroflow_lang/src/graph/ops/persist_mut.rs | 1 + .../src/graph/ops/persist_mut_keyed.rs | 1 + hydroflow_lang/src/graph/ops/py_udf.rs | 1 + hydroflow_lang/src/graph/ops/reduce.rs | 1 + hydroflow_lang/src/graph/ops/reduce_keyed.rs | 1 + hydroflow_lang/src/graph/ops/sort.rs | 1 + hydroflow_lang/src/graph/ops/sort_by_key.rs | 1 + hydroflow_lang/src/graph/ops/source_file.rs | 5 +- .../src/graph/ops/source_interval.rs | 5 +- hydroflow_lang/src/graph/ops/source_iter.rs | 4 +- hydroflow_lang/src/graph/ops/source_json.rs | 3 +- hydroflow_lang/src/graph/ops/source_stdin.rs | 5 +- hydroflow_lang/src/graph/ops/source_stream.rs | 5 +- .../src/graph/ops/source_stream_serde.rs | 5 +- hydroflow_lang/src/graph/ops/spin.rs | 1 + hydroflow_lang/src/graph/ops/state.rs | 1 + hydroflow_lang/src/graph/ops/state_by.rs | 1 + hydroflow_lang/src/graph/ops/tee.rs | 1 + hydroflow_lang/src/graph/ops/union.rs | 1 + hydroflow_lang/src/graph/ops/unique.rs | 1 + hydroflow_lang/src/graph/ops/unzip.rs | 1 + hydroflow_lang/src/graph/ops/zip.rs | 1 + hydroflow_lang/src/graph/ops/zip_longest.rs | 1 + hydroflow_lang/src/parse.rs | 89 ++++++-- 126 files changed, 983 insertions(+), 117 deletions(-) create mode 100644 hydroflow/tests/compile-fail/surface_loop_cycle.rs create mode 100644 hydroflow/tests/compile-fail/surface_loop_cycle.stderr create mode 100644 hydroflow/tests/compile-fail/surface_loop_missing_unwindowing.rs create mode 100644 hydroflow/tests/compile-fail/surface_loop_missing_unwindowing.stderr create mode 100644 hydroflow/tests/compile-fail/surface_loop_missing_windowing.rs create mode 100644 hydroflow/tests/compile-fail/surface_loop_missing_windowing.stderr create mode 100644 hydroflow/tests/compile-fail/surface_loop_multiple_window.rs create mode 100644 hydroflow/tests/compile-fail/surface_loop_multiple_window.stderr create mode 100644 hydroflow/tests/compile-fail/surface_loop_source.rs create mode 100644 hydroflow/tests/compile-fail/surface_loop_source.stderr create mode 100644 hydroflow/tests/snapshots/surface_loop__flo_nested@graphvis_dot.snap create mode 100644 hydroflow/tests/snapshots/surface_loop__flo_nested@graphvis_mermaid.snap create mode 100644 hydroflow/tests/snapshots/surface_loop__flo_syntax@graphvis_dot.snap create mode 100644 hydroflow/tests/snapshots/surface_loop__flo_syntax@graphvis_mermaid.snap create mode 100644 hydroflow/tests/surface_loop.rs create mode 100644 hydroflow_lang/src/graph/ops/all_once.rs create mode 100644 hydroflow_lang/src/graph/ops/batch.rs diff --git a/hydroflow/tests/compile-fail/surface_loop_cycle.rs b/hydroflow/tests/compile-fail/surface_loop_cycle.rs new file mode 100644 index 000000000000..0ddf384b4564 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_cycle.rs @@ -0,0 +1,9 @@ +fn main() { + let mut df = hydroflow::hydroflow_syntax! { + loop { + a = identity() -> identity() -> identity() -> identity(); + a -> a; + } + }; + df.run_available(); +} diff --git a/hydroflow/tests/compile-fail/surface_loop_cycle.stderr b/hydroflow/tests/compile-fail/surface_loop_cycle.stderr new file mode 100644 index 000000000000..dc056047cbf9 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_cycle.stderr @@ -0,0 +1,23 @@ +error: Operator forms an illegal cycle within a `loop { ... }` block (1/4). + --> tests/compile-fail/surface_loop_cycle.rs:4:31 + | +4 | a = identity() -> identity() -> identity() -> identity(); + | ^^^^^^^^^^ + +error: Operator forms an illegal cycle within a `loop { ... }` block (2/4). + --> tests/compile-fail/surface_loop_cycle.rs:4:45 + | +4 | a = identity() -> identity() -> identity() -> identity(); + | ^^^^^^^^^^ + +error: Operator forms an illegal cycle within a `loop { ... }` block (3/4). + --> tests/compile-fail/surface_loop_cycle.rs:4:59 + | +4 | a = identity() -> identity() -> identity() -> identity(); + | ^^^^^^^^^^ + +error: Operator forms an illegal cycle within a `loop { ... }` block (4/4). + --> tests/compile-fail/surface_loop_cycle.rs:4:17 + | +4 | a = identity() -> identity() -> identity() -> identity(); + | ^^^^^^^^^^ diff --git a/hydroflow/tests/compile-fail/surface_loop_missing_unwindowing.rs b/hydroflow/tests/compile-fail/surface_loop_missing_unwindowing.rs new file mode 100644 index 000000000000..808bfae7a0e2 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_missing_unwindowing.rs @@ -0,0 +1,10 @@ +fn main() { + let mut df = hydroflow::hydroflow_syntax! { + a = source_iter(0..10); + loop { + b = a -> batch(); + } + b -> null(); + }; + df.run_available(); +} diff --git a/hydroflow/tests/compile-fail/surface_loop_missing_unwindowing.stderr b/hydroflow/tests/compile-fail/surface_loop_missing_unwindowing.stderr new file mode 100644 index 000000000000..e0343ead3779 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_missing_unwindowing.stderr @@ -0,0 +1,5 @@ +error: Operator `null(...)` exiting a loop context must be an un-windowing operator, but is not. + --> tests/compile-fail/surface_loop_missing_unwindowing.rs:7:14 + | +7 | b -> null(); + | ^^^^^^ diff --git a/hydroflow/tests/compile-fail/surface_loop_missing_windowing.rs b/hydroflow/tests/compile-fail/surface_loop_missing_windowing.rs new file mode 100644 index 000000000000..2177d2ad0e8c --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_missing_windowing.rs @@ -0,0 +1,9 @@ +fn main() { + let mut df = hydroflow::hydroflow_syntax! { + a = source_iter(0..10); + loop { + a -> null(); + } + }; + df.run_available(); +} diff --git a/hydroflow/tests/compile-fail/surface_loop_missing_windowing.stderr b/hydroflow/tests/compile-fail/surface_loop_missing_windowing.stderr new file mode 100644 index 000000000000..1735ebd48911 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_missing_windowing.stderr @@ -0,0 +1,5 @@ +error: Operator `null(...)` entering a loop context must be a windowing operator, but is not. + --> tests/compile-fail/surface_loop_missing_windowing.rs:5:18 + | +5 | a -> null(); + | ^^^^^^ diff --git a/hydroflow/tests/compile-fail/surface_loop_multiple_window.rs b/hydroflow/tests/compile-fail/surface_loop_multiple_window.rs new file mode 100644 index 000000000000..eb0eb775bf29 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_multiple_window.rs @@ -0,0 +1,11 @@ +fn main() { + let mut df = hydroflow::hydroflow_syntax! { + a = source_iter(0..10); + loop { + loop { + a -> batch() -> null(); + } + } + }; + df.run_available(); +} diff --git a/hydroflow/tests/compile-fail/surface_loop_multiple_window.stderr b/hydroflow/tests/compile-fail/surface_loop_multiple_window.stderr new file mode 100644 index 000000000000..00a863b05482 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_multiple_window.stderr @@ -0,0 +1,5 @@ +error: Operator input edge may not cross multiple loop contexts. + --> tests/compile-fail/surface_loop_multiple_window.rs:6:22 + | +6 | a -> batch() -> null(); + | ^^^^^^^ diff --git a/hydroflow/tests/compile-fail/surface_loop_source.rs b/hydroflow/tests/compile-fail/surface_loop_source.rs new file mode 100644 index 000000000000..edea1a7ad958 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_source.rs @@ -0,0 +1,8 @@ +fn main() { + let mut df = hydroflow::hydroflow_syntax! { + loop { + source_iter(0..10) -> null(); + } + }; + df.run_available(); +} diff --git a/hydroflow/tests/compile-fail/surface_loop_source.stderr b/hydroflow/tests/compile-fail/surface_loop_source.stderr new file mode 100644 index 000000000000..34bbb210a144 --- /dev/null +++ b/hydroflow/tests/compile-fail/surface_loop_source.stderr @@ -0,0 +1,5 @@ +error: Source operator `source_iter(...)` must be at the root level, not within any `loop { ... }` contexts. + --> tests/compile-fail/surface_loop_source.rs:4:13 + | +4 | source_iter(0..10) -> null(); + | ^^^^^^^^^^^^^^^^^^ diff --git a/hydroflow/tests/snapshots/surface_loop__flo_nested@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_loop__flo_nested@graphvis_dot.snap new file mode 100644 index 000000000000..e566a3c1a17b --- /dev/null +++ b/hydroflow/tests/snapshots/surface_loop__flo_nested@graphvis_dot.snap @@ -0,0 +1,72 @@ +--- +source: hydroflow/tests/surface_loop.rs +expression: "df.meta_graph().unwrap().to_dot(& Default :: default())" +--- +digraph { + node [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace", style=filled]; + edge [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace"]; + n1v1 [label="(n1v1) source_iter([\"alice\", \"bob\"])", shape=invhouse, fillcolor="#88aaff"] + n2v1 [label="(n2v1) source_stream(iter_batches_stream(0..12, 3))", shape=invhouse, fillcolor="#88aaff"] + n3v1 [label="(n3v1) batch()", shape=invhouse, fillcolor="#88aaff"] + n4v1 [label="(n4v1) flatten()", shape=invhouse, fillcolor="#88aaff"] + n5v1 [label="(n5v1) batch()", shape=invhouse, fillcolor="#88aaff"] + n6v1 [label="(n6v1) flatten()", shape=invhouse, fillcolor="#88aaff"] + n7v1 [label="(n7v1) cross_join::<'static, 'tick>()", shape=invhouse, fillcolor="#88aaff"] + n8v1 [label="(n8v1) all_once()", shape=invhouse, fillcolor="#88aaff"] + n9v1 [label="(n9v1) for_each(|all| println!(\"{}: {:?}\", context.current_tick(), all))", shape=house, fillcolor="#ffff88"] + n10v1 [label="(n10v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n11v1 [label="(n11v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n12v1 [label="(n12v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n4v1 -> n7v1 [label="0"] + n3v1 -> n4v1 + n1v1 -> n10v1 + n6v1 -> n7v1 [label="1"] + n5v1 -> n6v1 + n2v1 -> n11v1 + n8v1 -> n9v1 + n7v1 -> n12v1 + n10v1 -> n3v1 + n11v1 -> n5v1 + n12v1 -> n8v1 [color=red] + subgraph "cluster n1v1" { + fillcolor="#dddddd" + style=filled + label = "sg_1v1\nstratum 0" + n1v1 + subgraph "cluster_sg_1v1_var_users" { + label="var users" + n1v1 + } + } + subgraph "cluster n2v1" { + fillcolor="#dddddd" + style=filled + label = "sg_2v1\nstratum 0" + n2v1 + subgraph "cluster_sg_2v1_var_messages" { + label="var messages" + n2v1 + } + } + subgraph "cluster n3v1" { + fillcolor="#dddddd" + style=filled + label = "sg_3v1\nstratum 0" + n3v1 + n4v1 + n5v1 + n6v1 + n7v1 + subgraph "cluster_sg_3v1_var_cp" { + label="var cp" + n7v1 + } + } + subgraph "cluster n4v1" { + fillcolor="#dddddd" + style=filled + label = "sg_4v1\nstratum 1" + n8v1 + n9v1 + } +} diff --git a/hydroflow/tests/snapshots/surface_loop__flo_nested@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_loop__flo_nested@graphvis_mermaid.snap new file mode 100644 index 000000000000..68f67faccaf8 --- /dev/null +++ b/hydroflow/tests/snapshots/surface_loop__flo_nested@graphvis_mermaid.snap @@ -0,0 +1,59 @@ +--- +source: hydroflow/tests/surface_loop.rs +expression: "df.meta_graph().unwrap().to_mermaid(& Default :: default())" +--- +%%{init:{'theme':'base','themeVariables':{'clusterBkg':'#ddd','clusterBorder':'#888'}}}%% +flowchart TD +classDef pullClass fill:#8af,stroke:#000,text-align:left,white-space:pre +classDef pushClass fill:#ff8,stroke:#000,text-align:left,white-space:pre +classDef otherClass fill:#fdc,stroke:#000,text-align:left,white-space:pre +linkStyle default stroke:#aaa +1v1[\"(1v1) source_iter(["alice", "bob"])"/]:::pullClass +2v1[\"(2v1) source_stream(iter_batches_stream(0..12, 3))"/]:::pullClass +3v1[\"(3v1) batch()"/]:::pullClass +4v1[\"(4v1) flatten()"/]:::pullClass +5v1[\"(5v1) batch()"/]:::pullClass +6v1[\"(6v1) flatten()"/]:::pullClass +7v1[\"(7v1) cross_join::<'static, 'tick>()"/]:::pullClass +8v1[\"(8v1) all_once()"/]:::pullClass +9v1[/"(9v1) for_each(|all| println!("{}: {:?}", context.current_tick(), all))"\]:::pushClass +10v1["(10v1) handoff"]:::otherClass +11v1["(11v1) handoff"]:::otherClass +12v1["(12v1) handoff"]:::otherClass +4v1-->|0|7v1 +3v1-->4v1 +1v1-->10v1 +6v1-->|1|7v1 +5v1-->6v1 +2v1-->11v1 +8v1-->9v1 +7v1-->12v1 +10v1-->3v1 +11v1-->5v1 +12v1--x8v1; linkStyle 10 stroke:red +subgraph sg_1v1 ["sg_1v1 stratum 0"] + 1v1 + subgraph sg_1v1_var_users ["var users"] + 1v1 + end +end +subgraph sg_2v1 ["sg_2v1 stratum 0"] + 2v1 + subgraph sg_2v1_var_messages ["var messages"] + 2v1 + end +end +subgraph sg_3v1 ["sg_3v1 stratum 0"] + 3v1 + 4v1 + 5v1 + 6v1 + 7v1 + subgraph sg_3v1_var_cp ["var cp"] + 7v1 + end +end +subgraph sg_4v1 ["sg_4v1 stratum 1"] + 8v1 + 9v1 +end diff --git a/hydroflow/tests/snapshots/surface_loop__flo_syntax@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_loop__flo_syntax@graphvis_dot.snap new file mode 100644 index 000000000000..57f118a3fee3 --- /dev/null +++ b/hydroflow/tests/snapshots/surface_loop__flo_syntax@graphvis_dot.snap @@ -0,0 +1,63 @@ +--- +source: hydroflow/tests/surface_loop.rs +expression: "df.meta_graph().unwrap().to_dot(& Default :: default())" +--- +digraph { + node [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace", style=filled]; + edge [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace"]; + n1v1 [label="(n1v1) source_iter([\"alice\", \"bob\"])", shape=invhouse, fillcolor="#88aaff"] + n2v1 [label="(n2v1) source_stream(iter_batches_stream(0..12, 3))", shape=invhouse, fillcolor="#88aaff"] + n3v1 [label="(n3v1) batch()", shape=invhouse, fillcolor="#88aaff"] + n4v1 [label="(n4v1) flatten()", shape=invhouse, fillcolor="#88aaff"] + n5v1 [label="(n5v1) batch()", shape=invhouse, fillcolor="#88aaff"] + n6v1 [label="(n6v1) flatten()", shape=invhouse, fillcolor="#88aaff"] + n7v1 [label="(n7v1) cross_join::<'static, 'tick>()", shape=invhouse, fillcolor="#88aaff"] + n8v1 [label="(n8v1) for_each(|(user, message)| {\l println!(\"{}: notify {} of {}\", context.current_tick(), user, message)\l})\l", shape=house, fillcolor="#ffff88"] + n9v1 [label="(n9v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n10v1 [label="(n10v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n4v1 -> n7v1 [label="0"] + n3v1 -> n4v1 + n1v1 -> n9v1 + n6v1 -> n7v1 [label="1"] + n5v1 -> n6v1 + n2v1 -> n10v1 + n7v1 -> n8v1 + n9v1 -> n3v1 + n10v1 -> n5v1 + subgraph "cluster n1v1" { + fillcolor="#dddddd" + style=filled + label = "sg_1v1\nstratum 0" + n1v1 + subgraph "cluster_sg_1v1_var_users" { + label="var users" + n1v1 + } + } + subgraph "cluster n2v1" { + fillcolor="#dddddd" + style=filled + label = "sg_2v1\nstratum 0" + n2v1 + subgraph "cluster_sg_2v1_var_messages" { + label="var messages" + n2v1 + } + } + subgraph "cluster n3v1" { + fillcolor="#dddddd" + style=filled + label = "sg_3v1\nstratum 0" + n3v1 + n4v1 + n5v1 + n6v1 + n7v1 + n8v1 + subgraph "cluster_sg_3v1_var_cp" { + label="var cp" + n7v1 + n8v1 + } + } +} diff --git a/hydroflow/tests/snapshots/surface_loop__flo_syntax@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_loop__flo_syntax@graphvis_mermaid.snap new file mode 100644 index 000000000000..3fdd6bd94591 --- /dev/null +++ b/hydroflow/tests/snapshots/surface_loop__flo_syntax@graphvis_mermaid.snap @@ -0,0 +1,53 @@ +--- +source: hydroflow/tests/surface_loop.rs +expression: "df.meta_graph().unwrap().to_mermaid(& Default :: default())" +--- +%%{init:{'theme':'base','themeVariables':{'clusterBkg':'#ddd','clusterBorder':'#888'}}}%% +flowchart TD +classDef pullClass fill:#8af,stroke:#000,text-align:left,white-space:pre +classDef pushClass fill:#ff8,stroke:#000,text-align:left,white-space:pre +classDef otherClass fill:#fdc,stroke:#000,text-align:left,white-space:pre +linkStyle default stroke:#aaa +1v1[\"(1v1) source_iter(["alice", "bob"])"/]:::pullClass +2v1[\"(2v1) source_stream(iter_batches_stream(0..12, 3))"/]:::pullClass +3v1[\"(3v1) batch()"/]:::pullClass +4v1[\"(4v1) flatten()"/]:::pullClass +5v1[\"(5v1) batch()"/]:::pullClass +6v1[\"(6v1) flatten()"/]:::pullClass +7v1[\"(7v1) cross_join::<'static, 'tick>()"/]:::pullClass +8v1[/"

    (8v1)
    for_each(|(user, message)| {
    println!("{}: notify {} of {}", context.current_tick(), user, message)
    })
    "\]:::pushClass +9v1["(9v1) handoff"]:::otherClass +10v1["(10v1) handoff"]:::otherClass +4v1-->|0|7v1 +3v1-->4v1 +1v1-->9v1 +6v1-->|1|7v1 +5v1-->6v1 +2v1-->10v1 +7v1-->8v1 +9v1-->3v1 +10v1-->5v1 +subgraph sg_1v1 ["sg_1v1 stratum 0"] + 1v1 + subgraph sg_1v1_var_users ["var users"] + 1v1 + end +end +subgraph sg_2v1 ["sg_2v1 stratum 0"] + 2v1 + subgraph sg_2v1_var_messages ["var messages"] + 2v1 + end +end +subgraph sg_3v1 ["sg_3v1 stratum 0"] + 3v1 + 4v1 + 5v1 + 6v1 + 7v1 + 8v1 + subgraph sg_3v1_var_cp ["var cp"] + 7v1 + 8v1 + end +end diff --git a/hydroflow/tests/snapshots/surface_scheduling__stratum_loop@graphvis_dot.snap b/hydroflow/tests/snapshots/surface_scheduling__stratum_loop@graphvis_dot.snap index 5e416ed49975..62351287df30 100644 --- a/hydroflow/tests/snapshots/surface_scheduling__stratum_loop@graphvis_dot.snap +++ b/hydroflow/tests/snapshots/surface_scheduling__stratum_loop@graphvis_dot.snap @@ -1,6 +1,6 @@ --- source: hydroflow/tests/surface_scheduling.rs -expression: "df.meta_graph().unwrap().to_dot(&Default::default())" +expression: "df.meta_graph().unwrap().to_dot(& Default :: default())" --- digraph { node [fontname="Monaco,Menlo,Consolas,"Droid Sans Mono",Inconsolata,"Courier New",monospace", style=filled]; @@ -9,41 +9,43 @@ digraph { n2v1 [label="(n2v1) union()", shape=invhouse, fillcolor="#88aaff"] n3v1 [label="(n3v1) tee()", shape=house, fillcolor="#ffff88"] n4v1 [label="(n4v1) map(|n| n + TickDuration::SINGLE_TICK)", shape=house, fillcolor="#ffff88"] - n5v1 [label="(n5v1) filter(|&n| n < TickInstant::new(10))", shape=invhouse, fillcolor="#88aaff"] + n5v1 [label="(n5v1) filter(|&n| n < TickInstant::new(10))", shape=house, fillcolor="#ffff88"] n6v1 [label="(n6v1) next_stratum()", shape=invhouse, fillcolor="#88aaff"] - n7v1 [label="(n7v1) for_each(|v| out_send.send(v).unwrap())", shape=house, fillcolor="#ffff88"] - n8v1 [label="(n8v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n7v1 [label="(n7v1) defer_tick()", shape=invhouse, fillcolor="#88aaff"] + n8v1 [label="(n8v1) for_each(|v| out_send.send(v).unwrap())", shape=house, fillcolor="#ffff88"] n9v1 [label="(n9v1) handoff", shape=parallelogram, fillcolor="#ddddff"] + n10v1 [label="(n10v1) handoff", shape=parallelogram, fillcolor="#ddddff"] n1v1 -> n2v1 n2v1 -> n3v1 - n6v1 -> n2v1 - n5v1 -> n8v1 - n4v1 -> n9v1 + n7v1 -> n2v1 + n6v1 -> n9v1 + n5v1 -> n10v1 + n4v1 -> n5v1 n3v1 -> n4v1 - n3v1 -> n7v1 - n8v1 -> n6v1 [color=red] - n9v1 -> n5v1 + n3v1 -> n8v1 + n9v1 -> n7v1 [color=red] + n10v1 -> n6v1 [color=red] subgraph "cluster n1v1" { fillcolor="#dddddd" style=filled label = "sg_1v1\nstratum 0" - n5v1 - } - subgraph "cluster n2v1" { - fillcolor="#dddddd" - style=filled - label = "sg_2v1\nstratum 1" n1v1 - n6v1 + n7v1 n2v1 n3v1 n4v1 - n7v1 - subgraph "cluster_sg_2v1_var_union_tee" { + n5v1 + n8v1 + subgraph "cluster_sg_1v1_var_union_tee" { label="var union_tee" n2v1 n3v1 } } + subgraph "cluster n2v1" { + fillcolor="#dddddd" + style=filled + label = "sg_2v1\nstratum 1" + n6v1 + } } - diff --git a/hydroflow/tests/snapshots/surface_scheduling__stratum_loop@graphvis_mermaid.snap b/hydroflow/tests/snapshots/surface_scheduling__stratum_loop@graphvis_mermaid.snap index b898992f1a1f..defafae56088 100644 --- a/hydroflow/tests/snapshots/surface_scheduling__stratum_loop@graphvis_mermaid.snap +++ b/hydroflow/tests/snapshots/surface_scheduling__stratum_loop@graphvis_mermaid.snap @@ -1,6 +1,6 @@ --- source: hydroflow/tests/surface_scheduling.rs -expression: "df.meta_graph().unwrap().to_mermaid(&Default::default())" +expression: "df.meta_graph().unwrap().to_mermaid(& Default :: default())" --- %%{init:{'theme':'base','themeVariables':{'clusterBkg':'#ddd','clusterBorder':'#888'}}}%% flowchart TD @@ -12,33 +12,35 @@ linkStyle default stroke:#aaa 2v1[\"(2v1) union()"/]:::pullClass 3v1[/"(3v1) tee()"\]:::pushClass 4v1[/"(4v1) map(|n| n + TickDuration::SINGLE_TICK)"\]:::pushClass -5v1[\"(5v1) filter(|&n| n < TickInstant::new(10))"/]:::pullClass +5v1[/"(5v1) filter(|&n| n < TickInstant::new(10))"\]:::pushClass 6v1[\"(6v1) next_stratum()"/]:::pullClass -7v1[/"(7v1) for_each(|v| out_send.send(v).unwrap())"\]:::pushClass -8v1["(8v1) handoff"]:::otherClass +7v1[\"(7v1) defer_tick()"/]:::pullClass +8v1[/"(8v1) for_each(|v| out_send.send(v).unwrap())"\]:::pushClass 9v1["(9v1) handoff"]:::otherClass +10v1["(10v1) handoff"]:::otherClass 1v1-->2v1 2v1-->3v1 -6v1-->2v1 -5v1-->8v1 -4v1-->9v1 +7v1-->2v1 +6v1-->9v1 +5v1-->10v1 +4v1-->5v1 3v1-->4v1 -3v1-->7v1 -8v1--x6v1; linkStyle 7 stroke:red -9v1-->5v1 +3v1-->8v1 +9v1--o7v1; linkStyle 8 stroke:red +10v1--x6v1; linkStyle 9 stroke:red subgraph sg_1v1 ["sg_1v1 stratum 0"] - 5v1 -end -subgraph sg_2v1 ["sg_2v1 stratum 1"] 1v1 - 6v1 + 7v1 2v1 3v1 4v1 - 7v1 - subgraph sg_2v1_var_union_tee ["var union_tee"] + 5v1 + 8v1 + subgraph sg_1v1_var_union_tee ["var union_tee"] 2v1 3v1 end end - +subgraph sg_2v1 ["sg_2v1 stratum 1"] + 6v1 +end diff --git a/hydroflow/tests/surface_loop.rs b/hydroflow/tests/surface_loop.rs new file mode 100644 index 000000000000..ec026fd9d8a3 --- /dev/null +++ b/hydroflow/tests/surface_loop.rs @@ -0,0 +1,38 @@ +use hydroflow::util::iter_batches_stream; +use hydroflow::{assert_graphvis_snapshots, hydroflow_syntax}; +use multiplatform_test::multiplatform_test; + +#[multiplatform_test] +pub fn test_flo_syntax() { + let mut df = hydroflow_syntax! { + users = source_iter(["alice", "bob"]); + messages = source_stream(iter_batches_stream(0..12, 3)); + loop { + // TODO(mingwei): cross_join type negotion should allow us to eliminate `flatten()`. + users -> batch() -> flatten() -> [0]cp; + messages -> batch() -> flatten() -> [1]cp; + cp = cross_join::<'static, 'tick>() -> for_each(|(user, message)| println!("{}: notify {} of {}", context.current_tick(), user, message)); + } + }; + assert_graphvis_snapshots!(df); + df.run_available(); +} + +#[multiplatform_test] +pub fn test_flo_nested() { + let mut df = hydroflow_syntax! { + users = source_iter(["alice", "bob"]); + messages = source_stream(iter_batches_stream(0..12, 3)); + loop { + // TODO(mingwei): cross_join type negotion should allow us to eliminate `flatten()`. + users -> batch() -> flatten() -> [0]cp; + messages -> batch() -> flatten() -> [1]cp; + cp = cross_join::<'static, 'tick>(); + loop { + cp -> all_once() -> for_each(|all| println!("{}: {:?}", context.current_tick(), all)); + } + } + }; + assert_graphvis_snapshots!(df); + df.run_available(); +} diff --git a/hydroflow/tests/surface_parser.rs b/hydroflow/tests/surface_parser.rs index ca0fc2c6a557..ad7a137dfd50 100644 --- a/hydroflow/tests/surface_parser.rs +++ b/hydroflow/tests/surface_parser.rs @@ -236,3 +236,16 @@ pub fn test_parser_forwardref_self_middle() { self_ref = map(|a: usize| a) -> [0]self_ref[1] -> map(|b: usize| b); }; } + +#[multiplatform_test] +pub fn test_flo_syntax() { + hydroflow_parser! { + users = source_stream(0..); + messages = source_stream(0..); + loop { + users -> batch() -> flatten() -> [0]cp; + messages -> batch() -> flatten() -> [1]cp; + cp = cross_join() -> for_each(|(user, message)| println!("notify {} of {}", user, message)); + } + } +} diff --git a/hydroflow/tests/surface_scheduling.rs b/hydroflow/tests/surface_scheduling.rs index 1bff6147ba27..ef7a8f72dcf8 100644 --- a/hydroflow/tests/surface_scheduling.rs +++ b/hydroflow/tests/surface_scheduling.rs @@ -13,7 +13,7 @@ pub fn test_stratum_loop() { let mut df = hydroflow_syntax! { source_iter([TickInstant::new(0)]) -> union_tee; union_tee = union() -> tee(); - union_tee -> map(|n| n + TickDuration::SINGLE_TICK) -> filter(|&n| n < TickInstant::new(10)) -> next_stratum() -> union_tee; + union_tee -> map(|n| n + TickDuration::SINGLE_TICK) -> filter(|&n| n < TickInstant::new(10)) -> next_stratum() -> defer_tick() -> union_tee; union_tee -> for_each(|v| out_send.send(v).unwrap()); }; assert_graphvis_snapshots!(df); @@ -35,7 +35,7 @@ pub fn test_stratum_loop() { &*hydroflow::util::collect_ready::, _>(&mut out_recv) ); assert_eq!( - (TickInstant::new(11), 0), + (TickInstant::new(10), 0), (df.current_tick(), df.current_stratum()) ); } diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__aggregations_and_comments@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__aggregations_and_comments@datalog_program.snap index ecb90385183f..7cae1cac97e2 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__aggregations_and_comments@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__aggregations_and_comments@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result2 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , ((row . 0) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (a . 0 . unwrap () . 1 , g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , (row . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (prev + val . 0) } else { Some (val . 0) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (a . 0 . unwrap () , g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , (row . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (prev) } else { Some (val . 0) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (a . 0 . unwrap () , g . 0 ,))\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Operator\":\"identity ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":24,\"version\":1}],\"version\":5},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":14,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":1,\"version\":3},{\"idx\":17,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":7,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":1,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":3},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":25,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1},{\"value\":2,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":\"ints\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result2_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":false,\"version\":1}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result2 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , ((row . 0) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (a . 0 . unwrap () . 1 , g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , (row . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (prev + val . 0) } else { Some (val . 0) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (a . 0 . unwrap () , g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , (row . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (prev) } else { Some (val . 0) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (a . 0 . unwrap () , g . 0 ,))\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Operator\":\"identity ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":24,\"version\":1}],\"version\":5},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":14,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":1,\"version\":3},{\"idx\":17,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":7,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":1,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":3},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":25,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1},{\"value\":2,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":\"ints\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result2_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":false,\"version\":1}]}", ); df.__assign_diagnostics("[]"); let (hoff_1v3_send, hoff_1v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__aggregations_fold_keyed_expr@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__aggregations_fold_keyed_expr@datalog_program.snap index 2571aa97ad2e..244fe6fcb3b6 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__aggregations_fold_keyed_expr@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__aggregations_fold_keyed_expr@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 % 2 ,) , (row . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (prev + val . 0) } else { Some (val . 0) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 , a . 0 . unwrap () ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 % 2 ,) , (row . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (prev + val . 0) } else { Some (val . 0) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 , a . 0 . unwrap () ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_6v3_send, hoff_6v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__anti_join@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__anti_join@datalog_program.snap index 7846d1ef1f36..5c182bc0857a 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__anti_join@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__anti_join@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints_1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints_2)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints_3)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , (_ ,))) | (kv . 0 . 0 , kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 ,) , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"anti_join ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , (_ , _ ,)) | (kv . 0 . 0 , kv . 1 . 0 , kv . 1 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ , _ ,) | ((_v . 0 ,) , (_v . 1 , _v . 2 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (_v . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ ,) | ((row . 1 , row . 2 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":5},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":3},{\"idx\":21,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":12,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":26,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":25,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":23,\"version\":1},{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1},{\"idx\":25,\"version\":1},{\"idx\":26,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_4\",\"version\":1},{\"value\":\"join_4\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints_1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints_2)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints_3)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , (_ ,))) | (kv . 0 . 0 , kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 ,) , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"anti_join ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , (_ , _ ,)) | (kv . 0 . 0 , kv . 1 . 0 , kv . 1 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ , _ ,) | ((_v . 0 ,) , (_v . 1 , _v . 2 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (_v . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ ,) | ((row . 1 , row . 2 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":5},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":3},{\"idx\":21,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":12,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":26,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":25,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":23,\"version\":1},{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1},{\"idx\":25,\"version\":1},{\"idx\":26,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_4\",\"version\":1},{\"value\":\"join_4\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_6v3_send, hoff_6v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__collect_vec@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__collect_vec@datalog_program.snap index 872c2f668cec..90dae240e971 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__collect_vec@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__collect_vec@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : (() , ((_ ,) , (_ ,))) | (kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | (() , ((row . 0 , row . 1) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , () , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev ; set . insert (val . 0) ; set }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; set }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () . into_iter () . collect :: < Vec < _ > > () ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : (() , ((_ ,) , (_ ,))) | (kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | (() , ((row . 0 , row . 1) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , () , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev ; set . insert (val . 0) ; set }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; set }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () . into_iter () . collect :: < Vec < _ > > () ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_9v3_send, hoff_9v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__detuple@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__detuple@datalog_program.snap index 1e92fc65bce3..a7191e5f2d54 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__detuple@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__detuple@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ ,) | (row_untuple . 0 . 0 , row_untuple . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ ,) | (row_untuple . 0 . 0 , row_untuple . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__detuple_then_flat@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__detuple_then_flat@datalog_program.snap index 2cbc8a5f4d24..6689588a52fa 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__detuple_then_flat@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__detuple_then_flat@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ ,) | (row_untuple . 0 . 0 , row_untuple . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"flat_map (| row : (_ , _ ,) | row . 0 . into_iter () . map (move | __flattened_element | (__flattened_element , :: std :: clone :: Clone :: clone (& row . 1) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"flat_map (| row : (_ , _ ,) | row . 1 . into_iter () . map (move | __flattened_element | (:: std :: clone :: Clone :: clone (& row . 0) , __flattened_element ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1},{\"value\":\"source_reader_0\",\"version\":1},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ ,) | (row_untuple . 0 . 0 , row_untuple . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"flat_map (| row : (_ , _ ,) | row . 0 . into_iter () . map (move | __flattened_element | (__flattened_element , :: std :: clone :: Clone :: clone (& row . 1) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"flat_map (| row : (_ , _ ,) | row . 1 . into_iter () . map (move | __flattened_element | (:: std :: clone :: Clone :: clone (& row . 0) , __flattened_element ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1},{\"value\":\"source_reader_0\",\"version\":1},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__expr_lhs@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__expr_lhs@datalog_program.snap index c4206cfd331f..cb69eb657cf6 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__expr_lhs@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__expr_lhs@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((123 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 + 123 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 . clone () + row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((123 - row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((123 % (row . 0 + 5) ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 * 5 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":1,\"version\":3},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":1,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"2\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"3\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"3\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"4\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"4\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"5\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"5\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":\"ints\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((123 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 + 123 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 . clone () + row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((123 - row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((123 % (row . 0 + 5) ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 * 5 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":1,\"version\":3},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":1,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"2\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"3\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"3\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"4\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"4\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"5\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"5\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":\"ints\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_1v3_send, hoff_1v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__expr_predicate@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__expr_predicate@datalog_program.snap index 8dff199f6780..88dec76ae23f 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__expr_predicate@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__expr_predicate@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ ,) | row . 0 == 0)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ ,) | row . 0 != 0)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((2 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ ,) | row . 0 - 1 == 0)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((3 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ ,) | row . 0 - 1 == 1 - 1)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((4 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":1,\"version\":3},{\"idx\":13,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":1,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"2\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"3\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"3\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":\"ints\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_1_filter\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_3_filter\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_5_filter\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_7_filter\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ ,) | row . 0 == 0)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ ,) | row . 0 != 0)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((2 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ ,) | row . 0 - 1 == 0)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((3 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ ,) | row . 0 - 1 == 1 - 1)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((4 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":1,\"version\":3},{\"idx\":13,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":1,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"2\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"3\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"3\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":\"ints\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_1_filter\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_3_filter\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_5_filter\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_7_filter\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_1v3_send, hoff_1v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__flat_then_detuple@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__flat_then_detuple@datalog_program.snap index 3a14f2488ed8..e4af75fc146c 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__flat_then_detuple@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__flat_then_detuple@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"flat_map (| row : (_ ,) | row . 0 . into_iter () . map (move | __flattened_element | (__flattened_element ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ ,) | (row_untuple . 0 . 0 , row_untuple . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"flat_map (| row : (_ ,) | row . 0 . into_iter () . map (move | __flattened_element | (__flattened_element ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ ,) | (row_untuple . 0 . 0 , row_untuple . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__flatten@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__flatten@datalog_program.snap index dbb1c6a05089..9ca19090821f 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__flatten@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__flatten@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"flat_map (| row : (_ , _ ,) | row . 1 . into_iter () . map (move | __flattened_element | (:: std :: clone :: Clone :: clone (& row . 0) , __flattened_element ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"flat_map (| row : (_ , _ ,) | row . 1 . into_iter () . map (move | __flattened_element | (:: std :: clone :: Clone :: clone (& row . 0) , __flattened_element ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__index@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__index@datalog_program.snap index efa067605866..4fba2d1f01a3 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__index@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__index@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result2 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result3 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result4 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result5 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ , _ ,) , _)) | (g . 0 , g . 1 , __enumerate_index ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 ,) , ((row . 1) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ ,) , _)) | (g . 0 , a . 0 . unwrap () . 1 , __enumerate_index ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ , _ ,) , _)) | (g . 0 , g . 1 , __enumerate_index ,))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 ,) , ((row . 1) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'static , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ ,) , _)) | (g . 0 , a . 0 . unwrap () . 1 , __enumerate_index ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ , _ ,) , _)) | (g . 0 , g . 1 , __enumerate_index ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":35,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":25,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":39,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":23,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":45,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":3},{\"idx\":20,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":49,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":3},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":52,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":15,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":12,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":41,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":27,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":28,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":29,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":30,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":31,\"version\":1},{\"idx\":32,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":31,\"version\":1}],\"version\":1},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":34,\"version\":1},{\"idx\":35,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":33,\"version\":1},{\"idx\":34,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":33,\"version\":1}],\"version\":1},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":38,\"version\":1},{\"idx\":39,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":37,\"version\":1},{\"idx\":38,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":36,\"version\":1},{\"idx\":21,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":36,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":21,\"version\":3},{\"idx\":37,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":40,\"version\":1},{\"idx\":16,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":40,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":3},{\"idx\":51,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":44,\"version\":1},{\"idx\":45,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":43,\"version\":1},{\"idx\":44,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":42,\"version\":1},{\"idx\":43,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":42,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":3},{\"idx\":47,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":48,\"version\":1},{\"idx\":49,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":47,\"version\":1},{\"idx\":48,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":46,\"version\":1},{\"idx\":13,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":46,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":16,\"version\":3},{\"idx\":41,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":51,\"version\":1},{\"idx\":52,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":50,\"version\":1},{\"idx\":10,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":50,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":25,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":33,\"version\":1},{\"idx\":34,\"version\":1},{\"idx\":35,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":27,\"version\":1},{\"idx\":36,\"version\":1},{\"idx\":40,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":37,\"version\":1},{\"idx\":38,\"version\":1},{\"idx\":39,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":28,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":41,\"version\":1},{\"idx\":22,\"version\":1},{\"idx\":23,\"version\":1},{\"idx\":24,\"version\":1},{\"idx\":42,\"version\":1},{\"idx\":43,\"version\":1},{\"idx\":44,\"version\":1},{\"idx\":45,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":29,\"version\":1},{\"idx\":46,\"version\":1},{\"idx\":50,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":47,\"version\":1},{\"idx\":48,\"version\":1},{\"idx\":49,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":30,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":51,\"version\":1},{\"idx\":52,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":31,\"version\":1},{\"idx\":32,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":2,\"version\":1},{\"value\":1,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":\"ints\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result4_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result5_insert\",\"version\":1},{\"value\":\"result5\",\"version\":1},{\"value\":\"result5\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_persisted_insert\",\"version\":1},{\"value\":\"ints_persisted\",\"version\":1},{\"value\":\"ints_persisted\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result2 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result3 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result4 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result5 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ , _ ,) , _)) | (g . 0 , g . 1 , __enumerate_index ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 ,) , ((row . 1) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ ,) , _)) | (g . 0 , a . 0 . unwrap () . 1 , __enumerate_index ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ , _ ,) , _)) | (g . 0 , g . 1 , __enumerate_index ,))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 ,) , ((row . 1) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'static , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ ,) , _)) | (g . 0 , a . 0 . unwrap () . 1 , __enumerate_index ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"enumerate :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (__enumerate_index , (g , a)) : (_ , ((_ , _ ,) , _)) | (g . 0 , g . 1 , __enumerate_index ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":35,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":25,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":39,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":23,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":45,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":3},{\"idx\":20,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":49,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":3},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":52,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":15,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":12,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":41,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":27,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":28,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":29,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":30,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":31,\"version\":1},{\"idx\":32,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":31,\"version\":1}],\"version\":1},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":34,\"version\":1},{\"idx\":35,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":33,\"version\":1},{\"idx\":34,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":33,\"version\":1}],\"version\":1},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":38,\"version\":1},{\"idx\":39,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":37,\"version\":1},{\"idx\":38,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":36,\"version\":1},{\"idx\":21,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":36,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":21,\"version\":3},{\"idx\":37,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":40,\"version\":1},{\"idx\":16,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":40,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":3},{\"idx\":51,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":44,\"version\":1},{\"idx\":45,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":43,\"version\":1},{\"idx\":44,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":42,\"version\":1},{\"idx\":43,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":42,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":3},{\"idx\":47,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":48,\"version\":1},{\"idx\":49,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":47,\"version\":1},{\"idx\":48,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":46,\"version\":1},{\"idx\":13,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":46,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":16,\"version\":3},{\"idx\":41,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":51,\"version\":1},{\"idx\":52,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":50,\"version\":1},{\"idx\":10,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":50,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":25,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":33,\"version\":1},{\"idx\":34,\"version\":1},{\"idx\":35,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":27,\"version\":1},{\"idx\":36,\"version\":1},{\"idx\":40,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":37,\"version\":1},{\"idx\":38,\"version\":1},{\"idx\":39,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":28,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":41,\"version\":1},{\"idx\":22,\"version\":1},{\"idx\":23,\"version\":1},{\"idx\":24,\"version\":1},{\"idx\":42,\"version\":1},{\"idx\":43,\"version\":1},{\"idx\":44,\"version\":1},{\"idx\":45,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":29,\"version\":1},{\"idx\":46,\"version\":1},{\"idx\":50,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":47,\"version\":1},{\"idx\":48,\"version\":1},{\"idx\":49,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":30,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":51,\"version\":1},{\"idx\":52,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":31,\"version\":1},{\"idx\":32,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":2,\"version\":1},{\"value\":1,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":\"ints\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result4_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result5_insert\",\"version\":1},{\"value\":\"result5\",\"version\":1},{\"value\":\"result5\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_persisted_insert\",\"version\":1},{\"value\":\"ints_persisted\",\"version\":1},{\"value\":\"ints_persisted\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_6v3_send, hoff_6v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__join_with_other@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__join_with_other@datalog_program.snap index 3a750740ca36..99a2e6d0dd0b 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__join_with_other@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__join_with_other@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (in1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ , _ ,) , (() , ())) | (kv . 0 . 0 , kv . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 , _v . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 , _v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (in1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ , _ ,) , (() , ())) | (kv . 0 . 0 , kv . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 , _v . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 , _v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_10v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__join_with_self@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__join_with_self@datalog_program.snap index 0b5cd7f8294d..cb8e9d725099 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__join_with_self@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__join_with_self@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ , _ ,) , (() , ())) | (kv . 0 . 0 , kv . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 , _v . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 , _v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":3},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":\"input\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ , _ ,) , (() , ())) | (kv . 0 . 0 , kv . 0 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 , _v . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 , _v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":3},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":\"input\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_4v3_send, hoff_4v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__local_constraints@datalog_program-2.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__local_constraints@datalog_program-2.snap index f9e805b2d63d..1bf96f5b575f 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__local_constraints@datalog_program-2.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__local_constraints@datalog_program-2.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ , _ , _ , _ ,) | row . 0 == row . 1 && row . 2 == row . 3)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ , _ ,) | ((row . 0 . clone () , row . 0 , row . 2 . clone () , row . 2 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ , _ , _ ,) , _) | (g . 0 , g . 1 , g . 2 , g . 3 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ , _ , _ , _ ,) | row . 0 == row . 1 && row . 2 == row . 3)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ , _ ,) | ((row . 0 . clone () , row . 0 , row . 2 . clone () , row . 2 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ , _ , _ ,) , _) | (g . 0 , g . 1 , g . 2 , g . 3 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__local_constraints@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__local_constraints@datalog_program.snap index 07759fdcbc84..c6c17b45f5db 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__local_constraints@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__local_constraints@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ , _ ,) | row . 0 == row . 1)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 . clone () , row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ , _ ,) | row . 0 == row . 1)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 . clone () , row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__max@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__max@datalog_program.snap index c3e12b920a4b..7db7f024c77a 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__max@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__max@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , (row . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (std :: cmp :: max (prev , val . 0)) } else { Some (val . 0) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (a . 0 . unwrap () , g . 0 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , (row . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , (_ ,) , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (std :: cmp :: max (prev , val . 0)) } else { Some (val . 0) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (a . 0 . unwrap () , g . 0 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_6v3_send, hoff_6v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__max_all@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__max_all@datalog_program.snap index 319c1fba5170..a0bfbfe795ce 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__max_all@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__max_all@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | (() , (row . 0 , row . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , () , (Option < _ > , Option < _ > ,) > (| | (None , None ,) , | old : & mut (Option < _ > , Option < _ > ,) , val : (_ , _ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (std :: cmp :: max (prev , val . 0)) } else { Some (val . 0) } ; old . 1 = if let Some (prev) = old . 1 . take () { Some (std :: cmp :: max (prev , val . 1)) } else { Some (val . 1) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () , a . 1 . unwrap () ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | (() , (row . 0 , row . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , () , (Option < _ > , Option < _ > ,) > (| | (None , None ,) , | old : & mut (Option < _ > , Option < _ > ,) , val : (_ , _ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (std :: cmp :: max (prev , val . 0)) } else { Some (val . 0) } ; old . 1 = if let Some (prev) = old . 1 . take () { Some (std :: cmp :: max (prev , val . 1)) } else { Some (val . 1) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () , a . 1 . unwrap () ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_6v3_send, hoff_6v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__minimal_program@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__minimal_program@datalog_program.snap index 0e148142fd48..7693f926774f 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__minimal_program@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__minimal_program@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 , row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 , row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__multi_detuple@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__multi_detuple@datalog_program.snap index 13d8c8d8c3b6..21e2d593d126 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__multi_detuple@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__multi_detuple@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ , _ ,) | (row_untuple . 0 . 0 , row_untuple . 0 . 1 , row_untuple . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ , _ , _ ,) | (row_untuple . 0 , row_untuple . 1 , row_untuple . 2 . 0 , row_untuple . 2 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ , _ ,) | ((row . 0 , row . 1 , row . 2 , row . 3 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ , _ , _ ,) , _) | (g . 0 , g . 1 , g . 2 , g . 3 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ , _ ,) | (row_untuple . 0 . 0 , row_untuple . 0 . 1 , row_untuple . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row_untuple : (_ , _ , _ ,) | (row_untuple . 0 , row_untuple . 1 , row_untuple . 2 . 0 , row_untuple . 2 . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ , _ ,) | ((row . 0 , row . 1 , row . 2 , row . 3 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ , _ , _ ,) , _) | (g . 0 , g . 1 , g . 2 , g . 3 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"source_reader_0\",\"version\":1},{\"value\":\"source_reader_0\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__multiple_contributors@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__multiple_contributors@datalog_program.snap index a60f67142010..30706f984277 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__multiple_contributors@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__multiple_contributors@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (in1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 , row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":7,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":7,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":\"out_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (in1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 , row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":7,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":7,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":\"out_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_10v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__non_copy_but_clone@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__non_copy_but_clone@datalog_program.snap index 83bdd91fba52..b3c4c5b04bc7 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__non_copy_but_clone@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__non_copy_but_clone@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (strings)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 . clone () , row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"strings_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (strings)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 . clone () , row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"strings_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__persist@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__persist@datalog_program.snap index 2f0da258c6ff..c1a1a5851b0f 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__persist@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__persist@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints2)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints3)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result2 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result3 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result4 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'static , 'static , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : (() , ((_ ,) , (_ ,))) | (kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'static , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : (() , ((_ , _ ,) , (_ ,))) | (kv . 1 . 0 . 0 , kv . 1 . 0 . 1 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | (() , (_v . 0 , _v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ ,) | ((row . 0 , row . 1 , row . 2 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ , _ ,) , _) | (g . 0 , g . 1 , g . 2 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"anti_join ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ()) | (kv . 0 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (_v . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":34,\"version\":1},{\"idx\":14,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":28,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":25,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":35,\"version\":1},{\"idx\":11,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":22,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":19,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":36,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":13,\"version\":3},{\"idx\":33,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":50,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":3},{\"idx\":31,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":58,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":19,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":63,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":22,\"version\":3},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":68,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":25,\"version\":3},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":61,\"version\":1},{\"idx\":27,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":28,\"version\":3},{\"idx\":3,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":65,\"version\":1},{\"idx\":30,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":31,\"version\":1},{\"idx\":32,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":30,\"version\":1},{\"idx\":31,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":33,\"version\":1},{\"idx\":16,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":32,\"version\":1},{\"idx\":13,\"version\":3}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":11,\"version\":3},{\"idx\":7,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":37,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":38,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":39,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":40,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":41,\"version\":1},{\"idx\":42,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":43,\"version\":1},{\"idx\":41,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":29,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":44,\"version\":1},{\"idx\":41,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":26,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":45,\"version\":1},{\"idx\":46,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":47,\"version\":1},{\"idx\":45,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":42,\"version\":1},{\"idx\":47,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":48,\"version\":1},{\"idx\":45,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":48,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":3},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":49,\"version\":1},{\"idx\":50,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":46,\"version\":1},{\"idx\":49,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":51,\"version\":1},{\"idx\":52,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":54,\"version\":1},{\"idx\":51,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":53,\"version\":1},{\"idx\":54,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":23,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":56,\"version\":1},{\"idx\":20,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":55,\"version\":1},{\"idx\":56,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":55,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":3},{\"idx\":65,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":57,\"version\":1},{\"idx\":58,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":52,\"version\":1},{\"idx\":57,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":26,\"version\":3},{\"idx\":44,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":60,\"version\":1},{\"idx\":61,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":59,\"version\":1},{\"idx\":60,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":59,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":20,\"version\":3},{\"idx\":51,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":62,\"version\":1},{\"idx\":63,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":27,\"version\":1},{\"idx\":62,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":29,\"version\":3},{\"idx\":43,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":64,\"version\":1},{\"idx\":17,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":64,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":3},{\"idx\":53,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":67,\"version\":1},{\"idx\":68,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":66,\"version\":1},{\"idx\":67,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":32,\"version\":1},{\"idx\":66,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"3\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":9,\"version\":1},\"version\":1},{\"value\":{\"idx\":10,\"version\":1},\"version\":1},{\"value\":{\"idx\":11,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":33,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":43,\"version\":1},{\"idx\":44,\"version\":1},{\"idx\":41,\"version\":1},{\"idx\":42,\"version\":1},{\"idx\":47,\"version\":1},{\"idx\":48,\"version\":1},{\"idx\":45,\"version\":1},{\"idx\":46,\"version\":1},{\"idx\":49,\"version\":1},{\"idx\":50,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":37,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":53,\"version\":1},{\"idx\":54,\"version\":1},{\"idx\":51,\"version\":1},{\"idx\":52,\"version\":1},{\"idx\":57,\"version\":1},{\"idx\":58,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":38,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":59,\"version\":1},{\"idx\":60,\"version\":1},{\"idx\":61,\"version\":1},{\"idx\":27,\"version\":1},{\"idx\":62,\"version\":1},{\"idx\":63,\"version\":1},{\"idx\":21,\"version\":1},{\"idx\":39,\"version\":1},{\"idx\":64,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":65,\"version\":1},{\"idx\":30,\"version\":1},{\"idx\":31,\"version\":1},{\"idx\":32,\"version\":1},{\"idx\":66,\"version\":1},{\"idx\":67,\"version\":1},{\"idx\":68,\"version\":1},{\"idx\":24,\"version\":1},{\"idx\":40,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":55,\"version\":1},{\"idx\":56,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":34,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":35,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":36,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":1,\"version\":1},{\"value\":2,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints2_insert\",\"version\":1},{\"value\":\"ints2\",\"version\":1},{\"value\":\"ints2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result4_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"intermediate_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"intermediate_persist_insert\",\"version\":1},{\"value\":\"intermediate_persist\",\"version\":1},{\"value\":\"intermediate_persist\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_4\",\"version\":1},{\"value\":\"join_4\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_7\",\"version\":1},{\"value\":\"join_7\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints2)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints3)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result2 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result3 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result4 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'static , 'static , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : (() , ((_ ,) , (_ ,))) | (kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'static , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : (() , ((_ , _ ,) , (_ ,))) | (kv . 1 . 0 . 0 , kv . 1 . 0 . 1 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | (() , (_v . 0 , _v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (() , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ ,) | ((row . 0 , row . 1 , row . 2 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ , _ ,) , _) | (g . 0 , g . 1 , g . 2 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"anti_join ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ()) | (kv . 0 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | (_v . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"persist :: < 'static > ()\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":34,\"version\":1},{\"idx\":14,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":28,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":25,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":35,\"version\":1},{\"idx\":11,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":22,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":19,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":36,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":13,\"version\":3},{\"idx\":33,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":50,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":3},{\"idx\":31,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":58,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":19,\"version\":3},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":63,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":22,\"version\":3},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":68,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":25,\"version\":3},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":61,\"version\":1},{\"idx\":27,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":28,\"version\":3},{\"idx\":3,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":65,\"version\":1},{\"idx\":30,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":31,\"version\":1},{\"idx\":32,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":30,\"version\":1},{\"idx\":31,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":33,\"version\":1},{\"idx\":16,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":32,\"version\":1},{\"idx\":13,\"version\":3}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":11,\"version\":3},{\"idx\":7,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":37,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":38,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":39,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":40,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":41,\"version\":1},{\"idx\":42,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":43,\"version\":1},{\"idx\":41,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":29,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":44,\"version\":1},{\"idx\":41,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":26,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":45,\"version\":1},{\"idx\":46,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":47,\"version\":1},{\"idx\":45,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":42,\"version\":1},{\"idx\":47,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":48,\"version\":1},{\"idx\":45,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":48,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":14,\"version\":3},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":49,\"version\":1},{\"idx\":50,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":46,\"version\":1},{\"idx\":49,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":51,\"version\":1},{\"idx\":52,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":54,\"version\":1},{\"idx\":51,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":53,\"version\":1},{\"idx\":54,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":23,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":56,\"version\":1},{\"idx\":20,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":55,\"version\":1},{\"idx\":56,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":55,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":3},{\"idx\":65,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":57,\"version\":1},{\"idx\":58,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":52,\"version\":1},{\"idx\":57,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":26,\"version\":3},{\"idx\":44,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":60,\"version\":1},{\"idx\":61,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":59,\"version\":1},{\"idx\":60,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":59,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":20,\"version\":3},{\"idx\":51,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":62,\"version\":1},{\"idx\":63,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":27,\"version\":1},{\"idx\":62,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":29,\"version\":3},{\"idx\":43,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":64,\"version\":1},{\"idx\":17,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":64,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":3},{\"idx\":53,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":67,\"version\":1},{\"idx\":68,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":66,\"version\":1},{\"idx\":67,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":32,\"version\":1},{\"idx\":66,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"2\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"3\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":9,\"version\":1},\"version\":1},{\"value\":{\"idx\":10,\"version\":1},\"version\":1},{\"value\":{\"idx\":11,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":8,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1},{\"value\":{\"idx\":7,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":33,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":43,\"version\":1},{\"idx\":44,\"version\":1},{\"idx\":41,\"version\":1},{\"idx\":42,\"version\":1},{\"idx\":47,\"version\":1},{\"idx\":48,\"version\":1},{\"idx\":45,\"version\":1},{\"idx\":46,\"version\":1},{\"idx\":49,\"version\":1},{\"idx\":50,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":37,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":53,\"version\":1},{\"idx\":54,\"version\":1},{\"idx\":51,\"version\":1},{\"idx\":52,\"version\":1},{\"idx\":57,\"version\":1},{\"idx\":58,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":38,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":59,\"version\":1},{\"idx\":60,\"version\":1},{\"idx\":61,\"version\":1},{\"idx\":27,\"version\":1},{\"idx\":62,\"version\":1},{\"idx\":63,\"version\":1},{\"idx\":21,\"version\":1},{\"idx\":39,\"version\":1},{\"idx\":64,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":65,\"version\":1},{\"idx\":30,\"version\":1},{\"idx\":31,\"version\":1},{\"idx\":32,\"version\":1},{\"idx\":66,\"version\":1},{\"idx\":67,\"version\":1},{\"idx\":68,\"version\":1},{\"idx\":24,\"version\":1},{\"idx\":40,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":55,\"version\":1},{\"idx\":56,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":34,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":35,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":36,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":1,\"version\":1},{\"value\":2,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints2_insert\",\"version\":1},{\"value\":\"ints2\",\"version\":1},{\"value\":\"ints2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result4_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"intermediate_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"intermediate_persist_insert\",\"version\":1},{\"value\":\"intermediate_persist\",\"version\":1},{\"value\":\"intermediate_persist\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_4\",\"version\":1},{\"value\":\"join_4\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_7\",\"version\":1},{\"value\":\"join_7\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_6v3_send, hoff_6v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__persist_uniqueness@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__persist_uniqueness@datalog_program.snap index ed9e278356b7..6ded593c5ed3 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__persist_uniqueness@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__persist_uniqueness@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | (() , ((row . 0) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'static , () , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":11,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":8,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":8,\"version\":3},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":11,\"version\":3},{\"idx\":3,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":7,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":17,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":2,\"version\":1},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"difference :: < 'tick , 'static > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"defer_tick ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | (() , ((row . 0) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'static , () , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":11,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":8,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":5},{\"value\":[{\"idx\":8,\"version\":3},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":11,\"version\":3},{\"idx\":3,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":7,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":17,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Path\":\"pos\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":5},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Path\":\"neg\"}],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":5,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":2,\"version\":1},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_6v3_send, hoff_6v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__send_to_node@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__send_to_node@datalog_program.snap index ae7b3312a109..d8e50a5296d1 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__send_to_node@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__send_to_node@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| (node , data) | async_send_result (node , data))\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (async_receive_result)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| v : (_ , _ ,) | (v . 1 , (v . 0 ,)))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_async_send\",\"version\":1},{\"value\":\"result_async_send\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (ints)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| (node , data) | async_send_result (node , data))\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (async_receive_result)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| v : (_ , _ ,) | (v . 1 , (v . 0 ,)))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":3}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result_async_send\",\"version\":1},{\"value\":\"result_async_send\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_12v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__simple_filter@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__simple_filter@datalog_program.snap index 7be3dad3e838..70f57da4ff37 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__simple_filter@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__simple_filter@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ , _ ,) | row . 0 > row . 1 && row . 1 == row . 0)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_1_filter\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"filter (| row : & (_ , _ ,) | row . 0 > row . 1 && row . 1 == row . 0)\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 0 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ ,) , _) | (g . 0 , g . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"predicate_1_filter\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_7v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__single_column_program@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__single_column_program@datalog_program.snap index fbbddeb2e483..ded1b15e735e 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__single_column_program@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__single_column_program@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (in1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , (() , ())) | (kv . 0 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (in1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , (() , ())) | (kv . 0 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_10v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__transitive_closure@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__transitive_closure@datalog_program.snap index ff630c42e379..53702adc0a27 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__transitive_closure@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__transitive_closure@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (edges)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (seed_reachable)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | reachable . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , (() , (_ ,))) | (kv . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":17,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":7,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":7,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"edges_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"seed_reachable_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"reachable_insert\",\"version\":1},{\"value\":\"reachable_insert\",\"version\":1},{\"value\":\"reachable\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_3\",\"version\":1},{\"value\":\"join_3\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"union ()\"},\"version\":1},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (edges)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (seed_reachable)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | reachable . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , (() , (_ ,))) | (kv . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | ((row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":17,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":7,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":7,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":16,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":16,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":7,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":12,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"edges_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"seed_reachable_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"reachable_insert\",\"version\":1},{\"value\":\"reachable_insert\",\"version\":1},{\"value\":\"reachable\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_3\",\"version\":1},{\"value\":\"join_3\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_6v3_send, hoff_6v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__triple_relation_join@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__triple_relation_join@datalog_program.snap index 969ec389b954..4257e5e0d620 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__triple_relation_join@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__triple_relation_join@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (in1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in2)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in3)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , (_ ,))) | (kv . 0 . 0 , kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 ,) , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ , _ ,) , (_ ,))) | (kv . 0 . 0 , kv . 1 . 0 . 0 , kv . 1 . 0 . 1 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ , _ ,) | ((_v . 2 ,) , (_v . 1 , _v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ , _ ,) | ((row . 3 , row . 0 , row . 2 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ , _ , _ ,) , _) | (g . 0 , g . 1 , g . 2 , g . 3 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":26,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":25,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":23,\"version\":1},{\"idx\":24,\"version\":1},{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1},{\"idx\":25,\"version\":1},{\"idx\":26,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_4\",\"version\":1},{\"value\":\"join_4\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"source_stream (in1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in2)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (in3)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , (_ ,))) | (kv . 0 . 0 , kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 ,) , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ , _ ,) , (_ ,))) | (kv . 0 . 0 , kv . 1 . 0 . 0 , kv . 1 . 0 . 1 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ , _ ,) | ((_v . 2 ,) , (_v . 1 , _v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ , _ ,) | ((row . 3 , row . 0 , row . 2 , row . 1 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ , _ , _ , _ ,) , _) | (g . 0 , g . 1 , g . 2 , g . 3 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":15,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":19,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":20,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":26,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":25,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":15,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":23,\"version\":1},{\"idx\":24,\"version\":1},{\"idx\":21,\"version\":1},{\"idx\":22,\"version\":1},{\"idx\":25,\"version\":1},{\"idx\":26,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in1_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"in3_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_4\",\"version\":1},{\"value\":\"join_4\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let mut sg_1v1_node_13v1_stream = { diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__wildcard_fields@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__wildcard_fields@datalog_program.snap index 277d0b9c27e3..7b2cbee6fe7c 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__wildcard_fields@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__wildcard_fields@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , (_ ,))) | (kv . 0 . 0 , kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 ,) , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":3},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":\"input\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":2},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (input)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | out . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , (_ ,))) | (kv . 0 . 0 , kv . 1 . 0 . 0 , kv . 1 . 1 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 1 ,) , (_v . 0 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ , _ ,) | ((row . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : ((_ ,) , _) | (g . 0 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":6,\"version\":3},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":null,\"version\":2},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":6,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":1},{\"idx\":9,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":4,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":3},{\"idx\":12,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":7,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":12,\"version\":1},{\"idx\":9,\"version\":1},{\"idx\":10,\"version\":1},{\"idx\":13,\"version\":1},{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"input_insert\",\"version\":1},{\"value\":\"input\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"out_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_4v3_send, hoff_4v3_recv) = df diff --git a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__wildcard_join_count@datalog_program.snap b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__wildcard_join_count@datalog_program.snap index 61ba10cfba10..505939142a53 100644 --- a/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__wildcard_join_count@datalog_program.snap +++ b/hydroflow_datalog_core/src/snapshots/hydroflow_datalog_core__tests__wildcard_join_count@datalog_program.snap @@ -9,7 +9,7 @@ fn main() { use hydroflow::{var_expr, var_args}; let mut df = hydroflow::scheduled::graph::Hydroflow::new(); df.__assign_meta_graph( - "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result2 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , ())) | (kv . 0 . 0 , kv . 1 . 0 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | (() , (() ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , () , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (prev + 1) } else { Some (1) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () ,))\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , ())) | (kv . 0 . 0 , kv . 1 . 0 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | (() , ((row . 0) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , () , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":6,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":20,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":30,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":3},{\"idx\":19,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":1,\"version\":3},{\"idx\":29,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":3},{\"idx\":27,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":12,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":7,\"version\":3},{\"idx\":26,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":10,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":25,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":7,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":27,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":1},{\"idx\":4,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":3},{\"idx\":22,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":29,\"version\":1},{\"idx\":30,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":28,\"version\":1},{\"idx\":1,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":28,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":6,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":23,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":29,\"version\":1},{\"idx\":30,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":27,\"version\":1},{\"idx\":24,\"version\":1},{\"idx\":25,\"version\":1},{\"idx\":28,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"ints2_insert\",\"version\":1},{\"value\":\"ints2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_5\",\"version\":1},{\"value\":\"join_5\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", + "{\"nodes\":[{\"value\":null,\"version\":0},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Operator\":\"tee ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"unique :: < 'tick > ()\"},\"version\":1},{\"value\":{\"Handoff\":{}},\"version\":3},{\"value\":{\"Operator\":\"source_stream (ints1)\"},\"version\":1},{\"value\":{\"Operator\":\"source_stream (ints2)\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"for_each (| v | result2 . send (v) . unwrap ())\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , ())) | (kv . 0 . 0 , kv . 1 . 0 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | (() , (() ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , () , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some (prev + 1) } else { Some (1) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () ,))\"},\"version\":1},{\"value\":{\"Operator\":\"join :: < 'tick , 'tick , hydroflow :: compiled :: pull :: HalfMultisetJoinState > ()\"},\"version\":1},{\"value\":{\"Operator\":\"map (| kv : ((_ ,) , ((_ ,) , ())) | (kv . 0 . 0 , kv . 1 . 0 . 0 ,))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ , _ ,) | ((_v . 0 ,) , (_v . 1 ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| _v : (_ ,) | ((_v . 0 ,) , ()))\"},\"version\":1},{\"value\":{\"Operator\":\"map (| row : (_ , _ ,) | (() , ((row . 0) ,)))\"},\"version\":1},{\"value\":{\"Operator\":\"fold_keyed :: < 'tick , () , (Option < _ > ,) > (| | (None ,) , | old : & mut (Option < _ > ,) , val : (_ ,) | { old . 0 = if let Some (prev) = old . 0 . take () { Some ({ let prev : (hydroflow :: rustc_hash :: FxHashSet < _ > , _) = prev ; let mut set : hydroflow :: rustc_hash :: FxHashSet < _ > = prev . 0 ; if set . insert (val . 0) { (set , prev . 1 + 1) } else { (set , prev . 1) } }) } else { Some ({ let mut set = hydroflow :: rustc_hash :: FxHashSet :: < _ > :: default () ; set . insert (val . 0) ; (set , 1) }) } ; })\"},\"version\":1},{\"value\":{\"Operator\":\"map (| (g , a) : (() , _) | (a . 0 . unwrap () . 1 ,))\"},\"version\":1}],\"graph\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":2,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":5,\"version\":1},{\"idx\":6,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":23,\"version\":1},{\"idx\":8,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":9,\"version\":3},{\"idx\":20,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":30,\"version\":1},{\"idx\":11,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":12,\"version\":3},{\"idx\":19,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":1,\"version\":3},{\"idx\":29,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":4,\"version\":3},{\"idx\":27,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":8,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":12,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":1},{\"idx\":9,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":7,\"version\":3},{\"idx\":26,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":23,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":21,\"version\":1},{\"idx\":10,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":18,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":24,\"version\":1},{\"idx\":25,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":3,\"version\":1},{\"idx\":7,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":27,\"version\":1},{\"idx\":24,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":6,\"version\":1},{\"idx\":4,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":10,\"version\":3},{\"idx\":22,\"version\":1}],\"version\":3},{\"value\":[{\"idx\":29,\"version\":1},{\"idx\":30,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":28,\"version\":1},{\"idx\":1,\"version\":3}],\"version\":3},{\"value\":[{\"idx\":25,\"version\":1},{\"idx\":28,\"version\":1}],\"version\":1}],\"ports\":[{\"value\":null,\"version\":0},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"0\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",{\"Int\":\"0\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",{\"Int\":\"1\"}],\"version\":1},{\"value\":[{\"Int\":\"1\"},\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1},{\"value\":[\"Elided\",\"Elided\"],\"version\":3},{\"value\":[\"Elided\",\"Elided\"],\"version\":1}],\"node_loops\":[{\"value\":null,\"version\":0}],\"loop_nodes\":[{\"value\":null,\"version\":0}],\"loop_parent\":[{\"value\":null,\"version\":0}],\"loop_children\":[{\"value\":null,\"version\":0}],\"node_subgraph\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":null,\"version\":0},{\"value\":{\"idx\":1,\"version\":1},\"version\":1},{\"value\":{\"idx\":2,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":5,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":3,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":6,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1},{\"value\":{\"idx\":4,\"version\":1},\"version\":1}],\"subgraph_nodes\":[{\"value\":null,\"version\":0},{\"value\":[{\"idx\":13,\"version\":1},{\"idx\":2,\"version\":1},{\"idx\":3,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":14,\"version\":1},{\"idx\":5,\"version\":1},{\"idx\":6,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":22,\"version\":1},{\"idx\":23,\"version\":1},{\"idx\":8,\"version\":1},{\"idx\":15,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":29,\"version\":1},{\"idx\":30,\"version\":1},{\"idx\":11,\"version\":1},{\"idx\":16,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":19,\"version\":1},{\"idx\":20,\"version\":1},{\"idx\":17,\"version\":1},{\"idx\":18,\"version\":1},{\"idx\":21,\"version\":1}],\"version\":1},{\"value\":[{\"idx\":26,\"version\":1},{\"idx\":27,\"version\":1},{\"idx\":24,\"version\":1},{\"idx\":25,\"version\":1},{\"idx\":28,\"version\":1}],\"version\":1}],\"subgraph_stratum\":[{\"value\":null,\"version\":0},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1},{\"value\":1,\"version\":1},{\"value\":1,\"version\":1},{\"value\":0,\"version\":1},{\"value\":0,\"version\":1}],\"node_singleton_references\":[{\"value\":null,\"version\":0},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1},{\"value\":[],\"version\":1}],\"node_varnames\":[{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"ints1_insert\",\"version\":1},{\"value\":\"ints1\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"ints2_insert\",\"version\":1},{\"value\":\"ints2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":\"result_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"result2_insert\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_2\",\"version\":1},{\"value\":\"join_2\",\"version\":1},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":null,\"version\":0},{\"value\":\"join_5\",\"version\":1},{\"value\":\"join_5\",\"version\":1}],\"subgraph_laziness\":[{\"value\":null,\"version\":0}]}", ); df.__assign_diagnostics("[]"); let (hoff_1v3_send, hoff_1v3_recv) = df diff --git a/hydroflow_lang/src/graph/flat_graph_builder.rs b/hydroflow_lang/src/graph/flat_graph_builder.rs index 2095b5768099..10c90a46c1a6 100644 --- a/hydroflow_lang/src/graph/flat_graph_builder.rs +++ b/hydroflow_lang/src/graph/flat_graph_builder.rs @@ -11,8 +11,11 @@ use quote::ToTokens; use syn::spanned::Spanned; use syn::{Error, Ident, ItemUse}; -use super::{GraphEdgeId, GraphNode, GraphNodeId, HydroflowGraph, PortIndexValue}; +use super::ops::defer_tick::DEFER_TICK; +use super::ops::FloType; +use super::{GraphEdgeId, GraphLoopId, GraphNode, GraphNodeId, HydroflowGraph, PortIndexValue}; use crate::diagnostic::{Diagnostic, Level}; +use crate::graph::graph_algorithms; use crate::graph::ops::{PortListSpec, RangeTrait}; use crate::parse::{HfCode, HfStatement, Operator, Pipeline}; use crate::pretty_span::PrettySpan; @@ -103,6 +106,7 @@ impl FlatGraphBuilder { import_expr: Span::call_site(), }, Some(Ident::new("input", Span::call_site())), + None, ), builder.flat_graph.insert_node( GraphNode::ModuleBoundary { @@ -110,6 +114,7 @@ impl FlatGraphBuilder { import_expr: Span::call_site(), }, Some(Ident::new("output", Span::call_site())), + None, ), )); builder.process_statements(input.statements); @@ -134,15 +139,24 @@ impl FlatGraphBuilder { (self.flat_graph, self.uses, self.diagnostics) } - /// Add a single [`HfStatement`] line to this `HydroflowGraph`. + /// Add a single [`HfStatement`] line to this `HydroflowGraph` in the root context. pub fn add_statement(&mut self, stmt: HfStatement) { + self.add_statement_with_loop(stmt, None) + } + + /// Add a single [`HfStatement`] line to this `HydroflowGraph` in the given loop context. + pub fn add_statement_with_loop( + &mut self, + stmt: HfStatement, + current_loop: Option, + ) { match stmt { HfStatement::Use(yuse) => { self.uses.push(yuse); } HfStatement::Named(named) => { let stmt_span = named.span(); - let ends = self.add_pipeline(named.pipeline, Some(&named.name)); + let ends = self.add_pipeline(named.pipeline, Some(&named.name), current_loop); match self.varname_ends.entry(named.name) { Entry::Vacant(vacant_entry) => { vacant_entry.insert(VarnameInfo::new(ends)); @@ -171,20 +185,32 @@ impl FlatGraphBuilder { } } HfStatement::Pipeline(pipeline_stmt) => { - let ends = self.add_pipeline(pipeline_stmt.pipeline, None); + let ends = self.add_pipeline(pipeline_stmt.pipeline, None, current_loop); Self::helper_check_unused_port(&mut self.diagnostics, &ends, true); Self::helper_check_unused_port(&mut self.diagnostics, &ends, false); } + HfStatement::Loop(loop_statement) => { + let inner_loop = self.flat_graph.insert_loop(current_loop); + for stmt in loop_statement.statements { + self.add_statement_with_loop(stmt, Some(inner_loop)); + } + } } } /// Helper: Add a pipeline, i.e. `a -> b -> c`. Return the input and output ends for it. - fn add_pipeline(&mut self, pipeline: Pipeline, current_varname: Option<&Ident>) -> Ends { + fn add_pipeline( + &mut self, + pipeline: Pipeline, + current_varname: Option<&Ident>, + current_loop: Option, + ) -> Ends { match pipeline { Pipeline::Paren(ported_pipeline_paren) => { let (inn_port, pipeline_paren, out_port) = PortIndexValue::from_ported(ported_pipeline_paren); - let og_ends = self.add_pipeline(*pipeline_paren.pipeline, current_varname); + let og_ends = + self.add_pipeline(*pipeline_paren.pipeline, current_varname, current_loop); Self::helper_combine_ends(&mut self.diagnostics, og_ends, inn_port, out_port) } Pipeline::Name(pipeline_name) => { @@ -222,8 +248,8 @@ impl FlatGraphBuilder { } Pipeline::Link(pipeline_link) => { // Add the nested LHS and RHS of this link. - let lhs_ends = self.add_pipeline(*pipeline_link.lhs, current_varname); - let rhs_ends = self.add_pipeline(*pipeline_link.rhs, current_varname); + let lhs_ends = self.add_pipeline(*pipeline_link.lhs, current_varname, current_loop); + let rhs_ends = self.add_pipeline(*pipeline_link.rhs, current_varname, current_loop); // Outer (first and last) ends. let outer_ends = Ends { @@ -240,9 +266,11 @@ impl FlatGraphBuilder { } Pipeline::Operator(operator) => { let op_span = Some(operator.span()); - let nid = self - .flat_graph - .insert_node(GraphNode::Operator(operator), current_varname.cloned()); + let nid = self.flat_graph.insert_node( + GraphNode::Operator(operator), + current_varname.cloned(), + current_loop, + ); Ends { inn: Some((PortIndexValue::Elided(op_span), GraphDet::Determined(nid))), out: Some((PortIndexValue::Elided(op_span), GraphDet::Determined(nid))), @@ -279,7 +307,7 @@ impl FlatGraphBuilder { self.diagnostics.push(Diagnostic::spanned( import.span(), Level::Error, - err.to_string(), + format!("Error in module: {}", err), )); return Ends { @@ -314,7 +342,7 @@ impl FlatGraphBuilder { match node { GraphNode::Operator(_) => { let varname = other.node_varname(other_node_id); - let new_id = self.flat_graph.insert_node(node.clone(), varname); + let new_id = self.flat_graph.insert_node(node.clone(), varname, None); node_mapping.insert(other_node_id, new_id); } GraphNode::ModuleBoundary { input, .. } => { @@ -324,6 +352,7 @@ impl FlatGraphBuilder { import_expr: parent_span, }, Some(Ident::new(&format!("module_{}", input), parent_span)), + None, ); node_mapping.insert(other_node_id, new_id); @@ -562,6 +591,7 @@ impl FlatGraphBuilder { self.make_operator_instances(); self.check_operator_errors(); self.warn_unused_port_indexing(); + self.check_loop_errors(); } /// Make `OperatorInstance`s for each operator node. @@ -888,4 +918,147 @@ impl FlatGraphBuilder { } } } + + /// Check for loop context-related errors. + fn check_loop_errors(&mut self) { + // All inputs must be declared in the root block. + for (node_id, node) in self.flat_graph.nodes() { + let Some(op_inst) = self.flat_graph.node_op_inst(node_id) else { + continue; + }; + let loop_id = self.flat_graph.node_loop(node_id); + + // Source operators must be at the top level. + if Some(FloType::Source) == op_inst.op_constraints.flo_type && loop_id.is_some() { + self.diagnostics.push(Diagnostic::spanned( + node.span(), + Level::Error, + format!( + "Source operator `{}(...)` must be at the root level, not within any `loop {{ ... }}` contexts.", + op_inst.op_constraints.name + ) + )); + } + } + + // Check windowing and un-windowing operators, for loop inputs and outputs respectively. + for (_edge_id, (pred_id, node_id)) in self.flat_graph.edges() { + let Some(op_inst) = self.flat_graph.node_op_inst(node_id) else { + continue; + }; + let flo_type = &op_inst.op_constraints.flo_type; + + let pred_loop_id = self.flat_graph.node_loop(pred_id); + let loop_id = self.flat_graph.node_loop(node_id); + + let span = self.flat_graph.node(node_id).span(); + + let (is_input, is_output) = { + let parent_pred_loop_id = + pred_loop_id.and_then(|lid| self.flat_graph.loop_parent(lid)); + let parent_loop_id = loop_id.and_then(|lid| self.flat_graph.loop_parent(lid)); + let is_same = pred_loop_id == loop_id; + let is_input = !is_same && parent_loop_id == pred_loop_id; + let is_output = !is_same && parent_pred_loop_id == loop_id; + if !(is_input || is_output || is_same) { + self.diagnostics.push(Diagnostic::spanned( + span, + Level::Error, + "Operator input edge may not cross multiple loop contexts.", + )); + continue; + } + (is_input, is_output) + }; + + match flo_type { + None => { + if is_input { + self.diagnostics.push(Diagnostic::spanned( + span, + Level::Error, + format!( + "Operator `{}(...)` entering a loop context must be a windowing operator, but is not.", + op_inst.op_constraints.name + ) + )); + } + if is_output { + self.diagnostics.push(Diagnostic::spanned( + span, + Level::Error, + format!( + "Operator `{}(...)` exiting a loop context must be an un-windowing operator, but is not.", + op_inst.op_constraints.name + ) + )); + } + } + Some(FloType::Windowing) => { + if !is_input { + self.diagnostics.push(Diagnostic::spanned( + span, + Level::Error, + format!( + "Windowing operator `{}(...)` must be the first input operator into a `loop {{ ... }} context.", + op_inst.op_constraints.name + ) + )); + } + } + Some(FloType::Unwindowing) => { + if !is_output { + self.diagnostics.push(Diagnostic::spanned( + span, + Level::Error, + format!( + "Un-windowing operator `{}(...)` must be the first output operator after exiting a `loop {{ ... }} context.", + op_inst.op_constraints.name + ) + )); + } + } + Some(FloType::Source) => { + // Handled above. + } + } + } + + // Must be a DAG (excluding `next_tick()` operators). + // TODO(mingwei): Nested loop blocks should count as a single node. + for (loop_id, loop_nodes) in self.flat_graph.loops() { + // Filter out `defer_tick()` operators. + let filter_defer_tick = |&node_id: &GraphNodeId| { + self.flat_graph + .node_op_inst(node_id) + .map(|op_inst| DEFER_TICK.name != op_inst.op_constraints.name) + .unwrap_or(true) + }; + + let topo_sort_result = graph_algorithms::topo_sort( + loop_nodes.iter().copied().filter(filter_defer_tick), + |dst| { + self.flat_graph + .node_predecessor_nodes(dst) + .filter(|&src| Some(loop_id) == self.flat_graph.node_loop(src)) + .filter(filter_defer_tick) + }, + ); + if let Err(cycle) = topo_sort_result { + let len = cycle.len(); + for (i, node_id) in cycle.into_iter().enumerate() { + let span = self.flat_graph.node(node_id).span(); + self.diagnostics.push(Diagnostic::spanned( + span, + Level::Error, + format!( + "Operator forms an illegal cycle within a `loop {{ ... }}` block ({}/{}).", + i + 1, + len + ), + )); + } + } + } + } } diff --git a/hydroflow_lang/src/graph/flat_to_partitioned.rs b/hydroflow_lang/src/graph/flat_to_partitioned.rs index da192516c2d9..418c6b4b0300 100644 --- a/hydroflow_lang/src/graph/flat_to_partitioned.rs +++ b/hydroflow_lang/src/graph/flat_to_partitioned.rs @@ -116,7 +116,7 @@ fn find_subgraph_unionfind( continue; } - // Ignore if would join stratum crossers (next edges). + // Do not connect stratum crossers (next edges). if barrier_crossers .iter_node_pairs(partitioned_graph) .any(|((x_src, x_dst), _)| { @@ -129,6 +129,11 @@ fn find_subgraph_unionfind( continue; } + // Do not connect across loop contexts. + if partitioned_graph.node_loop(src) != partitioned_graph.node_loop(dst) { + continue; + } + if can_connect_colorize(&mut node_color, src, dst) { // At this point we have selected this edge and its src & dst to be // within a single subgraph. diff --git a/hydroflow_lang/src/graph/graph_algorithms.rs b/hydroflow_lang/src/graph/graph_algorithms.rs index dfbaed243f39..478d1b2a63ed 100644 --- a/hydroflow_lang/src/graph/graph_algorithms.rs +++ b/hydroflow_lang/src/graph/graph_algorithms.rs @@ -3,7 +3,7 @@ use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet}; -/// Computers the topological sort of the nodes of a possibly cyclic graph by ordering strongly +/// Computes the topological sort of the nodes of a possibly cyclic graph by ordering strongly /// connected components together. pub fn topo_sort_scc( mut nodes_fn: NodesFn, diff --git a/hydroflow_lang/src/graph/hydroflow_graph.rs b/hydroflow_lang/src/graph/hydroflow_graph.rs index 18de444060d3..ac857f72cc5f 100644 --- a/hydroflow_lang/src/graph/hydroflow_graph.rs +++ b/hydroflow_lang/src/graph/hydroflow_graph.rs @@ -17,9 +17,9 @@ use super::ops::{ WriteContextArgs, OPERATORS, }; use super::{ - change_spans, get_operator_generics, Color, DiMulGraph, GraphEdgeId, GraphNode, GraphNodeId, - GraphSubgraphId, OperatorInstance, PortIndexValue, Varname, CONTEXT, HANDOFF_NODE_STR, - HYDROFLOW, MODULE_BOUNDARY_NODE_STR, + change_spans, get_operator_generics, Color, DiMulGraph, GraphEdgeId, GraphLoopId, GraphNode, + GraphNodeId, GraphSubgraphId, OperatorInstance, PortIndexValue, Varname, CONTEXT, + HANDOFF_NODE_STR, HYDROFLOW, MODULE_BOUNDARY_NODE_STR, }; use crate::diagnostic::{Diagnostic, Level}; use crate::pretty_span::{PrettyRowCol, PrettySpan}; @@ -47,6 +47,15 @@ pub struct HydroflowGraph { graph: DiMulGraph, /// Input and output port for each edge. ports: SecondaryMap, + + /// Which loop a node belongs to (or none for top-level). + node_loops: SecondaryMap, + loop_nodes: SlotMap>, + /// For the key loop, what is its parent (`None` for top-level). + loop_parent: SparseSecondaryMap, + /// For the key loop, what are its child loops. + loop_children: SecondaryMap>, + /// Which subgraph each node belongs to. node_subgraph: SecondaryMap, @@ -198,11 +207,20 @@ impl HydroflowGraph { } /// Insert a node, assigning the given varname. - pub fn insert_node(&mut self, node: GraphNode, varname_opt: Option) -> GraphNodeId { + pub fn insert_node( + &mut self, + node: GraphNode, + varname_opt: Option, + loop_opt: Option, + ) -> GraphNodeId { let node_id = self.nodes.insert(node); if let Some(varname) = varname_opt { self.node_varnames.insert(node_id, Varname(varname)); } + if let Some(loop_id) = loop_opt { + self.node_loops.insert(node_id, loop_id); + self.loop_nodes[loop_id].push(node_id); + } node_id } @@ -1521,6 +1539,48 @@ impl HydroflowGraph { } } +/// Loops +impl HydroflowGraph { + /// Iterator over all loop IDs. + pub fn loop_ids(&self) -> slotmap::basic::Keys<'_, GraphLoopId, Vec> { + self.loop_nodes.keys() + } + + /// Iterator over all loops, ID and members: `(GraphLoopId, Vec)`. + pub fn loops(&self) -> slotmap::basic::Iter<'_, GraphLoopId, Vec> { + self.loop_nodes.iter() + } + + /// Create a new loop context, with the given parent loop (or `None`). + pub fn insert_loop(&mut self, parent_loop: Option) -> GraphLoopId { + let loop_id = self.loop_nodes.insert(Vec::new()); + self.loop_children.insert(loop_id, Vec::new()); + if let Some(parent_loop) = parent_loop { + self.loop_parent.insert(loop_id, parent_loop); + self.loop_children + .get_mut(parent_loop) + .unwrap() + .push(loop_id); + } + loop_id + } + + /// Get a node's loop context (or `None` for root). + pub fn node_loop(&self, node_id: GraphNodeId) -> Option { + self.node_loops.get(node_id).copied() + } + + /// Get a loop context's parent loop context (or `None` for root). + pub fn loop_parent(&self, loop_id: GraphLoopId) -> Option { + self.loop_parent.get(loop_id).copied() + } + + /// Get a loop context's child loops. + pub fn loop_children(&self, loop_id: GraphLoopId) -> &Vec { + self.loop_children.get(loop_id).unwrap() + } +} + /// Configuration for writing graphs. #[derive(Clone, Debug, Default)] #[cfg_attr(feature = "clap-derive", derive(clap::Args))] diff --git a/hydroflow_lang/src/graph/mod.rs b/hydroflow_lang/src/graph/mod.rs index 5caa4971cde0..7cb82e22c8d9 100644 --- a/hydroflow_lang/src/graph/mod.rs +++ b/hydroflow_lang/src/graph/mod.rs @@ -45,6 +45,9 @@ new_key_type! { /// ID to identify a subgraph in [`HydroflowGraph`]. pub struct GraphSubgraphId; + + /// ID to identify a loop block in [`HydroflowGraph`]. + pub struct GraphLoopId; } /// Context identifier as a string. diff --git a/hydroflow_lang/src/graph/ops/_lattice_fold_batch.rs b/hydroflow_lang/src/graph/ops/_lattice_fold_batch.rs index d6f6f635fa21..4bdecb0c7858 100644 --- a/hydroflow_lang/src/graph/ops/_lattice_fold_batch.rs +++ b/hydroflow_lang/src/graph/ops/_lattice_fold_batch.rs @@ -42,6 +42,7 @@ pub const _LATTICE_FOLD_BATCH: OperatorConstraints = OperatorConstraints { num_args: 0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| PortListSpec::Fixed(parse_quote! { input, signal })), ports_out: None, input_delaytype_fn: |_| Some(DelayType::MonotoneAccum), diff --git a/hydroflow_lang/src/graph/ops/_lattice_join_fused_join.rs b/hydroflow_lang/src/graph/ops/_lattice_join_fused_join.rs index 7e945f0cc90c..f777acef489b 100644 --- a/hydroflow_lang/src/graph/ops/_lattice_join_fused_join.rs +++ b/hydroflow_lang/src/graph/ops/_lattice_join_fused_join.rs @@ -86,6 +86,7 @@ pub const _LATTICE_JOIN_FUSED_JOIN: OperatorConstraints = OperatorConstraints { type_args: &(2..=2), is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| Some(DelayType::MonotoneAccum), diff --git a/hydroflow_lang/src/graph/ops/all_once.rs b/hydroflow_lang/src/graph/ops/all_once.rs new file mode 100644 index 000000000000..c882a7cd3523 --- /dev/null +++ b/hydroflow_lang/src/graph/ops/all_once.rs @@ -0,0 +1,9 @@ +use super::{DelayType, OperatorConstraints}; + +// Same as batch, but with a stratum barrier. +/// TODO(mingwei): docs +pub const ALL_ONCE: OperatorConstraints = OperatorConstraints { + name: "all_once", + input_delaytype_fn: |_| Some(DelayType::Stratum), + ..super::batch::BATCH +}; diff --git a/hydroflow_lang/src/graph/ops/anti_join.rs b/hydroflow_lang/src/graph/ops/anti_join.rs index a81fcd48695a..c80f6384fd45 100644 --- a/hydroflow_lang/src/graph/ops/anti_join.rs +++ b/hydroflow_lang/src/graph/ops/anti_join.rs @@ -37,6 +37,7 @@ pub const ANTI_JOIN: OperatorConstraints = OperatorConstraints { // to prevent reading uncleared data if this subgraph doesn't run. // https://github.com/hydro-project/hydroflow/issues/1298 has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { pos, neg })), ports_out: None, input_delaytype_fn: |idx| match idx { diff --git a/hydroflow_lang/src/graph/ops/anti_join_multiset.rs b/hydroflow_lang/src/graph/ops/anti_join_multiset.rs index cf9a1e9441d4..0c37c202d48a 100644 --- a/hydroflow_lang/src/graph/ops/anti_join_multiset.rs +++ b/hydroflow_lang/src/graph/ops/anti_join_multiset.rs @@ -37,6 +37,7 @@ pub const ANTI_JOIN_MULTISET: OperatorConstraints = OperatorConstraints { // to prevent reading uncleared data if this subgraph doesn't run. // https://github.com/hydro-project/hydroflow/issues/1298 has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { pos, neg })), ports_out: None, input_delaytype_fn: |idx| match idx { diff --git a/hydroflow_lang/src/graph/ops/assert.rs b/hydroflow_lang/src/graph/ops/assert.rs index af3afeee9867..de859d5dddd7 100644 --- a/hydroflow_lang/src/graph/ops/assert.rs +++ b/hydroflow_lang/src/graph/ops/assert.rs @@ -26,6 +26,7 @@ pub const ASSERT: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/assert_eq.rs b/hydroflow_lang/src/graph/ops/assert_eq.rs index 0aca9dbd257f..2d08e58fcc70 100644 --- a/hydroflow_lang/src/graph/ops/assert_eq.rs +++ b/hydroflow_lang/src/graph/ops/assert_eq.rs @@ -37,6 +37,7 @@ pub const ASSERT_EQ: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/batch.rs b/hydroflow_lang/src/graph/ops/batch.rs new file mode 100644 index 000000000000..faccafdbd0c7 --- /dev/null +++ b/hydroflow_lang/src/graph/ops/batch.rs @@ -0,0 +1,79 @@ +use quote::quote_spanned; + +use super::{ + FloType, OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, RANGE_0, + RANGE_1, +}; + +/// TODO(mingwei): docs +pub const BATCH: OperatorConstraints = OperatorConstraints { + name: "batch", + categories: &[OperatorCategory::Fold, OperatorCategory::Windowing], + hard_range_inn: RANGE_1, + soft_range_inn: RANGE_1, + hard_range_out: &(0..=1), + soft_range_out: &(0..=1), + num_args: 0, + persistence_args: RANGE_0, + type_args: RANGE_0, + is_external_input: false, + has_singleton_output: true, + flo_type: Some(FloType::Windowing), + ports_inn: None, + ports_out: None, + input_delaytype_fn: |_| None, + write_fn: |wc @ &WriteContextArgs { + root, + context, + hydroflow, + op_span, + ident, + is_pull, + inputs, + outputs, + singleton_output_ident, + .. + }, + _diagnostics| { + let write_prologue = quote_spanned! {op_span=> + #[allow(clippy::redundant_closure_call)] + let #singleton_output_ident = #hydroflow.add_state( + ::std::cell::RefCell::new(::std::vec::Vec::new()) + ); + + // TODO(mingwei): Is this needed? + // Reset the value to the initializer fn if it is a new tick. + #hydroflow.set_state_tick_hook(#singleton_output_ident, move |rcell| { rcell.take(); }); + }; + + let vec_ident = wc.make_ident("vec"); + + let write_iterator = if is_pull { + // Pull. + let input = &inputs[0]; + quote_spanned! {op_span=> + let mut #vec_ident = #context.state_ref(#singleton_output_ident).borrow_mut(); + *#vec_ident = #input.collect::<::std::vec::Vec<_>>(); + let #ident = ::std::iter::once(::std::clone::Clone::clone(&*#vec_ident)); + } + } else if let Some(_output) = outputs.first() { + // Push with output. + // TODO(mingwei): Not supported - cannot tell EOS for pusherators. + panic!("Should not happen - batch must be at ingress to a loop, therefore ingress to a subgraph, so would be pull-based."); + } else { + // Push with no output. + quote_spanned! {op_span=> + let mut #vec_ident = #context.state_ref(#singleton_output_ident).borrow_mut(); + let #ident = #root::pusherator::for_each::ForEach::new(|item| { + ::std::vec::Vec::push(#vec_ident, item); + }); + } + }; + + Ok(OperatorWriteOutput { + write_prologue, + write_iterator, + ..Default::default() + }) + }, +}; diff --git a/hydroflow_lang/src/graph/ops/chain.rs b/hydroflow_lang/src/graph/ops/chain.rs index 96006fc396af..3ff9c90fdec4 100644 --- a/hydroflow_lang/src/graph/ops/chain.rs +++ b/hydroflow_lang/src/graph/ops/chain.rs @@ -30,6 +30,7 @@ pub const CHAIN: OperatorConstraints = OperatorConstraints { num_args: 0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |idx| match idx { diff --git a/hydroflow_lang/src/graph/ops/cross_join.rs b/hydroflow_lang/src/graph/ops/cross_join.rs index fe4c2aeed80f..c22d0225bc7a 100644 --- a/hydroflow_lang/src/graph/ops/cross_join.rs +++ b/hydroflow_lang/src/graph/ops/cross_join.rs @@ -45,6 +45,7 @@ pub const CROSS_JOIN: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/cross_join_multiset.rs b/hydroflow_lang/src/graph/ops/cross_join_multiset.rs index e050f2818c3e..5708800bcb28 100644 --- a/hydroflow_lang/src/graph/ops/cross_join_multiset.rs +++ b/hydroflow_lang/src/graph/ops/cross_join_multiset.rs @@ -34,6 +34,7 @@ pub const CROSS_JOIN_MULTISET: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/cross_singleton.rs b/hydroflow_lang/src/graph/ops/cross_singleton.rs index ee9e2c8def78..6d4b3757e667 100644 --- a/hydroflow_lang/src/graph/ops/cross_singleton.rs +++ b/hydroflow_lang/src/graph/ops/cross_singleton.rs @@ -39,6 +39,7 @@ pub const CROSS_SINGLETON: OperatorConstraints = OperatorConstraints { num_args: 0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { input, single })), ports_out: None, input_delaytype_fn: |idx| match idx { diff --git a/hydroflow_lang/src/graph/ops/defer_signal.rs b/hydroflow_lang/src/graph/ops/defer_signal.rs index b8e6459cb233..2adb2b4a993d 100644 --- a/hydroflow_lang/src/graph/ops/defer_signal.rs +++ b/hydroflow_lang/src/graph/ops/defer_signal.rs @@ -34,6 +34,7 @@ pub const DEFER_SIGNAL: OperatorConstraints = OperatorConstraints { num_args: 0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { input, signal })), ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/defer_tick.rs b/hydroflow_lang/src/graph/ops/defer_tick.rs index fa25f1ee1a84..42c3d92ba8d8 100644 --- a/hydroflow_lang/src/graph/ops/defer_tick.rs +++ b/hydroflow_lang/src/graph/ops/defer_tick.rs @@ -72,6 +72,7 @@ pub const DEFER_TICK: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Tick), diff --git a/hydroflow_lang/src/graph/ops/defer_tick_lazy.rs b/hydroflow_lang/src/graph/ops/defer_tick_lazy.rs index 8417d9ae9a92..96c505f19c7b 100644 --- a/hydroflow_lang/src/graph/ops/defer_tick_lazy.rs +++ b/hydroflow_lang/src/graph/ops/defer_tick_lazy.rs @@ -17,6 +17,7 @@ pub const DEFER_TICK_LAZY: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::TickLazy), diff --git a/hydroflow_lang/src/graph/ops/demux.rs b/hydroflow_lang/src/graph/ops/demux.rs index b96fe44df4c5..bfedf7a9b8cc 100644 --- a/hydroflow_lang/src/graph/ops/demux.rs +++ b/hydroflow_lang/src/graph/ops/demux.rs @@ -53,6 +53,7 @@ pub const DEMUX: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: Some(|| PortListSpec::Variadic), input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/demux_enum.rs b/hydroflow_lang/src/graph/ops/demux_enum.rs index 2d3ea6e9e501..8b612c243539 100644 --- a/hydroflow_lang/src/graph/ops/demux_enum.rs +++ b/hydroflow_lang/src/graph/ops/demux_enum.rs @@ -52,6 +52,7 @@ pub const DEMUX_ENUM: OperatorConstraints = OperatorConstraints { type_args: RANGE_1, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: Some(|| PortListSpec::Variadic), input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/dest_file.rs b/hydroflow_lang/src/graph/ops/dest_file.rs index f0190bfd07f5..0067cd53242d 100644 --- a/hydroflow_lang/src/graph/ops/dest_file.rs +++ b/hydroflow_lang/src/graph/ops/dest_file.rs @@ -32,6 +32,7 @@ pub const DEST_FILE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/dest_sink.rs b/hydroflow_lang/src/graph/ops/dest_sink.rs index 7493acee85b9..ee2f5b38d610 100644 --- a/hydroflow_lang/src/graph/ops/dest_sink.rs +++ b/hydroflow_lang/src/graph/ops/dest_sink.rs @@ -93,6 +93,7 @@ pub const DEST_SINK: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/dest_sink_serde.rs b/hydroflow_lang/src/graph/ops/dest_sink_serde.rs index a517655ae88e..78b8a7ceb5fb 100644 --- a/hydroflow_lang/src/graph/ops/dest_sink_serde.rs +++ b/hydroflow_lang/src/graph/ops/dest_sink_serde.rs @@ -35,6 +35,7 @@ pub const DEST_SINK_SERDE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/difference.rs b/hydroflow_lang/src/graph/ops/difference.rs index f626e86c5658..e2e8e83d2176 100644 --- a/hydroflow_lang/src/graph/ops/difference.rs +++ b/hydroflow_lang/src/graph/ops/difference.rs @@ -34,6 +34,7 @@ pub const DIFFERENCE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { pos, neg })), ports_out: None, input_delaytype_fn: |idx| match idx { diff --git a/hydroflow_lang/src/graph/ops/difference_multiset.rs b/hydroflow_lang/src/graph/ops/difference_multiset.rs index 8c87e9991846..603ffe05f3de 100644 --- a/hydroflow_lang/src/graph/ops/difference_multiset.rs +++ b/hydroflow_lang/src/graph/ops/difference_multiset.rs @@ -35,6 +35,7 @@ pub const DIFFERENCE_MULTISET: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { pos, neg })), ports_out: None, input_delaytype_fn: |idx| match idx { diff --git a/hydroflow_lang/src/graph/ops/enumerate.rs b/hydroflow_lang/src/graph/ops/enumerate.rs index e4ede20d40e7..c371870afc69 100644 --- a/hydroflow_lang/src/graph/ops/enumerate.rs +++ b/hydroflow_lang/src/graph/ops/enumerate.rs @@ -32,6 +32,7 @@ pub const ENUMERATE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/filter.rs b/hydroflow_lang/src/graph/ops/filter.rs index eea8150e1810..bb2906f93fe7 100644 --- a/hydroflow_lang/src/graph/ops/filter.rs +++ b/hydroflow_lang/src/graph/ops/filter.rs @@ -30,6 +30,7 @@ pub const FILTER: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/filter_map.rs b/hydroflow_lang/src/graph/ops/filter_map.rs index 0eb9a9d2bdfd..19f912849b1e 100644 --- a/hydroflow_lang/src/graph/ops/filter_map.rs +++ b/hydroflow_lang/src/graph/ops/filter_map.rs @@ -28,6 +28,7 @@ pub const FILTER_MAP: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/flat_map.rs b/hydroflow_lang/src/graph/ops/flat_map.rs index 94de32f24aaf..85aa66fb7ce5 100644 --- a/hydroflow_lang/src/graph/ops/flat_map.rs +++ b/hydroflow_lang/src/graph/ops/flat_map.rs @@ -32,6 +32,7 @@ pub const FLAT_MAP: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/flatten.rs b/hydroflow_lang/src/graph/ops/flatten.rs index 625a71d43195..533884da6570 100644 --- a/hydroflow_lang/src/graph/ops/flatten.rs +++ b/hydroflow_lang/src/graph/ops/flatten.rs @@ -27,6 +27,7 @@ pub const FLATTEN: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/fold.rs b/hydroflow_lang/src/graph/ops/fold.rs index 2cef16c6f2e8..7072b08c2a68 100644 --- a/hydroflow_lang/src/graph/ops/fold.rs +++ b/hydroflow_lang/src/graph/ops/fold.rs @@ -45,6 +45,7 @@ pub const FOLD: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: true, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/fold_keyed.rs b/hydroflow_lang/src/graph/ops/fold_keyed.rs index c4572cae6b70..f91e3d2deadc 100644 --- a/hydroflow_lang/src/graph/ops/fold_keyed.rs +++ b/hydroflow_lang/src/graph/ops/fold_keyed.rs @@ -80,6 +80,7 @@ pub const FOLD_KEYED: OperatorConstraints = OperatorConstraints { // to prevent reading uncleared data if this subgraph doesn't run. // https://github.com/hydro-project/hydroflow/issues/1298 has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/for_each.rs b/hydroflow_lang/src/graph/ops/for_each.rs index 601b079e874e..e087aa8eb149 100644 --- a/hydroflow_lang/src/graph/ops/for_each.rs +++ b/hydroflow_lang/src/graph/ops/for_each.rs @@ -30,6 +30,7 @@ pub const FOR_EACH: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/identity.rs b/hydroflow_lang/src/graph/ops/identity.rs index a55bab030c63..ffc1f44a4fcd 100644 --- a/hydroflow_lang/src/graph/ops/identity.rs +++ b/hydroflow_lang/src/graph/ops/identity.rs @@ -33,6 +33,7 @@ pub const IDENTITY: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/initialize.rs b/hydroflow_lang/src/graph/ops/initialize.rs index 877d655a121a..e7dbe0834658 100644 --- a/hydroflow_lang/src/graph/ops/initialize.rs +++ b/hydroflow_lang/src/graph/ops/initialize.rs @@ -26,6 +26,7 @@ pub const INITIALIZE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/inspect.rs b/hydroflow_lang/src/graph/ops/inspect.rs index d99576a703c0..bdf888d92a2d 100644 --- a/hydroflow_lang/src/graph/ops/inspect.rs +++ b/hydroflow_lang/src/graph/ops/inspect.rs @@ -31,6 +31,7 @@ pub const INSPECT: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/join.rs b/hydroflow_lang/src/graph/ops/join.rs index 874d9ceaa118..764dc9b8ea34 100644 --- a/hydroflow_lang/src/graph/ops/join.rs +++ b/hydroflow_lang/src/graph/ops/join.rs @@ -91,6 +91,7 @@ pub const JOIN: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/join_fused.rs b/hydroflow_lang/src/graph/ops/join_fused.rs index e7166086c53e..867310d674a1 100644 --- a/hydroflow_lang/src/graph/ops/join_fused.rs +++ b/hydroflow_lang/src/graph/ops/join_fused.rs @@ -100,6 +100,7 @@ pub const JOIN_FUSED: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/join_fused_lhs.rs b/hydroflow_lang/src/graph/ops/join_fused_lhs.rs index 9ecefbc730c3..198e0bdb0806 100644 --- a/hydroflow_lang/src/graph/ops/join_fused_lhs.rs +++ b/hydroflow_lang/src/graph/ops/join_fused_lhs.rs @@ -34,6 +34,7 @@ pub const JOIN_FUSED_LHS: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |idx| match idx { diff --git a/hydroflow_lang/src/graph/ops/join_fused_rhs.rs b/hydroflow_lang/src/graph/ops/join_fused_rhs.rs index 04aae55a5063..e6f819b340d9 100644 --- a/hydroflow_lang/src/graph/ops/join_fused_rhs.rs +++ b/hydroflow_lang/src/graph/ops/join_fused_rhs.rs @@ -21,6 +21,7 @@ pub const JOIN_FUSED_RHS: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |idx| match idx { diff --git a/hydroflow_lang/src/graph/ops/join_multiset.rs b/hydroflow_lang/src/graph/ops/join_multiset.rs index d5b446c71182..338dac329b60 100644 --- a/hydroflow_lang/src/graph/ops/join_multiset.rs +++ b/hydroflow_lang/src/graph/ops/join_multiset.rs @@ -37,6 +37,7 @@ pub const JOIN_MULTISET: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/lattice_bimorphism.rs b/hydroflow_lang/src/graph/ops/lattice_bimorphism.rs index a5ef7183ec7c..176258ae3331 100644 --- a/hydroflow_lang/src/graph/ops/lattice_bimorphism.rs +++ b/hydroflow_lang/src/graph/ops/lattice_bimorphism.rs @@ -52,6 +52,7 @@ pub const LATTICE_BIMORPHISM: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/lattice_fold.rs b/hydroflow_lang/src/graph/ops/lattice_fold.rs index 5475c7303138..8d85945451eb 100644 --- a/hydroflow_lang/src/graph/ops/lattice_fold.rs +++ b/hydroflow_lang/src/graph/ops/lattice_fold.rs @@ -39,6 +39,7 @@ pub const LATTICE_FOLD: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::MonotoneAccum), diff --git a/hydroflow_lang/src/graph/ops/lattice_reduce.rs b/hydroflow_lang/src/graph/ops/lattice_reduce.rs index 2d5731769c98..a38a7dc2481f 100644 --- a/hydroflow_lang/src/graph/ops/lattice_reduce.rs +++ b/hydroflow_lang/src/graph/ops/lattice_reduce.rs @@ -40,6 +40,7 @@ pub const LATTICE_REDUCE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::MonotoneAccum), diff --git a/hydroflow_lang/src/graph/ops/map.rs b/hydroflow_lang/src/graph/ops/map.rs index ed49fe48bbe5..bd03ada64c5f 100644 --- a/hydroflow_lang/src/graph/ops/map.rs +++ b/hydroflow_lang/src/graph/ops/map.rs @@ -32,6 +32,7 @@ pub const MAP: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/mod.rs b/hydroflow_lang/src/graph/ops/mod.rs index 52ad4105e03c..c4c35ce02065 100644 --- a/hydroflow_lang/src/graph/ops/mod.rs +++ b/hydroflow_lang/src/graph/ops/mod.rs @@ -70,6 +70,8 @@ pub struct OperatorConstraints { /// If true, [`WriteContextArgs::singleton_output_ident`] will be set to a meaningful value in /// the [`Self::write_fn`] invocation. pub has_singleton_output: bool, + /// Flo semantics type. + pub flo_type: Option, /// What named or numbered input ports to expect? pub ports_inn: Option PortListSpec>, @@ -240,10 +242,12 @@ macro_rules! declare_ops { }; } declare_ops![ + all_once::ALL_ONCE, anti_join::ANTI_JOIN, anti_join_multiset::ANTI_JOIN_MULTISET, assert::ASSERT, assert_eq::ASSERT_EQ, + batch::BATCH, chain::CHAIN, cross_join::CROSS_JOIN, cross_join_multiset::CROSS_JOIN_MULTISET, @@ -496,6 +500,8 @@ pub enum OperatorCategory { Sink, Control, CompilerFusionOperator, + Windowing, + Unwindowing, } impl OperatorCategory { /// Human-readible heading name, for docs. @@ -514,6 +520,8 @@ impl OperatorCategory { OperatorCategory::Sink => "Sinks", OperatorCategory::Control => "Control Flow Operators", OperatorCategory::CompilerFusionOperator => "Compiler Fusion Operators", + OperatorCategory::Windowing => "Windowing Operator", + OperatorCategory::Unwindowing => "Un-Windowing Operator", } } /// Human description, for docs. @@ -538,6 +546,19 @@ impl OperatorCategory { OperatorCategory::CompilerFusionOperator => { "Operators which are necessary to implement certain optimizations and rewrite rules" } + OperatorCategory::Windowing => "Operators for windowing `loop` inputs.", + OperatorCategory::Unwindowing => "Operators for collecting `loop` outputs.", } } } + +/// Operator type for Flo semantics. +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)] +pub enum FloType { + /// A source operator, which must be at the top level. + Source, + /// A windowing operator, for moving data into a loop context. + Windowing, + /// An un-windowing operator, for moving data out of a loop context. + Unwindowing, +} diff --git a/hydroflow_lang/src/graph/ops/multiset_delta.rs b/hydroflow_lang/src/graph/ops/multiset_delta.rs index dd47834cf2b9..46e34da6f33c 100644 --- a/hydroflow_lang/src/graph/ops/multiset_delta.rs +++ b/hydroflow_lang/src/graph/ops/multiset_delta.rs @@ -46,6 +46,7 @@ pub const MULTISET_DELTA: OperatorConstraints = OperatorConstraints { // https://github.com/hydro-project/hydroflow/issues/1298 // If `'tick` lifetimes are added. has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/next_stratum.rs b/hydroflow_lang/src/graph/ops/next_stratum.rs index 0a6238f5544e..318a3bcd5e6b 100644 --- a/hydroflow_lang/src/graph/ops/next_stratum.rs +++ b/hydroflow_lang/src/graph/ops/next_stratum.rs @@ -20,6 +20,7 @@ pub const NEXT_STRATUM: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/null.rs b/hydroflow_lang/src/graph/ops/null.rs index 73ae3f48e3d8..45f574e2c3d3 100644 --- a/hydroflow_lang/src/graph/ops/null.rs +++ b/hydroflow_lang/src/graph/ops/null.rs @@ -26,6 +26,7 @@ pub const NULL: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/partition.rs b/hydroflow_lang/src/graph/ops/partition.rs index 1f1fddb83512..d232ffd12e9a 100644 --- a/hydroflow_lang/src/graph/ops/partition.rs +++ b/hydroflow_lang/src/graph/ops/partition.rs @@ -65,6 +65,7 @@ pub const PARTITION: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: Some(|| PortListSpec::Variadic), input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/persist.rs b/hydroflow_lang/src/graph/ops/persist.rs index 187f571e5693..4f3f41d5decf 100644 --- a/hydroflow_lang/src/graph/ops/persist.rs +++ b/hydroflow_lang/src/graph/ops/persist.rs @@ -48,6 +48,7 @@ pub const PERSIST: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: true, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/persist_mut.rs b/hydroflow_lang/src/graph/ops/persist_mut.rs index 08c0b5f627d9..822d98b30e9e 100644 --- a/hydroflow_lang/src/graph/ops/persist_mut.rs +++ b/hydroflow_lang/src/graph/ops/persist_mut.rs @@ -39,6 +39,7 @@ pub const PERSIST_MUT: OperatorConstraints = OperatorConstraints { // https://github.com/hydro-project/hydroflow/issues/1298 // If `'tick` lifetimes are added. has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/persist_mut_keyed.rs b/hydroflow_lang/src/graph/ops/persist_mut_keyed.rs index a0cf662ed8a7..6e97e74b95cb 100644 --- a/hydroflow_lang/src/graph/ops/persist_mut_keyed.rs +++ b/hydroflow_lang/src/graph/ops/persist_mut_keyed.rs @@ -39,6 +39,7 @@ pub const PERSIST_MUT_KEYED: OperatorConstraints = OperatorConstraints { // https://github.com/hydro-project/hydroflow/issues/1298 // If `'tick` lifetimes are added. has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/py_udf.rs b/hydroflow_lang/src/graph/ops/py_udf.rs index 7b2bc0adb8f8..67901a58d0c7 100644 --- a/hydroflow_lang/src/graph/ops/py_udf.rs +++ b/hydroflow_lang/src/graph/ops/py_udf.rs @@ -62,6 +62,7 @@ pub const PY_UDF: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/reduce.rs b/hydroflow_lang/src/graph/ops/reduce.rs index 53530b2d49b4..03b16c2e4c09 100644 --- a/hydroflow_lang/src/graph/ops/reduce.rs +++ b/hydroflow_lang/src/graph/ops/reduce.rs @@ -43,6 +43,7 @@ pub const REDUCE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: true, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/reduce_keyed.rs b/hydroflow_lang/src/graph/ops/reduce_keyed.rs index 611bad6b1c6d..bf1fe2f8b4c7 100644 --- a/hydroflow_lang/src/graph/ops/reduce_keyed.rs +++ b/hydroflow_lang/src/graph/ops/reduce_keyed.rs @@ -70,6 +70,7 @@ pub const REDUCE_KEYED: OperatorConstraints = OperatorConstraints { // to prevent reading uncleared data if this subgraph doesn't run. // https://github.com/hydro-project/hydroflow/issues/1298 has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/sort.rs b/hydroflow_lang/src/graph/ops/sort.rs index a516e4e6368f..e10349b180a9 100644 --- a/hydroflow_lang/src/graph/ops/sort.rs +++ b/hydroflow_lang/src/graph/ops/sort.rs @@ -27,6 +27,7 @@ pub const SORT: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/sort_by_key.rs b/hydroflow_lang/src/graph/ops/sort_by_key.rs index 967da1027f73..1e7aacb603c2 100644 --- a/hydroflow_lang/src/graph/ops/sort_by_key.rs +++ b/hydroflow_lang/src/graph/ops/sort_by_key.rs @@ -27,6 +27,7 @@ pub const SORT_BY_KEY: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/graph/ops/source_file.rs b/hydroflow_lang/src/graph/ops/source_file.rs index 604da35ffbfb..664e9ec89640 100644 --- a/hydroflow_lang/src/graph/ops/source_file.rs +++ b/hydroflow_lang/src/graph/ops/source_file.rs @@ -2,8 +2,8 @@ use quote::quote_spanned; use syn::parse_quote_spanned; use super::{ - make_missing_runtime_msg, OperatorCategory, OperatorConstraints, - OperatorWriteOutput, WriteContextArgs, RANGE_0, RANGE_1, + make_missing_runtime_msg, FloType, OperatorCategory, OperatorConstraints, OperatorWriteOutput, + WriteContextArgs, RANGE_0, RANGE_1, }; /// > 0 input streams, 1 output stream @@ -30,6 +30,7 @@ pub const SOURCE_FILE: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: true, has_singleton_output: false, + flo_type: Some(FloType::Source), ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/source_interval.rs b/hydroflow_lang/src/graph/ops/source_interval.rs index e1759023c7df..4f77fd526958 100644 --- a/hydroflow_lang/src/graph/ops/source_interval.rs +++ b/hydroflow_lang/src/graph/ops/source_interval.rs @@ -2,8 +2,8 @@ use quote::quote_spanned; use syn::parse_quote_spanned; use super::{ - OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, - RANGE_0, RANGE_1, + FloType, OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, RANGE_0, + RANGE_1, }; /// > 0 input streams, 1 output stream @@ -54,6 +54,7 @@ pub const SOURCE_INTERVAL: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: true, has_singleton_output: false, + flo_type: Some(FloType::Source), ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/source_iter.rs b/hydroflow_lang/src/graph/ops/source_iter.rs index 74dce7bf2d50..ca5a299b9c32 100644 --- a/hydroflow_lang/src/graph/ops/source_iter.rs +++ b/hydroflow_lang/src/graph/ops/source_iter.rs @@ -1,8 +1,7 @@ use quote::quote_spanned; use super::{ - OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, - RANGE_0, RANGE_1, + FloType, OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, RANGE_0, RANGE_1 }; /// > 0 input streams, 1 output stream @@ -30,6 +29,7 @@ pub const SOURCE_ITER: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: Some(FloType::Source), ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/source_json.rs b/hydroflow_lang/src/graph/ops/source_json.rs index 40cc97cccc60..890a25372aac 100644 --- a/hydroflow_lang/src/graph/ops/source_json.rs +++ b/hydroflow_lang/src/graph/ops/source_json.rs @@ -1,7 +1,7 @@ use quote::quote_spanned; use super::{ - OpInstGenerics, OperatorCategory, OperatorConstraints, OperatorInstance, + FloType, OpInstGenerics, OperatorCategory, OperatorConstraints, OperatorInstance, OperatorWriteOutput, WriteContextArgs, RANGE_0, RANGE_1, }; @@ -27,6 +27,7 @@ pub const SOURCE_JSON: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: true, has_singleton_output: false, + flo_type: Some(FloType::Source), ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/source_stdin.rs b/hydroflow_lang/src/graph/ops/source_stdin.rs index af5d8a17cc67..4a788eecc9e6 100644 --- a/hydroflow_lang/src/graph/ops/source_stdin.rs +++ b/hydroflow_lang/src/graph/ops/source_stdin.rs @@ -1,8 +1,8 @@ use quote::quote_spanned; use super::{ - OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, - RANGE_0, RANGE_1, + FloType, OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, RANGE_0, + RANGE_1, }; /// > 0 input streams, 1 output stream @@ -29,6 +29,7 @@ pub const SOURCE_STDIN: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: true, has_singleton_output: false, + flo_type: Some(FloType::Source), ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/source_stream.rs b/hydroflow_lang/src/graph/ops/source_stream.rs index f5610dfbeb32..8a5b7f392e3a 100644 --- a/hydroflow_lang/src/graph/ops/source_stream.rs +++ b/hydroflow_lang/src/graph/ops/source_stream.rs @@ -1,8 +1,8 @@ use quote::quote_spanned; use super::{ - OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, - RANGE_0, RANGE_1, + FloType, OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, RANGE_0, + RANGE_1, }; /// > 0 input streams, 1 output stream @@ -36,6 +36,7 @@ pub const SOURCE_STREAM: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: true, has_singleton_output: false, + flo_type: Some(FloType::Source), ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/source_stream_serde.rs b/hydroflow_lang/src/graph/ops/source_stream_serde.rs index cb0d7003522f..4c4fbf51d6f8 100644 --- a/hydroflow_lang/src/graph/ops/source_stream_serde.rs +++ b/hydroflow_lang/src/graph/ops/source_stream_serde.rs @@ -1,8 +1,8 @@ use quote::quote_spanned; use super::{ - OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, - RANGE_0, RANGE_1, + FloType, OperatorCategory, OperatorConstraints, OperatorWriteOutput, WriteContextArgs, RANGE_0, + RANGE_1, }; /// > 0 input streams, 1 output stream @@ -36,6 +36,7 @@ pub const SOURCE_STREAM_SERDE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: true, has_singleton_output: false, + flo_type: Some(FloType::Source), ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/spin.rs b/hydroflow_lang/src/graph/ops/spin.rs index b943cb09ac9e..c686d03d0c95 100644 --- a/hydroflow_lang/src/graph/ops/spin.rs +++ b/hydroflow_lang/src/graph/ops/spin.rs @@ -29,6 +29,7 @@ pub const SPIN: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/state.rs b/hydroflow_lang/src/graph/ops/state.rs index b2c830916afe..ddcf213af446 100644 --- a/hydroflow_lang/src/graph/ops/state.rs +++ b/hydroflow_lang/src/graph/ops/state.rs @@ -31,6 +31,7 @@ pub const STATE: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: false, has_singleton_output: true, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/state_by.rs b/hydroflow_lang/src/graph/ops/state_by.rs index f067059ab514..d676c5fc8439 100644 --- a/hydroflow_lang/src/graph/ops/state_by.rs +++ b/hydroflow_lang/src/graph/ops/state_by.rs @@ -32,6 +32,7 @@ pub const STATE_BY: OperatorConstraints = OperatorConstraints { type_args: &(0..=1), is_external_input: false, has_singleton_output: true, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/tee.rs b/hydroflow_lang/src/graph/ops/tee.rs index fcbf0a182d6d..01641044f328 100644 --- a/hydroflow_lang/src/graph/ops/tee.rs +++ b/hydroflow_lang/src/graph/ops/tee.rs @@ -28,6 +28,7 @@ pub const TEE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/union.rs b/hydroflow_lang/src/graph/ops/union.rs index dbc8e6af4077..ae59c8053164 100644 --- a/hydroflow_lang/src/graph/ops/union.rs +++ b/hydroflow_lang/src/graph/ops/union.rs @@ -32,6 +32,7 @@ pub const UNION: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/unique.rs b/hydroflow_lang/src/graph/ops/unique.rs index 8dbc63d2a111..28b44a049e4f 100644 --- a/hydroflow_lang/src/graph/ops/unique.rs +++ b/hydroflow_lang/src/graph/ops/unique.rs @@ -54,6 +54,7 @@ pub const UNIQUE: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/unzip.rs b/hydroflow_lang/src/graph/ops/unzip.rs index 272df573983d..ac3f04d21461 100644 --- a/hydroflow_lang/src/graph/ops/unzip.rs +++ b/hydroflow_lang/src/graph/ops/unzip.rs @@ -28,6 +28,7 @@ pub const UNZIP: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: None, ports_out: Some(|| super::PortListSpec::Fixed(parse_quote!(0, 1))), input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/zip.rs b/hydroflow_lang/src/graph/ops/zip.rs index 1cb58f85316d..290d642cc098 100644 --- a/hydroflow_lang/src/graph/ops/zip.rs +++ b/hydroflow_lang/src/graph/ops/zip.rs @@ -30,6 +30,7 @@ pub const ZIP: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| None, diff --git a/hydroflow_lang/src/graph/ops/zip_longest.rs b/hydroflow_lang/src/graph/ops/zip_longest.rs index db07f791bd55..1ed5e5c51af6 100644 --- a/hydroflow_lang/src/graph/ops/zip_longest.rs +++ b/hydroflow_lang/src/graph/ops/zip_longest.rs @@ -34,6 +34,7 @@ pub const ZIP_LONGEST: OperatorConstraints = OperatorConstraints { type_args: RANGE_0, is_external_input: false, has_singleton_output: false, + flo_type: None, ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { 0, 1 })), ports_out: None, input_delaytype_fn: |_| Some(DelayType::Stratum), diff --git a/hydroflow_lang/src/parse.rs b/hydroflow_lang/src/parse.rs index cca523c9b34a..e08fdda44b9d 100644 --- a/hydroflow_lang/src/parse.rs +++ b/hydroflow_lang/src/parse.rs @@ -8,10 +8,10 @@ use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::token::{Bracket, Paren}; +use syn::token::{Brace, Bracket, Paren}; use syn::{ - bracketed, parenthesized, AngleBracketedGenericArguments, Expr, ExprPath, GenericArgument, - Ident, ItemUse, LitInt, LitStr, Path, PathArguments, PathSegment, Token, + braced, bracketed, parenthesized, AngleBracketedGenericArguments, Expr, ExprPath, + GenericArgument, Ident, ItemUse, LitInt, LitStr, Path, PathArguments, PathSegment, Token, }; use crate::process_singletons::preprocess_singletons; @@ -40,24 +40,45 @@ pub enum HfStatement { Use(ItemUse), Named(NamedHfStatement), Pipeline(PipelineStatement), + Loop(LoopStatement), } impl Parse for HfStatement { fn parse(input: ParseStream) -> syn::Result { - if input.peek(Token![use]) { + let lookahead1 = input.lookahead1(); + if lookahead1.peek(Token![use]) { Ok(Self::Use(ItemUse::parse(input)?)) - } else if input.peek2(Token![=]) { - Ok(Self::Named(NamedHfStatement::parse(input)?)) - } else { + } else if lookahead1.peek(Paren) || lookahead1.peek(Bracket) || lookahead1.peek(Token![mod]) + { Ok(Self::Pipeline(PipelineStatement::parse(input)?)) + } else if lookahead1.peek(Token![loop]) { + Ok(Self::Loop(LoopStatement::parse(input)?)) + } else if lookahead1.peek(Ident) { + let fork = input.fork(); + let _: Path = fork.parse()?; + let lookahead2 = fork.lookahead1(); + if lookahead2.peek(Token![=]) { + Ok(Self::Named(NamedHfStatement::parse(input)?)) + } else if lookahead2.peek(Token![->]) + || lookahead2.peek(Paren) + || lookahead2.peek(Bracket) + || lookahead2.peek(Token![!]) + { + Ok(Self::Pipeline(PipelineStatement::parse(input)?)) + } else { + Err(lookahead2.error()) + } + } else { + Err(lookahead1.error()) } } } impl ToTokens for HfStatement { fn to_tokens(&self, tokens: &mut TokenStream) { match self { - HfStatement::Use(x) => x.to_tokens(tokens), - HfStatement::Named(x) => x.to_tokens(tokens), - HfStatement::Pipeline(x) => x.to_tokens(tokens), + Self::Use(x) => x.to_tokens(tokens), + Self::Named(x) => x.to_tokens(tokens), + Self::Pipeline(x) => x.to_tokens(tokens), + Self::Loop(x) => x.to_tokens(tokens), } } } @@ -197,13 +218,49 @@ impl Parse for Pipeline { impl ToTokens for Pipeline { fn to_tokens(&self, tokens: &mut TokenStream) { match self { - Pipeline::Paren(x) => x.to_tokens(tokens), - Pipeline::Link(x) => x.to_tokens(tokens), - Pipeline::Name(x) => x.to_tokens(tokens), - Pipeline::Operator(x) => x.to_tokens(tokens), - Pipeline::ModuleBoundary(x) => x.to_tokens(tokens), - Pipeline::Import(x) => x.to_tokens(tokens), + Self::Paren(x) => x.to_tokens(tokens), + Self::Link(x) => x.to_tokens(tokens), + Self::Name(x) => x.to_tokens(tokens), + Self::Operator(x) => x.to_tokens(tokens), + Self::ModuleBoundary(x) => x.to_tokens(tokens), + Self::Import(x) => x.to_tokens(tokens), + } + } +} + +pub struct LoopStatement { + pub loop_token: Token![loop], + pub ident: Option, + pub brace_token: Brace, + pub statements: Vec, +} +impl Parse for LoopStatement { + fn parse(input: ParseStream) -> syn::Result { + let loop_token = input.parse()?; + let ident = input.parse()?; + let content; + let brace_token = braced!(content in input); + let mut statements = Vec::new(); + while !content.is_empty() { + statements.push(content.parse()?); } + Ok(Self { + loop_token, + ident, + brace_token, + statements, + }) + } +} +impl ToTokens for LoopStatement { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.loop_token.to_tokens(tokens); + self.ident.to_tokens(tokens); + self.brace_token.surround(tokens, |tokens| { + for statement in self.statements.iter() { + statement.to_tokens(tokens); + } + }); } }

    v2WSkS3YwyMSFJ{yjhiYK5#rIKC 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 V2f@rc?#-+Pz+pa;GTbS&q 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%0D^$0)RnF>~D1|@c@Bu@JGip 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

    d^2AEBwVopdw()`!9;Jc5Cp9Q_RyM*r^q8Jsph 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