From 7fd4456f325c45e23c4e21c6f43b61c171691471 Mon Sep 17 00:00:00 2001 From: Till Rohrmann Date: Tue, 2 Jun 2026 17:18:56 +0200 Subject: [PATCH] Move InvokerMemoryTest services to a Dockerized Rust container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Background: InvokerMemoryTest previously bound MemoryPressureService and StatefulObject in-process in the JVM via Endpoint.bind(...), with the Restate container reaching the SDK through the testcontainers host-port relay (host.testcontainers.internal). Under sustained sequential load, that relay throttled service-runtime traffic to a fraction of normal throughput. We were not able to pinpoint the exact mechanism (single-threaded socat hop, lack of HTTP/2 flow-control awareness, and connection-tracking on the bridge network are all candidates), but the slowdown reproduces only on the relay path — sibling-container deployments on the same bridge network are unaffected. Switching the test off the relay was the most actionable mitigation. Implementation: - New aggregate Rust binary at e2e-tests/services/rust/ packaged as ghcr.io/restatedev/e2e-test-services-rs. The binary reads the SERVICES env var (already injected by ServiceSpec.withServices(...)) and conditionally binds the requested services, matching the sdk-tests convention. Adding a new Rust-side e2e service is now a module + match-arm change — no new image. - MemoryPressureService and StatefulObject implemented under src/invoker_memory.rs with #[restate_sdk::service] / #[restate_sdk::object] and explicit #[name = "..."] mappings so the camelCase Restate names line up with the Kotlin contracts. - Kotlin-side contracts promoted to top-level interfaces under e2e-tests/.../contracts/, matching the sdk-tests layout. - infra: new RestateDeployer.Builder.withServiceDeploymentConfig(...) so a test can pin its own image without going through the global --service-container-image CLI flag. - InvokerMemoryTest swaps withEndpoint(Endpoint.bind(...)) for withServiceDeploymentConfig + withServiceSpec. The runtime now reaches the service over the standard bridge network at http://invoker-memory:9080/ — no SSH relay involved. Image must be published manually once before this branch passes CI: docker buildx build --platform linux/amd64,linux/arm64 \\ -t ghcr.io/restatedev/e2e-test-services-rs:0.1.0 \\ --push e2e-tests/services/rust See e2e-tests/services/rust/README.md for the full publish flow. Co-Authored-By: Claude Opus 4.7 (1M context) --- e2e-tests/services/rust/.dockerignore | 4 + e2e-tests/services/rust/.gitignore | 1 + e2e-tests/services/rust/Cargo.lock | 1925 +++++++++++++++++ e2e-tests/services/rust/Cargo.toml | 17 + e2e-tests/services/rust/Dockerfile | 48 + e2e-tests/services/rust/README.md | 117 + e2e-tests/services/rust/rust-toolchain.toml | 3 + e2e-tests/services/rust/src/main.rs | 94 + .../rust/src/memory_pressure_service.rs | 49 + .../services/rust/src/stateful_object.rs | 62 + e2e-tests/services/rust/src/util.rs | 27 + .../contracts/MemoryPressureService.kt | 27 + .../sdktesting/contracts/StatefulObject.kt | 29 + .../sdktesting/tests/InvokerMemoryTest.kt | 90 +- .../sdktesting/infra/RestateDeployer.kt | 13 + 15 files changed, 2446 insertions(+), 60 deletions(-) create mode 100644 e2e-tests/services/rust/.dockerignore create mode 100644 e2e-tests/services/rust/.gitignore create mode 100644 e2e-tests/services/rust/Cargo.lock create mode 100644 e2e-tests/services/rust/Cargo.toml create mode 100644 e2e-tests/services/rust/Dockerfile create mode 100644 e2e-tests/services/rust/README.md create mode 100644 e2e-tests/services/rust/rust-toolchain.toml create mode 100644 e2e-tests/services/rust/src/main.rs create mode 100644 e2e-tests/services/rust/src/memory_pressure_service.rs create mode 100644 e2e-tests/services/rust/src/stateful_object.rs create mode 100644 e2e-tests/services/rust/src/util.rs create mode 100644 e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/MemoryPressureService.kt create mode 100644 e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/StatefulObject.kt diff --git a/e2e-tests/services/rust/.dockerignore b/e2e-tests/services/rust/.dockerignore new file mode 100644 index 00000000..5a45872d --- /dev/null +++ b/e2e-tests/services/rust/.dockerignore @@ -0,0 +1,4 @@ +target +.git +.gitignore +*.md diff --git a/e2e-tests/services/rust/.gitignore b/e2e-tests/services/rust/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/e2e-tests/services/rust/.gitignore @@ -0,0 +1 @@ +target diff --git a/e2e-tests/services/rust/Cargo.lock b/e2e-tests/services/rust/Cargo.lock new file mode 100644 index 00000000..c51ac7ec --- /dev/null +++ b/e2e-tests/services/rust/Cargo.lock @@ -0,0 +1,1925 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.6", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid 0.10.2", + "crypto-common 0.2.2", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "e2e-test-services" +version = "0.1.0" +dependencies = [ + "rand 0.10.1", + "restate-sdk", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2 0.10.9", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core 0.10.1", + "wasip2", + "wasip3", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httpdate", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonptr" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "jsonwebtoken" +version = "10.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" +dependencies = [ + "base64", + "ed25519-dalek", + "getrandom 0.2.17", + "hmac", + "js-sys", + "p256", + "p384", + "rand 0.8.6", + "rsa", + "serde", + "serde_json", + "sha2 0.10.9", + "signature", + "zeroize", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.6", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "regress" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" +dependencies = [ + "hashbrown 0.16.1", + "memchr", +] + +[[package]] +name = "regress" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158a764437582235e3501f683b93a0a6f8d825d04a789dbe5ed30b8799b8908a" +dependencies = [ + "hashbrown 0.16.1", + "memchr", +] + +[[package]] +name = "restate-sdk" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f79f0cd584fc799c4d7ee40f66b18ffe27057615a327285fc08c8143b84eac" +dependencies = [ + "bytes", + "futures", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "jsonptr", + "pin-project-lite", + "prettyplease", + "rand 0.10.1", + "regress 0.10.5", + "restate-sdk-macros", + "restate-sdk-shared-core", + "serde", + "serde_json", + "syn", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber", + "typify", + "uuid", +] + +[[package]] +name = "restate-sdk-macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b035bf9a494063b7ae225627bfbd6c465ef1d32f4ba34a3f9269ad11528b52ad" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "restate-sdk-shared-core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9eb19f86fb0290c4f73a6971c792ff71423baf1866dc7fab868c614804e98dd" +dependencies = [ + "base64", + "bs58", + "bytes", + "bytes-utils", + "http", + "jsonwebtoken", + "pastey", + "prost", + "serde", + "sha2 0.11.0", + "strum", + "thiserror", + "tracing", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid 0.9.6", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_tokenstream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c49585c52c01f13c5c2ebb333f14f6885d76daa768d8a037d28017ec538c69" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "typify" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0b89f47309feaeb23c4509c15c9a04234f7deccef6f96c3bfe95319819a304" +dependencies = [ + "typify-impl", + "typify-macro", +] + +[[package]] +name = "typify-impl" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7b026f540b148b81043c720889dbb942b08659aa8a43f624ac4f04dbfc1861" +dependencies = [ + "heck", + "log", + "proc-macro2", + "quote", + "regress 0.11.1", + "schemars", + "semver", + "serde", + "serde_json", + "syn", + "thiserror", + "unicode-ident", +] + +[[package]] +name = "typify-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ed96c57f06ae0839416b986921a98f18b220da63bbb243a8570a00c8492183" +dependencies = [ + "proc-macro2", + "quote", + "schemars", + "semver", + "serde", + "serde_json", + "serde_tokenstream", + "syn", + "typify-impl", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/e2e-tests/services/rust/Cargo.toml b/e2e-tests/services/rust/Cargo.toml new file mode 100644 index 00000000..10ef63e5 --- /dev/null +++ b/e2e-tests/services/rust/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "e2e-test-services" +version = "0.1.0" +edition = "2024" +publish = false +description = "Aggregate Rust binary hosting all Rust-side service implementations used by the e2e test suite. Services to bind are selected at startup via the SERVICES env var." + +[dependencies] +restate-sdk = { version = "0.10", features = ["http_server"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +rand = "0.10" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +[profile.release] +lto = "thin" +codegen-units = 1 diff --git a/e2e-tests/services/rust/Dockerfile b/e2e-tests/services/rust/Dockerfile new file mode 100644 index 00000000..99c5cf21 --- /dev/null +++ b/e2e-tests/services/rust/Dockerfile @@ -0,0 +1,48 @@ +# syntax=docker/dockerfile:1.7 +# Copyright (c) 2023 - 2026 Restate Software, Inc., Restate GmbH +# +# This file is part of the Restate e2e test suite, +# which is released under the MIT license. +# +# You can find a copy of the license in file LICENSE in the root +# directory of this repository or package, or at +# https://github.com/restatedev/e2e/blob/main/LICENSE +# +# No `--platform=$BUILDPLATFORM` pin on the builder: we *want* `docker buildx` +# to run the builder stage at the target architecture (under QEMU when it +# differs from the host) so `cargo build --release` produces a binary matching +# `$TARGETPLATFORM`. Pinning the builder to the host would compile for the +# host's triple and copy that host-arch binary into every per-arch runtime +# image, yielding `exec format error` on the foreign arch. +# +# Emulated Rust builds are slow (~5-10× native); acceptable for a manually +# triggered publish. Cross-compile via `rustup target add` + a `--target` +# flag if/when that becomes painful. +FROM rust:1.90-slim AS builder + +WORKDIR /app + +COPY rust-toolchain.toml Cargo.toml ./ +COPY Cargo.lock* ./ +COPY src ./src + +# BuildKit cache mounts speed up incremental rebuilds; they're keyed per-arch +# so each target platform has its own registry / target dir. +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/app/target,sharing=locked \ + cargo build --release && \ + cp target/release/e2e-test-services /usr/local/bin/e2e-test-services + +FROM debian:bookworm-slim + +# `restate-sdk` uses rustls + ring/aws-lc-rs — no system OpenSSL needed. +# ca-certificates is included for any outbound HTTPS the service might add later. +RUN apt-get update && \ + apt-get install -y --no-install-recommends ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=builder /usr/local/bin/e2e-test-services /usr/local/bin/e2e-test-services + +EXPOSE 9080 +ENV RUST_LOG=info +CMD ["/usr/local/bin/e2e-test-services"] diff --git a/e2e-tests/services/rust/README.md b/e2e-tests/services/rust/README.md new file mode 100644 index 00000000..4b54e302 --- /dev/null +++ b/e2e-tests/services/rust/README.md @@ -0,0 +1,117 @@ +# e2e-test-services (Rust) + +Aggregate Rust binary hosting every Rust-side service implementation consumed +by the JVM e2e test suite. Packaged and published as a single multi-arch +Docker image at **`ghcr.io/restatedev/e2e-test-services-rs`**. + +The binary reads the `SERVICES` environment variable at startup +(comma-separated list of service names) and binds only those services to its +Restate HTTP/2 endpoint. The Testcontainers-based deployer in +`infra/.../ServiceSpec.kt` already injects this variable for any sibling +service container — see `ServiceSpec.withServices(...)` callers. If `SERVICES` +is missing or empty all known services are bound (a convenient local-dev +default). + +## Currently hosted services + +| Restate name | Module | Kotlin contract (under `e2e-tests/.../contracts/`) | +| ------------------------ | ------------------ | -------------------------------------------------- | +| `MemoryPressureService` | `invoker_memory` | `MemoryPressureService.kt` | +| `StatefulObject` | `invoker_memory` | `StatefulObject.kt` | + +If you rename a Restate-visible name on either side you must update the other +in the same change — a mismatch surfaces immediately as a 404 from the Restate +ingress on the first test call. + +## Adding a new service + +1. Create `src/.rs` with the trait + impl, decorated with + `#[restate_sdk::service]` / `#[restate_sdk::object]` and `#[name = "..."]` + to fix the Restate-visible camelCase name. +2. Declare the module in `src/main.rs`: `mod ;` and import the + impl (and trait, so `.serve()` is in scope). +3. Add an entry to `ALL_SERVICES` and a `match` arm in `bind_services(...)`. +4. Add a matching Kotlin `@Service` / `@VirtualObject` interface under + `e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/`. +5. Update the "Currently hosted services" table above. +6. Rebuild and republish the image (bump the tag, see below) and update the + `DEFAULT_E2E_RS_IMAGE` constant in any test that pins the tag. + +## Prerequisites + +- Rust toolchain ≥ 1.90 (`rust-toolchain.toml` pins this). +- Docker with `buildx` enabled. +- A multi-arch builder, created once per machine: + + ```bash + docker buildx create --name multiarch --use + ``` + +## Local development + +```bash +cd e2e-tests/services/rust +cargo build --release + +# Bind every service (default): +PORT=9080 target/release/e2e-test-services + +# Bind a strict subset: +PORT=9080 SERVICES=MemoryPressureService target/release/e2e-test-services +``` + +Point a local Restate runtime at the listening port, or run the e2e test in +debug mode with the local-forward service deployment config. + +## Building and publishing the multi-arch image to ghcr.io + +CI does **not** build this image — it pulls a pre-built multi-arch image +from `ghcr.io`. Republish manually whenever any service implementation, the +`Cargo.toml`, or the `Dockerfile` changes meaningfully. + +```bash +# 1. Authenticate to ghcr.io (once per machine): +echo $GITHUB_TOKEN | docker login ghcr.io -u --password-stdin + +# 2. Build for linux/amd64 + linux/arm64 and push: +docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -t ghcr.io/restatedev/e2e-test-services-rs:0.1.0 \ + --push \ + e2e-tests/services/rust + +# 3. Verify the manifest lists both arches: +docker manifest inspect ghcr.io/restatedev/e2e-test-services-rs:0.1.0 +``` + +**Bump cadence**: bump the tag (`0.1.0` → `0.1.1` for patches, `0.2.0` when a +new service is added or an existing handler signature changes) and update +the `DEFAULT_E2E_RS_IMAGE` constant in any test that pins the tag. + +**GHCR package visibility**: set to **public** so workflow runners (and other +contributors) can pull without authentication. UI: +. + +## Discovery manifest sanity check + +To confirm the running binary exposes the handler names the Kotlin client +expects: + +```bash +PORT=9080 SERVICES=MemoryPressureService,StatefulObject \ + target/release/e2e-test-services & + +curl --http2-prior-knowledge \ + -H 'accept: application/vnd.restate.endpointmanifest.v3+json' \ + http://127.0.0.1:9080/discover \ + | jq '.services[] | {name, ty, handlers: [.handlers[].name]}' +``` + +Expected: + +```json +{ "name": "MemoryPressureService", "ty": "SERVICE", + "handlers": ["generate", "generateOversized"] } +{ "name": "StatefulObject", "ty": "VIRTUAL_OBJECT", + "handlers": ["initState", "readState", "readLargeState"] } +``` diff --git a/e2e-tests/services/rust/rust-toolchain.toml b/e2e-tests/services/rust/rust-toolchain.toml new file mode 100644 index 00000000..d2314162 --- /dev/null +++ b/e2e-tests/services/rust/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.90" +components = ["rustfmt"] diff --git a/e2e-tests/services/rust/src/main.rs b/e2e-tests/services/rust/src/main.rs new file mode 100644 index 00000000..5deb2fe3 --- /dev/null +++ b/e2e-tests/services/rust/src/main.rs @@ -0,0 +1,94 @@ +// Copyright (c) 2023 - 2026 Restate Software, Inc., Restate GmbH +// +// This file is part of the Restate e2e test suite, +// which is released under the MIT license. +// +// You can find a copy of the license in file LICENSE in the root +// directory of this repository or package, or at +// https://github.com/restatedev/e2e/blob/main/LICENSE + +//! Aggregate Rust binary hosting all Rust-side service implementations consumed +//! by the JVM e2e test suite. +//! +//! The binary reads a comma-separated list of service names from the `SERVICES` +//! environment variable (the same variable the Testcontainers-based deployer in +//! `infra/.../ServiceSpec.kt` injects for sibling service containers). Each +//! requested service is bound to a Restate HTTP/2 endpoint listening on `$PORT` +//! (default `9080`). If `SERVICES` is missing or empty all known services are +//! bound — a convenient default for local development. +//! +//! To add a new service: +//! 1. Create a new module under `src/` with the trait, impl struct and +//! `#[restate_sdk::service]` / `#[restate_sdk::object]` annotation. +//! 2. Declare the module below (`mod my_service;`). +//! 3. Add a match arm in `bind_services` and an entry in `ALL_SERVICES`. +//! 4. Add a Kotlin contract interface in +//! `e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/`. + +mod memory_pressure_service; +mod stateful_object; +mod util; + +use crate::memory_pressure_service::{MemoryPressureService, MemoryPressureServiceImpl}; +use crate::stateful_object::{StatefulObject, StatefulObjectImpl}; +use restate_sdk::endpoint::Builder; +use restate_sdk::prelude::*; +use std::time::Duration; + +/// Names of every service this binary can bind. Used as the default set when +/// `SERVICES` is not set. +const ALL_SERVICES: &[&str] = &["MemoryPressureService", "StatefulObject"]; + +/// Apply the given service names to the endpoint builder. Unknown names are +/// logged and skipped — at worst the ingress returns 404 on the first call, +/// which is louder and easier to diagnose than a startup crash. +fn bind_services(mut b: Builder, names: &[&str]) -> Builder { + // journal_retention = 0 mirrors the in-process Kotlin path's + // `it.journalRetention = 0.seconds`. InvokerMemoryTest creates large payloads + // in journal steps and queries sys_journal at teardown, so retaining them + // would bloat the table. Override per-arm if a future service needs more. + let opts = || ServiceOptions::default().journal_retention(Duration::ZERO); + for name in names { + b = match *name { + "MemoryPressureService" => { + b.bind_with_options(MemoryPressureServiceImpl.serve(), opts()) + } + "StatefulObject" => b.bind_with_options(StatefulObjectImpl.serve(), opts()), + unknown => { + tracing::warn!(service = unknown, "Unknown service name in SERVICES; skipping"); + b + } + }; + } + b +} + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + + let services_var = std::env::var("SERVICES").unwrap_or_default(); + let requested: Vec<&str> = if services_var.trim().is_empty() { + ALL_SERVICES.to_vec() + } else { + services_var + .split(',') + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) + .collect() + }; + tracing::info!(?requested, "Binding services"); + + let endpoint = bind_services(Endpoint::builder(), &requested).build(); + + let port: u16 = std::env::var("PORT") + .ok() + .and_then(|v| v.parse().ok()) + .unwrap_or(9080); + + HttpServer::new(endpoint) + .listen_and_serve(([0, 0, 0, 0], port).into()) + .await; +} diff --git a/e2e-tests/services/rust/src/memory_pressure_service.rs b/e2e-tests/services/rust/src/memory_pressure_service.rs new file mode 100644 index 00000000..af29884f --- /dev/null +++ b/e2e-tests/services/rust/src/memory_pressure_service.rs @@ -0,0 +1,49 @@ +// Copyright (c) 2023 - 2026 Restate Software, Inc., Restate GmbH +// +// This file is part of the Restate e2e test suite, +// which is released under the MIT license. +// +// You can find a copy of the license in file LICENSE in the root +// directory of this repository or package, or at +// https://github.com/restatedev/e2e/blob/main/LICENSE + +//! `MemoryPressureService`, consumed by `InvokerMemoryTest` in the JVM e2e suite. +//! +//! The Kotlin test holds a matching `@Service` interface at +//! `e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/MemoryPressureService.kt`; +//! the trait and handler names below must stay in sync with that contract. + +use crate::util::{KB, random_string}; +use restate_sdk::prelude::*; + +#[restate_sdk::service] +#[name = "MemoryPressureService"] +pub trait MemoryPressureService { + async fn generate(input: String) -> Result; + #[name = "generateOversized"] + async fn generate_oversized(input: String) -> Result; +} + +pub struct MemoryPressureServiceImpl; + +impl MemoryPressureService for MemoryPressureServiceImpl { + async fn generate(&self, ctx: Context<'_>, input: String) -> Result { + for _ in 0..10 { + ctx.run(|| async { Ok::<_, HandlerError>(random_string(64 * KB)) }) + .await?; + } + Ok(format!("ok-{input}")) + } + + async fn generate_oversized( + &self, + ctx: Context<'_>, + input: String, + ) -> Result { + // 512 KiB single side effect — exceeds the 256 KiB per-invocation memory + // limit so the runtime should pause the invocation. + ctx.run(|| async { Ok::<_, HandlerError>(random_string(512 * KB)) }) + .await?; + Ok(format!("ok-{input}")) + } +} diff --git a/e2e-tests/services/rust/src/stateful_object.rs b/e2e-tests/services/rust/src/stateful_object.rs new file mode 100644 index 00000000..18d68414 --- /dev/null +++ b/e2e-tests/services/rust/src/stateful_object.rs @@ -0,0 +1,62 @@ +// Copyright (c) 2023 - 2026 Restate Software, Inc., Restate GmbH +// +// This file is part of the Restate e2e test suite, +// which is released under the MIT license. +// +// You can find a copy of the license in file LICENSE in the root +// directory of this repository or package, or at +// https://github.com/restatedev/e2e/blob/main/LICENSE + +//! `StatefulObject`, consumed by `InvokerMemoryTest` in the JVM e2e suite. +//! +//! The Kotlin test holds a matching `@VirtualObject` interface at +//! `e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/StatefulObject.kt`; +//! the trait and handler names below must stay in sync with that contract. + +use crate::util::{KB, random_string}; +use restate_sdk::prelude::*; + +#[restate_sdk::object] +#[name = "StatefulObject"] +pub trait StatefulObject { + #[name = "initState"] + async fn init_state(input: String) -> Result<(), HandlerError>; + #[name = "readState"] + async fn read_state(input: String) -> Result; + #[name = "readLargeState"] + async fn read_large_state(input: String) -> Result; +} + +pub struct StatefulObjectImpl; + +impl StatefulObject for StatefulObjectImpl { + async fn init_state( + &self, + ctx: ObjectContext<'_>, + _input: String, + ) -> Result<(), HandlerError> { + // 2 × 32 KiB → 64 KiB total state per virtual object. + ctx.set("state-a", random_string(32 * KB)); + ctx.set("state-b", random_string(32 * KB)); + Ok(()) + } + + async fn read_state( + &self, + ctx: ObjectContext<'_>, + _input: String, + ) -> Result { + let a: String = ctx.get("state-a").await?.unwrap_or_default(); + let b: String = ctx.get("state-b").await?.unwrap_or_default(); + Ok((a.len() + b.len()) as i32) + } + + async fn read_large_state( + &self, + ctx: ObjectContext<'_>, + _input: String, + ) -> Result { + let data: String = ctx.get("large-state").await?.unwrap_or_default(); + Ok(data.len() as i32) + } +} diff --git a/e2e-tests/services/rust/src/util.rs b/e2e-tests/services/rust/src/util.rs new file mode 100644 index 00000000..2a69f3e7 --- /dev/null +++ b/e2e-tests/services/rust/src/util.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2023 - 2026 Restate Software, Inc., Restate GmbH +// +// This file is part of the Restate e2e test suite, +// which is released under the MIT license. +// +// You can find a copy of the license in file LICENSE in the root +// directory of this repository or package, or at +// https://github.com/restatedev/e2e/blob/main/LICENSE + +//! Helpers shared across the Rust-side service implementations. + +use rand::RngExt; +use rand::distr::Alphanumeric; + +pub(crate) const KB: usize = 1024; + +/// Returns an alphanumeric string of the given length. The output resists +/// compression, which matters because the tests rely on each `ctx.run` block / +/// state value actually consuming the configured memory budget. +pub(crate) fn random_string(len: usize) -> String { + let mut rng = rand::rng(); + (&mut rng) + .sample_iter(Alphanumeric) + .take(len) + .map(char::from) + .collect() +} diff --git a/e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/MemoryPressureService.kt b/e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/MemoryPressureService.kt new file mode 100644 index 00000000..9cf96f69 --- /dev/null +++ b/e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/MemoryPressureService.kt @@ -0,0 +1,27 @@ +// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH +// +// This file is part of the Restate SDK Test suite tool, +// which is released under the MIT license. +// +// You can find a copy of the license in file LICENSE in the root +// directory of this repository or package, or at +// https://github.com/restatedev/sdk-test-suite/blob/main/LICENSE +package dev.restate.sdktesting.contracts + +import dev.restate.sdk.annotation.Handler +import dev.restate.sdk.annotation.Name +import dev.restate.sdk.annotation.Service + +/** + * Contract for the basic service used by `InvokerMemoryTest`. The implementation lives in the Rust + * crate at `e2e-tests/services/invoker-memory-rs` and is shipped as + * `ghcr.io/restatedev/e2e-invoker-memory-rs`. If you change a handler name or signature here, + * update the Rust trait in lockstep — see that crate's README for the mapping table. + */ +@Service +@Name("MemoryPressureService") +interface MemoryPressureService { + @Handler suspend fun generate(input: String): String + + @Handler suspend fun generateOversized(input: String): String +} diff --git a/e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/StatefulObject.kt b/e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/StatefulObject.kt new file mode 100644 index 00000000..c1fba7f0 --- /dev/null +++ b/e2e-tests/src/main/kotlin/dev/restate/sdktesting/contracts/StatefulObject.kt @@ -0,0 +1,29 @@ +// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH +// +// This file is part of the Restate SDK Test suite tool, +// which is released under the MIT license. +// +// You can find a copy of the license in file LICENSE in the root +// directory of this repository or package, or at +// https://github.com/restatedev/sdk-test-suite/blob/main/LICENSE +package dev.restate.sdktesting.contracts + +import dev.restate.sdk.annotation.Handler +import dev.restate.sdk.annotation.Name +import dev.restate.sdk.annotation.VirtualObject + +/** + * Contract for the virtual object used by `InvokerMemoryTest`. The implementation lives in the Rust + * crate at `e2e-tests/services/invoker-memory-rs` and is shipped as + * `ghcr.io/restatedev/e2e-invoker-memory-rs`. If you change a handler name or signature here, + * update the Rust trait in lockstep — see that crate's README for the mapping table. + */ +@VirtualObject +@Name("StatefulObject") +interface StatefulObject { + @Handler suspend fun initState(input: String) + + @Handler suspend fun readState(input: String): Int + + @Handler suspend fun readLargeState(input: String): Int +} diff --git a/e2e-tests/src/main/kotlin/dev/restate/sdktesting/tests/InvokerMemoryTest.kt b/e2e-tests/src/main/kotlin/dev/restate/sdktesting/tests/InvokerMemoryTest.kt index a62adc50..764421c1 100644 --- a/e2e-tests/src/main/kotlin/dev/restate/sdktesting/tests/InvokerMemoryTest.kt +++ b/e2e-tests/src/main/kotlin/dev/restate/sdktesting/tests/InvokerMemoryTest.kt @@ -16,28 +16,21 @@ import dev.restate.client.Client import dev.restate.client.kotlin.response import dev.restate.client.kotlin.toService import dev.restate.client.kotlin.toVirtualObject -import dev.restate.sdk.annotation.Handler -import dev.restate.sdk.annotation.Name -import dev.restate.sdk.annotation.Service -import dev.restate.sdk.annotation.VirtualObject -import dev.restate.sdk.endpoint.Endpoint -import dev.restate.sdk.kotlin.endpoint.journalRetention -import dev.restate.sdk.kotlin.get -import dev.restate.sdk.kotlin.runBlock -import dev.restate.sdk.kotlin.set -import dev.restate.sdk.kotlin.state +import dev.restate.sdktesting.contracts.MemoryPressureService +import dev.restate.sdktesting.contracts.StatefulObject +import dev.restate.sdktesting.infra.ContainerServiceDeploymentConfig import dev.restate.sdktesting.infra.InjectAdminURI import dev.restate.sdktesting.infra.InjectClient import dev.restate.sdktesting.infra.InjectContainerPort import dev.restate.sdktesting.infra.RESTATE_RUNTIME import dev.restate.sdktesting.infra.RUNTIME_NODE_PORT import dev.restate.sdktesting.infra.RestateDeployerExtension +import dev.restate.sdktesting.infra.ServiceSpec import java.net.URI import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse import kotlin.time.Duration.Companion.minutes -import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking @@ -70,51 +63,22 @@ import org.junit.jupiter.api.extension.RegisterExtension */ class InvokerMemoryTest { - @Service - @Name("MemoryPressureService") - class MemoryPressureService { - @Handler - suspend fun generate(input: String): String { - for (i in 0 until 10) { - runBlock { randomString(64.kb) } - } - return "ok-$input" - } - - @Handler - suspend fun generateOversized(input: String): String { - // Single side effect producing 512KiB — exceeds the 256KiB per-invocation memory limit. - // The invocation can never make progress and should be paused by the server. - runBlock { randomString(512.kb) } - return "ok-$input" - } - } - - @VirtualObject - @Name("StatefulObject") - class StatefulObject { - @Handler - suspend fun initState(input: String) { - // Store two 32KiB state entries (64KiB total per virtual object) - state().set("state-a", randomString(32.kb)) - state().set("state-b", randomString(32.kb)) - } - - @Handler - suspend fun readState(input: String): Int { - val a = state().get("state-a") ?: "" - val b = state().get("state-b") ?: "" - return a.length + b.length - } + companion object { + /** + * Aggregate Rust image hosting every Rust-side e2e service, including the two consumed by this + * test. See `e2e-tests/services/rust/README.md` for the publish workflow and the list of + * services the image hosts. Override with the `E2E_TEST_SERVICES_RS_IMAGE` env var when + * iterating against an unpublished image locally. + */ + private const val DEFAULT_E2E_RS_IMAGE = "ghcr.io/restatedev/e2e-test-services-rs:0.1.0" - @Handler - suspend fun readLargeState(input: String): Int { - val data = state().get("large-state") ?: "" - return data.length - } - } + /** + * Spec name for the Rust service deployment. Doubles as the container hostname on the + * Testcontainers bridge network, so the runtime discovers the service at + * `http://invoker-memory:9080/`. + */ + private const val SERVICE_SPEC_NAME = "invoker-memory" - companion object { /** * Fetch the invoker memory pool usage from the Prometheus metrics endpoint. Returns the value * of `restate_memory_pool_usage_bytes{name="invoker"}` as a [Double]. @@ -152,12 +116,18 @@ class InvokerMemoryTest { withEnv("RESTATE_DEFAULT_RETRY_POLICY__MAX_ATTEMPTS", "10") withEnv("RESTATE_DEFAULT_RETRY_POLICY__ON_MAX_ATTEMPTS", "pause") - // Disable journal retention so that completed journal entries are cleaned up immediately. - // This test creates large payloads in journal steps and queries sys_journal at the end, - // so retaining them would bloat the table and slow down the test. - withEndpoint( - Endpoint.bind(MemoryPressureService()) { it.journalRetention = 0.seconds } - .bind(StatefulObject()) { it.journalRetention = 0.seconds }) + // Deploy the aggregate Rust services image as a sibling container on the same bridge + // network as the Restate runtime. The image hosts every Rust-side e2e service; the + // `SERVICES=...` env var injected by ServiceSpec.withServices(...) picks the subset bound + // for this test. Journal retention is set to zero inside the Rust binary (see + // `e2e-tests/services/rust/src/main.rs`) — the test creates large payloads in journal + // steps and queries sys_journal at teardown, so retaining them would bloat the table. + val image = System.getenv("E2E_TEST_SERVICES_RS_IMAGE") ?: DEFAULT_E2E_RS_IMAGE + withServiceDeploymentConfig( + SERVICE_SPEC_NAME, ContainerServiceDeploymentConfig(image, emptyMap())) + withServiceSpec( + ServiceSpec.builder(SERVICE_SPEC_NAME) + .withServices("MemoryPressureService", "StatefulObject")) } } diff --git a/infra/src/main/kotlin/dev/restate/sdktesting/infra/RestateDeployer.kt b/infra/src/main/kotlin/dev/restate/sdktesting/infra/RestateDeployer.kt index 2ca18cb1..55c631d5 100644 --- a/infra/src/main/kotlin/dev/restate/sdktesting/infra/RestateDeployer.kt +++ b/infra/src/main/kotlin/dev/restate/sdktesting/infra/RestateDeployer.kt @@ -110,6 +110,19 @@ private constructor( this.serviceEndpoints.add(builder.build()) } + /** + * Register a [ServiceDeploymentConfig] (e.g. a container image) for the given service spec + * name. Lets a test bind a specific image to its [ServiceSpec] without going through the global + * `--service-container-image` CLI flag. + */ + fun withServiceDeploymentConfig(name: String, deploymentConfig: ServiceDeploymentConfig) = + apply { + this.config = + this.config.copy( + serviceDeploymentConfig = + this.config.serviceDeploymentConfig + (name to deploymentConfig)) + } + /** Add a container that will be added within the same network of functions and runtime. */ fun withContainer(hostName: String, container: GenericContainer<*>) = apply { this.additionalContainers[hostName] = container