diff --git a/.woodpecker/debug.yaml b/.woodpecker/debug.yaml index 4dc7d3c9..65dab9ab 100644 --- a/.woodpecker/debug.yaml +++ b/.woodpecker/debug.yaml @@ -1,6 +1,3 @@ -labels: - nix: "enabled" - when: event: - push @@ -12,32 +9,27 @@ when: steps: - name: check formatting - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - - nix-build -j4 --attr flakePackages.fmt + - nix-shell --attr devShell --run "cargo fmt -- --check" - name: build - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - nix-build -j4 --attr flakePackages.dev - name: unit + func tests (lmdb) - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - nix-build -j4 --attr flakePackages.tests-lmdb - name: unit + func tests (sqlite) - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - nix-build -j4 --attr flakePackages.tests-sqlite - - name: unit + func tests (fjall) - image: nixpkgs/nix:nixos-24.05 - commands: - - nix-build -j4 --attr flakePackages.tests-fjall - - name: integration tests - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - nix-build -j4 --attr flakePackages.dev - nix-shell --attr ci --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) diff --git a/.woodpecker/publish.yaml b/.woodpecker/publish.yaml index 8f3b482f..7522d58d 100644 --- a/.woodpecker/publish.yaml +++ b/.woodpecker/publish.yaml @@ -1,6 +1,3 @@ -labels: - nix: "enabled" - when: event: - deployment @@ -11,7 +8,7 @@ depends_on: steps: - name: refresh-index - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 environment: AWS_ACCESS_KEY_ID: from_secret: garagehq_aws_access_key_id @@ -22,7 +19,7 @@ steps: - nix-shell --attr ci --run "refresh_index" - name: multiarch-docker - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 environment: DOCKER_AUTH: from_secret: docker_auth diff --git a/.woodpecker/release.yaml b/.woodpecker/release.yaml index a94a9ccf..0678a45b 100644 --- a/.woodpecker/release.yaml +++ b/.woodpecker/release.yaml @@ -1,6 +1,3 @@ -labels: - nix: "enabled" - when: event: - deployment @@ -19,17 +16,17 @@ matrix: steps: - name: build - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - nix-build --attr releasePackages.${ARCH} --argstr git_version ${CI_COMMIT_TAG:-$CI_COMMIT_SHA} - name: check is static binary - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - nix-shell --attr ci --run "./script/not-dynamic.sh result/bin/garage" - name: integration tests - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - nix-shell --attr ci --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) when: @@ -39,7 +36,7 @@ steps: ARCH: i386 - name: upgrade tests - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 commands: - nix-shell --attr ci --run "./script/test-upgrade.sh v0.8.4 x86_64-unknown-linux-musl" || (cat /tmp/garage.log; false) when: @@ -47,7 +44,7 @@ steps: ARCH: amd64 - name: push static binary - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 environment: TARGET: "${TARGET}" AWS_ACCESS_KEY_ID: @@ -58,7 +55,7 @@ steps: - nix-shell --attr ci --run "to_s3" - name: docker build and publish - image: nixpkgs/nix:nixos-24.05 + image: nixpkgs/nix:nixos-22.05 environment: DOCKER_PLATFORM: "linux/${ARCH}" CONTAINER_NAME: "dxflrs/${ARCH}_garage" diff --git a/Cargo.lock b/Cargo.lock index 7473d9af..25f3a068 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 4 [[package]] name = "addr2line" -version = "0.25.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -54,22 +54,22 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.12" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.3.4", + "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -80,6 +80,12 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -91,9 +97,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -106,53 +112,50 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.11" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "arc-swap" -version = "1.8.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" -dependencies = [ - "rustversion", -] +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "argon2" @@ -184,14 +187,16 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.37" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" +checksum = "c0cf008e5e1a9e9e22a7d3c9a4992e21a350290069e36d8fb72304ed17e8f2d2" dependencies = [ - "compression-codecs", - "compression-core", + "futures-core", + "memchr", "pin-project-lite", "tokio", + "zstd", + "zstd-safe", ] [[package]] @@ -213,18 +218,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] @@ -235,15 +240,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-credential-types" -version = "1.2.11" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd362783681b15d136480ad555a099e82ecd8e2d10a841e14dfd0078d67fee3" +checksum = "4471bef4c22a06d2c7a1b6492493d3fdf24a805323109d6874f9c94d5906ac14" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -253,9 +258,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.18" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "959dab27ce613e6c9658eb3621064d0e2027e5f2acb65bc526a43577facea557" +checksum = "0aff45ffe35196e593ea3b9dd65b320e51e2dda95aff4390bc459e461d09c6ad" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -270,6 +275,7 @@ dependencies = [ "fastrand", "http 0.2.12", "http-body 0.4.6", + "once_cell", "percent-encoding", "pin-project-lite", "tracing", @@ -278,32 +284,31 @@ dependencies = [ [[package]] name = "aws-sdk-config" -version = "1.99.0" +version = "1.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e62e5ffb669e13f084c4e1d89d687604e001187f61503606a7f8cc7a411995" +checksum = "2645fb2c8b9876a46a3d79f06aad47063baf054085ea887a1e6d6f159e8a7501" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", "aws-smithy-json", - "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", "bytes", - "fastrand", "http 0.2.12", + "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-s3" -version = "1.120.0" +version = "1.79.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06673901e961f20fa8d7da907da48f7ad6c1b383e3726c22bd418900f015abe1" +checksum = "a8f63ba8f5fca32061c7d62d866ef65470edde38d4c5f8a0ebb8ff40a0521e1c" dependencies = [ "aws-credential-types", "aws-runtime", @@ -313,7 +318,6 @@ dependencies = [ "aws-smithy-eventstream", "aws-smithy-http", "aws-smithy-json", - "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -324,9 +328,10 @@ dependencies = [ "hex", "hmac", "http 0.2.12", - "http 1.4.0", + "http 1.3.1", "http-body 0.4.6", "lru", + "once_cell", "percent-encoding", "regex-lite", "sha2", @@ -336,9 +341,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.3.7" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e523e1c4e8e7e8ff219d732988e22bfeae8a1cafdbe6d9eca1546fa080be7c" +checksum = "69d03c3c05ff80d54ff860fe38c726f6f494c639ae975203a101335f223386db" dependencies = [ "aws-credential-types", "aws-smithy-eventstream", @@ -350,7 +355,8 @@ dependencies = [ "hex", "hmac", "http 0.2.12", - "http 1.4.0", + "http 1.3.1", + "once_cell", "percent-encoding", "sha2", "time", @@ -359,9 +365,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.7" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee19095c7c4dda59f1697d028ce704c24b2d33c6718790c7f1d5a3015b4107c" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" dependencies = [ "futures-util", "pin-project-lite", @@ -370,14 +376,16 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.63.13" +version = "0.63.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23374b9170cbbcc6f5df8dc5ebb9b6c5c28a3c8f599f0e8b8b10eb6f4a5c6e74" +checksum = "b65d21e1ba6f2cdec92044f904356a19f5ad86961acf015741106cdfafd747c0" dependencies = [ "aws-smithy-http", "aws-smithy-types", "bytes", - "crc-fast", + "crc32c", + "crc32fast", + "crc64fast-nvme", "hex", "http 0.2.12", "http-body 0.4.6", @@ -390,9 +398,9 @@ dependencies = [ [[package]] name = "aws-smithy-eventstream" -version = "0.60.14" +version = "0.60.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc12f8b310e38cad85cf3bef45ad236f470717393c613266ce0a89512286b650" +checksum = "7c45d3dddac16c5c59d553ece225a88870cf81b7b813c9cc17b78cf4685eac7a" dependencies = [ "aws-smithy-types", "bytes", @@ -401,9 +409,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.62.6" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826141069295752372f8203c17f28e30c464d22899a43a0c9fd9c458d469c88b" +checksum = "c5949124d11e538ca21142d1fba61ab0a2a2c1bc3ed323cdb3e4b878bfb83166" dependencies = [ "aws-smithy-eventstream", "aws-smithy-runtime-api", @@ -411,10 +419,10 @@ dependencies = [ "bytes", "bytes-utils", "futures-core", - "futures-util", "http 0.2.12", - "http 1.4.0", + "http 1.3.1", "http-body 0.4.6", + "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", @@ -423,62 +431,51 @@ dependencies = [ [[package]] name = "aws-smithy-http-client" -version = "1.1.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e62db736db19c488966c8d787f52e6270be565727236fd5579eaa301e7bc4a" +checksum = "0497ef5d53065b7cd6a35e9c1654bd1fefeae5c52900d91d1b188b0af0f29324" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "h2 0.3.27", - "h2 0.4.13", + "h2 0.4.8", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", "hyper-rustls 0.24.2", "pin-project-lite", "rustls 0.21.12", - "rustls-native-certs 0.8.3", "tokio", "tracing", ] [[package]] name = "aws-smithy-json" -version = "0.61.9" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fa1213db31ac95288d981476f78d05d9cbb0353d22cdf3472cc05bb02f6551" +checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" dependencies = [ "aws-smithy-types", ] -[[package]] -name = "aws-smithy-observability" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1fcbefc7ece1d70dcce29e490f269695dfca2d2bacdeaf9e5c3f799e4e6a42" -dependencies = [ - "aws-smithy-runtime-api", -] - [[package]] name = "aws-smithy-runtime" -version = "1.9.8" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb5b6167fcdf47399024e81ac08e795180c576a20e4d4ce67949f9a88ae37dc1" +checksum = "f6328865e36c6fd970094ead6b05efd047d3a80ec5fc3be5e743910da9f2ebf8" dependencies = [ "aws-smithy-async", "aws-smithy-http", "aws-smithy-http-client", - "aws-smithy-observability", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", + "once_cell", "pin-project-lite", "pin-utils", "tokio", @@ -487,15 +484,15 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.10.0" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efce7aaaf59ad53c5412f14fc19b2d5c6ab2c3ec688d272fd31f76ec12f44fb0" +checksum = "3da37cf5d57011cb1753456518ec76e31691f1f474b73934a284eb2a1c76510f" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.4.0", + "http 1.3.1", "pin-project-lite", "tokio", "tracing", @@ -504,16 +501,16 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.3.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f172bcb02424eb94425db8aed1b6d583b5104d4d5ddddf22402c661a320048" +checksum = "836155caafba616c0ff9b07944324785de2ab016141c3550bd1c07882f8cee8f" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.4.0", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -525,23 +522,23 @@ dependencies = [ "serde", "time", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.14", ] [[package]] name = "aws-smithy-xml" -version = "0.60.13" +version = "0.60.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b2f670422ff42bf7065031e72b45bc52a3508bd089f743ea90731ca2b6ea57" +checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc" dependencies = [ "xmlparser", ] [[package]] name = "aws-types" -version = "1.3.11" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d980627d2dd7bfc32a3c025685a033eeab8d365cc840c631ef59d1b8f428164" +checksum = "3873f8deed8927ce8d04487630dc9ff73193bab64742a61d050e57a68dec4125" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -557,16 +554,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.2.15", "instant", "rand", ] [[package]] name = "backtrace" -version = "0.3.76" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", "cfg-if", @@ -574,7 +571,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link", + "windows-targets 0.52.6", ] [[package]] @@ -607,9 +604,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bincode" @@ -628,9 +625,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake2" @@ -652,15 +649,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" [[package]] name = "byteorder" @@ -670,9 +667,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytes-utils" @@ -686,23 +683,16 @@ dependencies = [ [[package]] name = "bytesize" -version = "1.3.3" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" - -[[package]] -name = "byteview" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6236364b88b9b6d0bc181ba374cf1ab55ba3ef97a1cb6f8cddad48a273767fb5" +checksum = "2d2c12f985c78475a6b8d629afd0c360260ef34cfef52efccdcfd31972f81c2e" [[package]] name = "cc" -version = "1.2.54" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ - "find-msvc-tools", "jobserver", "libc", "shlex", @@ -710,9 +700,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" @@ -728,10 +718,11 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", "num-traits", @@ -763,9 +754,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.54" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", "clap_derive", @@ -773,9 +764,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstream", "anstyle", @@ -785,50 +776,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] name = "clap_lex" -version = "0.7.7" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "compare" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0095f6103c2a8b44acd6fd15960c801dafebf02e21940360833e0673f48ba7" - -[[package]] -name = "compression-codecs" -version = "0.4.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" -dependencies = [ - "compression-core", - "zstd", - "zstd-safe", -] - -[[package]] -name = "compression-core" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "core-foundation" @@ -840,16 +808,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -867,9 +825,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -880,18 +838,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" -[[package]] -name = "crc-fast" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd92aca2c6001b1bf5ba0ff84ee74ec8501b52bbef0cac80bf25a6c1d87a83d" -dependencies = [ - "crc", - "digest", - "rustversion", - "spin 0.10.0", -] - [[package]] name = "crc32c" version = "0.6.8" @@ -903,27 +849,27 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] -name = "crossbeam-channel" -version = "0.5.15" +name = "crc64fast-nvme" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "4955638f00a809894c947f85a024020a20815b65a5eea633798ea7924edab2b3" dependencies = [ - "crossbeam-utils", + "crc", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.18" +name = "crossbeam-channel" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] @@ -937,16 +883,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-skiplist" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df29de440c58ca2cc6e587ec3d22347551a32435fbde9d2bff64e78a9ffa151b" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -955,9 +891,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "rand_core", @@ -975,9 +911,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -985,27 +921,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] @@ -1018,25 +954,11 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "dashmap" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "deranged" -version = "0.5.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -1071,20 +993,14 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] -[[package]] -name = "double-ended-peekable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0d05e1c0dbad51b52c38bda7adceef61b9efc2baf04acfe8726a8c4630a6f57" - [[package]] name = "dyn-clone" -version = "1.0.20" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "either" @@ -1101,18 +1017,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "env_logger" version = "0.10.2" @@ -1133,13 +1037,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "errno" -version = "0.3.14" +name = "err-derive" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "c34a887c8df3ed90498c1c437ce21f211c8e27672921a8ffa293cb8d6d4caa9e" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1160,35 +1078,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "find-msvc-tools" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" - [[package]] name = "fixedbitset" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "fjall" -version = "2.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25ad44cd4360a0448a9b5a0a6f1c7a621101cca4578706d43c9a821418aebc" -dependencies = [ - "byteorder", - "byteview", - "dashmap 6.1.0", - "log", - "lsm-tree", - "path-absolutize", - "std-semaphore", - "tempfile", - "xxhash-rust", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1201,17 +1096,11 @@ 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 = "form_urlencoded" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1276,7 +1165,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] @@ -1311,7 +1200,7 @@ dependencies = [ [[package]] name = "garage" -version = "1.3.1" +version = "1.1.0" dependencies = [ "assert-json-diff", "async-trait", @@ -1340,9 +1229,9 @@ dependencies = [ "git-version", "hex", "hmac", - "http 1.4.0", + "http 1.3.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-util", "k2v-client", "kuska-sodiumoxide", @@ -1361,16 +1250,16 @@ dependencies = [ "timeago", "tokio", "tracing", - "tracing-journald", "tracing-subscriber", ] [[package]] name = "garage_api_admin" -version = "1.3.1" +version = "1.1.0" dependencies = [ "argon2", "async-trait", + "err-derive", "futures", "garage_api_common", "garage_model", @@ -1378,14 +1267,13 @@ dependencies = [ "garage_table", "garage_util", "hex", - "http 1.4.0", - "hyper 1.8.1", + "http 1.3.1", + "hyper 1.6.0", "opentelemetry", "opentelemetry-prometheus", "prometheus", "serde", "serde_json", - "thiserror 2.0.18", "tokio", "tracing", "url", @@ -1393,7 +1281,7 @@ dependencies = [ [[package]] name = "garage_api_common" -version = "1.3.1" +version = "1.1.0" dependencies = [ "base64 0.21.7", "bytes", @@ -1401,16 +1289,18 @@ dependencies = [ "crc32c", "crc32fast", "crypto-common", + "err-derive", "futures", "garage_model", "garage_table", "garage_util", "hex", "hmac", - "http 1.4.0", + "http 1.3.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-util", + "idna 0.5.0", "md-5", "nom", "opentelemetry", @@ -1419,7 +1309,6 @@ dependencies = [ "serde_json", "sha1", "sha2", - "thiserror 2.0.18", "tokio", "tracing", "url", @@ -1427,22 +1316,22 @@ dependencies = [ [[package]] name = "garage_api_k2v" -version = "1.3.1" +version = "1.1.0" dependencies = [ "base64 0.21.7", + "err-derive", "futures", "garage_api_common", "garage_model", "garage_table", "garage_util", - "http 1.4.0", + "http 1.3.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "opentelemetry", "percent-encoding", "serde", "serde_json", - "thiserror 2.0.18", "tokio", "tracing", "url", @@ -1450,7 +1339,7 @@ dependencies = [ [[package]] name = "garage_api_s3" -version = "1.3.1" +version = "1.1.0" dependencies = [ "aes-gcm", "async-compression", @@ -1459,6 +1348,7 @@ dependencies = [ "chrono", "crc32c", "crc32fast", + "err-derive", "form_urlencoded", "futures", "garage_api_common", @@ -1469,11 +1359,11 @@ dependencies = [ "garage_table", "garage_util", "hex", - "http 1.4.0", + "http 1.3.1", "http-body-util", "http-range", "httpdate", - "hyper 1.8.1", + "hyper 1.6.0", "md-5", "multer", "opentelemetry", @@ -1485,17 +1375,16 @@ dependencies = [ "serde_json", "sha1", "sha2", - "thiserror 2.0.18", "tokio", "tokio-stream", - "tokio-util 0.7.18", + "tokio-util 0.7.14", "tracing", "url", ] [[package]] name = "garage_block" -version = "1.3.1" +version = "1.1.0" dependencies = [ "arc-swap", "async-compression", @@ -1513,34 +1402,33 @@ dependencies = [ "rand", "serde", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.14", "tracing", "zstd", ] [[package]] name = "garage_db" -version = "1.3.1" +version = "1.1.0" dependencies = [ - "fjall", + "err-derive", "heed", "mktemp", - "parking_lot", "r2d2", "r2d2_sqlite", "rusqlite", - "thiserror 2.0.18", "tracing", ] [[package]] name = "garage_model" -version = "1.3.1" +version = "1.1.0" dependencies = [ "async-trait", "base64 0.21.7", "blake2", "chrono", + "err-derive", "futures", "garage_block", "garage_db", @@ -1549,12 +1437,11 @@ dependencies = [ "garage_table", "garage_util", "hex", - "http 1.4.0", + "http 1.3.1", "parse_duration", "rand", "serde", "serde_bytes", - "thiserror 2.0.18", "tokio", "tracing", "zstd", @@ -1562,11 +1449,12 @@ dependencies = [ [[package]] name = "garage_net" -version = "1.3.1" +version = "1.1.0" dependencies = [ "arc-swap", "bytes", "cfg-if", + "err-derive", "futures", "hex", "kuska-handshake", @@ -1579,19 +1467,20 @@ dependencies = [ "rand", "rmp-serde", "serde", - "thiserror 2.0.18", "tokio", "tokio-stream", - "tokio-util 0.7.18", + "tokio-util 0.7.14", + "tracing", ] [[package]] name = "garage_rpc" -version = "1.3.1" +version = "1.1.0" dependencies = [ "arc-swap", "async-trait", "bytesize", + "err-derive", "format_table", "futures", "garage_net", @@ -1612,14 +1501,13 @@ dependencies = [ "serde", "serde_bytes", "serde_json", - "thiserror 2.0.18", "tokio", "tracing", ] [[package]] name = "garage_table" -version = "1.3.1" +version = "1.1.0" dependencies = [ "arc-swap", "async-trait", @@ -1640,20 +1528,21 @@ dependencies = [ [[package]] name = "garage_util" -version = "1.3.1" +version = "1.1.0" dependencies = [ "arc-swap", "async-trait", "blake2", "bytesize", "chrono", + "err-derive", "futures", "garage_db", "garage_net", "hex", "hexdump", - "http 1.4.0", - "hyper 1.8.1", + "http 1.3.1", + "hyper 1.6.0", "lazy_static", "mktemp", "opentelemetry", @@ -1663,7 +1552,6 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 2.0.18", "tokio", "toml", "tracing", @@ -1672,19 +1560,19 @@ dependencies = [ [[package]] name = "garage_web" -version = "1.3.1" +version = "1.1.0" dependencies = [ + "err-derive", "garage_api_common", "garage_api_s3", "garage_model", "garage_table", "garage_util", - "http 1.4.0", + "http 1.3.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "opentelemetry", "percent-encoding", - "thiserror 2.0.18", "tokio", "tracing", ] @@ -1711,25 +1599,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.17" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.3.4" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", "r-efi", - "wasip2", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1744,9 +1632,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.32.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git-version" @@ -1765,20 +1653,14 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] -[[package]] -name = "guardian" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" - [[package]] name = "h2" -version = "0.3.27" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1786,29 +1668,29 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.13.0", + "indexmap 2.8.0", "slab", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.14", "tracing", ] [[package]] name = "h2" -version = "0.4.13" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.4.0", - "indexmap 2.13.0", + "http 1.3.1", + "indexmap 2.8.0", "slab", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.14", "tracing", ] @@ -1830,31 +1712,22 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.15.2" 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" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", - "foldhash 0.2.0", + "foldhash", ] [[package]] name = "hashlink" -version = "0.10.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.14.5", ] [[package]] @@ -1912,9 +1785,15 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" [[package]] name = "hex" @@ -1942,11 +1821,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.12" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1962,11 +1841,12 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", + "fnv", "itoa", ] @@ -1988,7 +1868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http 1.3.1", ] [[package]] @@ -1999,7 +1879,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] @@ -2030,9 +1910,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "hyper" @@ -2044,14 +1924,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.27", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.10", + "socket2", "tokio", "tower-service", "tracing", @@ -2060,22 +1940,20 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ - "atomic-waker", "bytes", "futures-channel", - "futures-core", - "h2 0.4.13", - "http 1.4.0", + "futures-util", + "h2 0.4.8", + "http 1.3.1", "http-body 1.0.1", "httparse", "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -2104,8 +1982,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 1.4.0", - "hyper 1.8.1", + "http 1.3.1", + "hyper 1.6.0", "hyper-util", "rustls 0.22.4", "rustls-native-certs 0.7.3", @@ -2129,42 +2007,33 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ - "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", - "hyper 1.8.1", - "ipnet", - "libc", - "percent-encoding", + "hyper 1.6.0", "pin-project-lite", - "socket2 0.6.2", - "system-configuration 0.6.1", + "socket2", "tokio", - "tower-layer", "tower-service", "tracing", - "windows-registry", ] [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", - "log", "wasm-bindgen", "windows-core", ] @@ -2180,22 +2049,21 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", - "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locale_core" -version = "2.1.1" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", @@ -2205,60 +2073,98 @@ dependencies = [ ] [[package]] -name = "icu_normalizer" -version = "2.1.1" +name = "icu_locid_transform" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", + "utf16_iter", + "utf8_iter", + "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] name = "icu_properties" -version = "2.1.2" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ + "displaydoc", "icu_collections", - "icu_locale_core", + "icu_locid_transform", "icu_properties_data", "icu_provider", - "zerotrie", + "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" -version = "2.1.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", - "icu_locale_core", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", "writeable", "yoke", "zerofrom", - "zerotrie", "zerovec", ] +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2267,9 +2173,19 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.1.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", @@ -2278,9 +2194,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", @@ -2298,12 +2214,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.15.2", ] [[package]] @@ -2324,15 +2240,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "interval-heap" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11274e5e8e89b8607cfedc2910b6626e998779b48a019151c7604d0adcb86ac6" -dependencies = [ - "compare", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -2350,20 +2257,20 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.17" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi", + "hermit-abi 0.5.0", "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.2" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -2385,25 +2292,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ - "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -2440,12 +2346,12 @@ dependencies = [ "aws-sdk-config", "aws-sigv4", "base64 0.21.7", - "clap 4.5.54", + "clap 4.5.32", "format_table", "hex", - "http 1.4.0", + "http 1.3.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.6.0", "hyper-rustls 0.26.0", "hyper-util", "log", @@ -2453,7 +2359,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 2.0.18", + "thiserror 1.0.69", "tokio", "tracing-subscriber", ] @@ -2514,7 +2420,7 @@ dependencies = [ "serde_yaml", "thiserror 1.0.69", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.14", "tower", "tower-http", "tracing", @@ -2548,7 +2454,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] @@ -2566,14 +2472,14 @@ dependencies = [ "json-patch", "k8s-openapi", "kube-client", - "parking_lot", + "parking_lot 0.12.3", "pin-project", "serde", "serde_json", "smallvec", "thiserror 1.0.69", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.14", "tracing", ] @@ -2609,9 +2515,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libsodium-sys" @@ -2627,9 +2533,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.35.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "pkg-config", @@ -2644,15 +2550,15 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" -version = "0.8.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lmdb-rkv-sys" @@ -2667,65 +2573,36 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.14" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.29" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "lru" -version = "0.16.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.15.2", ] -[[package]] -name = "lsm-tree" -version = "2.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799399117a2bfb37660e08be33f470958babb98386b04185288d829df362ea15" -dependencies = [ - "byteorder", - "crossbeam-skiplist", - "double-ended-peekable", - "enum_dispatch", - "guardian", - "interval-heap", - "log", - "lz4_flex", - "path-absolutize", - "quick_cache", - "rustc-hash", - "self_cell", - "tempfile", - "value-log", - "varint-rs", - "xxhash-rust", -] - -[[package]] -name = "lz4_flex" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" - [[package]] name = "matchers" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -2740,9 +2617,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -2758,22 +2635,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.9" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.1.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", - "windows-sys 0.61.2", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", ] [[package]] @@ -2794,11 +2671,11 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.4.0", + "http 1.3.1", "httparse", "memchr", "mime", - "spin 0.9.8", + "spin", "version_check", ] @@ -2814,7 +2691,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.0", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -2838,11 +2715,12 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.3" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ - "windows-sys 0.61.2", + "overload", + "winapi", ] [[package]] @@ -2882,9 +2760,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" @@ -2929,34 +2807,28 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.17.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "object" -version = "0.37.3" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "opaque-debug" @@ -2970,12 +2842,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - [[package]] name = "opentelemetry" version = "0.17.0" @@ -2984,7 +2850,7 @@ checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" dependencies = [ "async-trait", "crossbeam-channel", - "dashmap 4.0.2", + "dashmap", "fnv", "futures-channel", "futures-executor", @@ -3053,6 +2919,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "page_size" version = "0.4.2" @@ -3065,25 +2937,50 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.5" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", ] [[package]] name = "parking_lot_core" -version = "0.9.12" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.10", "smallvec", - "windows-link", + "windows-targets 0.52.6", ] [[package]] @@ -3109,54 +3006,43 @@ dependencies = [ ] [[package]] -name = "path-absolutize" -version = "3.1.1" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5" -dependencies = [ - "path-dedot", -] - -[[package]] -name = "path-dedot" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" -dependencies = [ - "once_cell", -] +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem" -version = "3.0.6" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", - "serde_core", + "serde", ] [[package]] name = "percent-encoding" -version = "2.3.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.5" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", + "thiserror 2.0.12", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.5" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -3164,23 +3050,24 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.5" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] name = "pest_meta" -version = "2.8.5" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ + "once_cell", "pest", "sha2", ] @@ -3192,7 +3079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.13.0", + "indexmap 2.8.0", ] [[package]] @@ -3212,7 +3099,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] @@ -3277,15 +3164,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -3298,7 +3176,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.23", ] [[package]] @@ -3337,9 +3215,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -3354,7 +3232,7 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot", + "parking_lot 0.12.3", "protobuf", "thiserror 1.0.69", ] @@ -3428,30 +3306,20 @@ dependencies = [ "serde", ] -[[package]] -name = "quick_cache" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ada44a88ef953a3294f6eb55d2007ba44646015e18613d2f213016379203ef3" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", -] - [[package]] name = "quote" -version = "1.0.44" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.3.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "r2d2" @@ -3460,15 +3328,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" dependencies = [ "log", - "parking_lot", + "parking_lot 0.12.3", "scheduled-thread-pool", ] [[package]] name = "r2d2_sqlite" -version = "0.31.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63417e83dc891797eea3ad379f52a5986da4bca0d6ef28baf4d14034dd111b0c" +checksum = "6a982edf65c129796dba72f8775b292ef482b40d035e827a9825b3bc07ccc5f2" dependencies = [ "r2d2", "rusqlite", @@ -3502,52 +3370,76 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.2.15", ] [[package]] name = "redox_syscall" -version = "0.5.18" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags 2.10.0", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags 2.9.0", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] [[package]] name = "regex-lite" -version = "0.1.8" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -3560,7 +3452,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.27", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", @@ -3578,7 +3470,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration 0.5.1", + "system-configuration", "tokio", "tokio-rustls 0.24.1", "tower-service", @@ -3597,7 +3489,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.17", + "getrandom 0.2.15", "libc", "untrusted", "windows-sys 0.52.0", @@ -3605,19 +3497,22 @@ dependencies = [ [[package]] name = "rmp" -version = "0.8.15" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" dependencies = [ + "byteorder", "num-traits", + "paste", ] [[package]] name = "rmp-serde" -version = "1.3.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" dependencies = [ + "byteorder", "rmp", "serde", ] @@ -3630,11 +3525,11 @@ checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" [[package]] name = "rusqlite" -version = "0.37.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -3644,15 +3539,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.27" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" @@ -3669,7 +3558,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3678,15 +3567,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "linux-raw-sys 0.9.3", + "windows-sys 0.59.0", ] [[package]] @@ -3720,10 +3609,10 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ - "openssl-probe 0.1.6", + "openssl-probe", "rustls-pemfile 1.0.4", "schannel", - "security-framework 2.11.1", + "security-framework", ] [[package]] @@ -3732,23 +3621,11 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ - "openssl-probe 0.1.6", + "openssl-probe", "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework 2.11.1", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe 0.2.1", - "rustls-pki-types", - "schannel", - "security-framework 3.5.1", + "security-framework", ] [[package]] @@ -3771,12 +3648,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "zeroize", -] +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" @@ -3801,15 +3675,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -3822,11 +3696,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3835,7 +3709,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot", + "parking_lot 0.12.3", ] [[package]] @@ -3859,7 +3733,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] @@ -3894,21 +3768,8 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.10.1", + "bitflags 2.9.0", + "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", @@ -3916,33 +3777,26 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", ] -[[package]] -name = "self_cell" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" - [[package]] name = "semver" -version = "1.0.27" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ - "serde_core", "serde_derive", ] @@ -3958,32 +3812,22 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.19" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" dependencies = [ "serde", - "serde_core", -] - -[[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" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] @@ -3994,27 +3838,26 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", + "ryu", "serde", - "serde_core", - "zmij", ] [[package]] name = "serde_spanned" -version = "0.6.9" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -4037,7 +3880,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.8.0", "itoa", "ryu", "serde", @@ -4057,9 +3900,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.9" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -4083,44 +3926,36 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.8" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ - "errno", "libc", ] [[package]] name = "slab" -version = "0.4.11" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "libc", - "windows-sys 0.52.0", + "autocfg", ] [[package]] -name = "socket2" -version = "0.6.2" +name = "smallvec" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -4129,29 +3964,23 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" - [[package]] name = "stable_deref_trait" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_init" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bae1df58c5fea7502e8e352ec26b5579f6178e1fdb311e088580c980dee25ed" +checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" dependencies = [ "bitflags 1.3.2", - "cfg_aliases 0.2.1", + "cfg_aliases 0.1.1", "libc", - "parking_lot", - "parking_lot_core", + "parking_lot 0.11.2", + "parking_lot_core 0.8.6", "static_init_macro", "winapi", ] @@ -4169,12 +3998,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "std-semaphore" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ae9eec00137a8eed469fb4148acd9fc6ac8c3f9b110f52cd34698c8b5bfa0e" - [[package]] name = "strsim" version = "0.11.1" @@ -4224,9 +4047,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -4250,13 +4073,25 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.2" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] @@ -4277,19 +4112,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", - "system-configuration-sys 0.5.0", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "system-configuration-sys 0.6.0", + "core-foundation", + "system-configuration-sys", ] [[package]] @@ -4302,27 +4126,17 @@ dependencies = [ "libc", ] -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" -version = "3.24.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.3.2", "once_cell", - "rustix 1.1.3", - "windows-sys 0.61.2", + "rustix 1.0.3", + "windows-sys 0.59.0", ] [[package]] @@ -4354,11 +4168,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.12", ] [[package]] @@ -4369,54 +4183,55 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", + "once_cell", ] [[package]] name = "time" -version = "0.3.46" +version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +checksum = "9d9c75b47bdff86fa3334a3db91356b8d7d86a9b839dab7d0bdc5c3d3a077618" dependencies = [ "deranged", "num-conv", "powerfmt", - "serde_core", + "serde", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.26" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +checksum = "29aa485584182073ed57fd5004aa09c371f021325014694e432313345865fd04" dependencies = [ "num-conv", "time-core", @@ -4430,36 +4245,52 @@ checksum = "a1710e589de0a76aaf295cd47a6699f6405737dbfd3cf2b75c92d000b548d0e6" [[package]] name = "tinystr" -version = "0.8.2" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] -name = "tokio" -version = "1.49.0" +name = "tinyvec" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" 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.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +dependencies = [ + "backtrace", "bytes", "libc", "mio", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "socket2", "tokio-macros", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] name = "tokio-io-timeout" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd86198d9ee903fedd2f9a2e72014287c0d9167e4ae43b5853007205dda1b76" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ "pin-project-lite", "tokio", @@ -4467,13 +4298,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] @@ -4499,9 +4330,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -4524,9 +4355,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", @@ -4539,9 +4370,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", @@ -4551,20 +4382,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.8.0", "serde", "serde_spanned", "toml_datetime", @@ -4583,7 +4414,7 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "h2 0.3.27", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", @@ -4628,7 +4459,7 @@ dependencies = [ "rand", "slab", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.14", "tower-layer", "tower-service", "tracing", @@ -4641,7 +4472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ "base64 0.21.7", - "bitflags 2.10.0", + "bitflags 2.9.0", "bytes", "futures-core", "futures-util", @@ -4669,9 +4500,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.44" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -4681,20 +4512,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.31" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] [[package]] name = "tracing-core" -version = "0.1.36" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -4710,17 +4541,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tracing-journald" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3a81ed245bfb62592b1e2bc153e77656d94ee6a0497683a65a12ccaf2438d0" -dependencies = [ - "libc", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -4734,14 +4554,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex-automata", + "regex", "sharded-slab", "smallvec", "thread_local", @@ -4758,9 +4578,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.19.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ucd-trie" @@ -4769,10 +4589,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] -name = "unicode-ident" -version = "1.0.22" +name = "unicode-bidi" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] [[package]] name = "unicode-segmentation" @@ -4786,6 +4621,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" @@ -4810,16 +4651,21 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.8" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna", + "idna 1.0.3", "percent-encoding", - "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -4838,7 +4684,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.2.15", "rand", ] @@ -4848,29 +4694,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "value-log" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62fc7c4ce161f049607ecea654dca3f2d727da5371ae85e2e4f14ce2b98ed67c" -dependencies = [ - "byteorder", - "byteview", - "interval-heap", - "log", - "path-absolutize", - "rustc-hash", - "tempfile", - "varint-rs", - "xxhash-rust", -] - -[[package]] -name = "varint-rs" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" - [[package]] name = "vcpkg" version = "0.2.15" @@ -4910,40 +4733,52 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" +name = "wasi" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ - "wit-bindgen", + "wit-bindgen-rt", ] [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.100", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", - "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -4952,9 +4787,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4962,31 +4797,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ - "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", + "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5022,11 +4857,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.11" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5037,73 +4872,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.62.2" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", + "windows-targets 0.52.6", ] [[package]] name = "windows-link" -version = "0.2.1" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-sys" @@ -5132,24 +4912,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[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 = "windows-targets" version = "0.48.5" @@ -5174,30 +4936,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -5210,12 +4955,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -5228,12 +4967,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -5246,24 +4979,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -5276,12 +4997,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -5294,12 +5009,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -5312,12 +5021,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -5330,17 +5033,11 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] @@ -5356,16 +5053,25 @@ dependencies = [ ] [[package]] -name = "wit-bindgen" -version = "0.51.0" +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" -version = "0.6.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "xmlparser" @@ -5381,10 +5087,11 @@ checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" [[package]] name = "yoke" -version = "0.8.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ + "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -5392,34 +5099,54 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", - "synstructure", + "syn 2.0.100", + "synstructure 0.13.1", ] [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive 0.8.23", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] @@ -5439,32 +5166,21 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", - "synstructure", + "syn 2.0.100", + "synstructure 0.13.1", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zerovec" -version = "0.11.5" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", @@ -5473,21 +5189,15 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.100", ] -[[package]] -name = "zmij" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" - [[package]] name = "zstd" version = "0.13.3" @@ -5499,18 +5209,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.2.4" +version = "7.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" +version = "2.0.14+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index df4005a3..732f6f05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,18 +24,18 @@ default-members = ["src/garage"] # Internal Garage crates format_table = { version = "0.1.1", path = "src/format-table" } -garage_api_common = { version = "1.3.1", path = "src/api/common" } -garage_api_admin = { version = "1.3.1", path = "src/api/admin" } -garage_api_s3 = { version = "1.3.1", path = "src/api/s3" } -garage_api_k2v = { version = "1.3.1", path = "src/api/k2v" } -garage_block = { version = "1.3.1", path = "src/block" } -garage_db = { version = "1.3.1", path = "src/db", default-features = false } -garage_model = { version = "1.3.1", path = "src/model", default-features = false } -garage_net = { version = "1.3.1", path = "src/net" } -garage_rpc = { version = "1.3.1", path = "src/rpc" } -garage_table = { version = "1.3.1", path = "src/table" } -garage_util = { version = "1.3.1", path = "src/util" } -garage_web = { version = "1.3.1", path = "src/web" } +garage_api_common = { version = "1.1.0", path = "src/api/common" } +garage_api_admin = { version = "1.1.0", path = "src/api/admin" } +garage_api_s3 = { version = "1.1.0", path = "src/api/s3" } +garage_api_k2v = { version = "1.1.0", path = "src/api/k2v" } +garage_block = { version = "1.1.0", path = "src/block" } +garage_db = { version = "1.1.0", path = "src/db", default-features = false } +garage_model = { version = "1.1.0", path = "src/model", default-features = false } +garage_net = { version = "1.1.0", path = "src/net" } +garage_rpc = { version = "1.1.0", path = "src/rpc" } +garage_table = { version = "1.1.0", path = "src/table" } +garage_util = { version = "1.1.0", path = "src/util" } +garage_web = { version = "1.1.0", path = "src/web" } k2v-client = { version = "0.0.4", path = "src/k2v-client" } # External crates from crates.io @@ -52,11 +52,13 @@ chrono = "0.4" crc32fast = "1.4" crc32c = "0.6" crypto-common = "0.1" +err-derive = "0.3" gethostname = "0.4" git-version = "0.3.4" hex = "0.4" hexdump = "0.1" hmac = "0.12" +idna = "0.5" itertools = "0.12" ipnet = "2.9.0" lazy_static = "1.4" @@ -64,7 +66,6 @@ md-5 = "0.10" mktemp = "0.5" nix = { version = "0.29", default-features = false, features = ["fs"] } nom = "7.1" -parking_lot = "0.12" parse_duration = "2.1" pin-project = "1.0.12" pnet_datalink = "0.34" @@ -83,14 +84,12 @@ pretty_env_logger = "0.5" structopt = { version = "0.3", default-features = false } syslog-tracing = "0.3" tracing = "0.1" -tracing-journald = "0.3.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } heed = { version = "0.11", default-features = false, features = ["lmdb"] } -rusqlite = "0.37" +rusqlite = "0.31.0" r2d2 = "0.8" -r2d2_sqlite = "0.31" -fjall = "2.4" +r2d2_sqlite = "0.24" async-compression = { version = "0.4", features = ["tokio", "zstd"] } zstd = { version = "0.13", default-features = false } @@ -136,7 +135,7 @@ prometheus = "0.13" aws-sigv4 = { version = "1.1", default-features = false } hyper-rustls = { version = "0.26", default-features = false, features = ["http1", "http2", "ring", "rustls-native-certs"] } log = "0.4" -thiserror = "2.0" +thiserror = "1.0" # ---- used only as build / dev dependencies ---- assert-json-diff = "2.0" @@ -146,8 +145,12 @@ aws-smithy-runtime = { version = "1.8", default-features = false, features = ["t aws-sdk-config = { version = "1.62", default-features = false } aws-sdk-s3 = { version = "1.79", default-features = false, features = ["rt-tokio"] } +[profile.dev] +#lto = "thin" # disabled for now, adds 2-4 min to each CI build +lto = "off" + [profile.release] -lto = "thin" -codegen-units = 16 -opt-level = 3 -strip = "debuginfo" +lto = true +codegen-units = 1 +opt-level = "s" +strip = true diff --git a/doc/book/connect/apps/index.md b/doc/book/connect/apps/index.md index f52d434b..5ec9686c 100644 --- a/doc/book/connect/apps/index.md +++ b/doc/book/connect/apps/index.md @@ -12,7 +12,7 @@ In this section, we cover the following web applications: | [Mastodon](#mastodon) | ✅ | Natively supported | | [Matrix](#matrix) | ✅ | Tested with `synapse-s3-storage-provider` | | [ejabberd](#ejabberd) | ✅ | `mod_s3_upload` | -| [Pixelfed](#pixelfed) | ✅ | Natively supported | +| [Pixelfed](#pixelfed) | ❓ | Not yet tested | | [Pleroma](#pleroma) | ❓ | Not yet tested | | [Lemmy](#lemmy) | ✅ | Supported with pict-rs | | [Funkwhale](#funkwhale) | ❓ | Not yet tested | @@ -191,10 +191,10 @@ garage key create peertube-key Keep the Key ID and the Secret key in a pad, they will be needed later. -We need two buckets, one for normal videos (named peertube-videos) and one for webtorrent videos (named peertube-playlists). +We need two buckets, one for normal videos (named peertube-video) and one for webtorrent videos (named peertube-playlist). ```bash garage bucket create peertube-videos -garage bucket create peertube-playlists +garage bucket create peertube-playlist ``` Now we allow our key to read and write on these buckets: @@ -253,7 +253,7 @@ object_storage: proxify_private_files: false streaming_playlists: - bucket_name: 'peertube-playlists' + bucket_name: 'peertube-playlist' # Keep it empty for our example prefix: '' diff --git a/doc/book/connect/backup.md b/doc/book/connect/backup.md index 7e97d777..f39cc3b6 100644 --- a/doc/book/connect/backup.md +++ b/doc/book/connect/backup.md @@ -161,49 +161,3 @@ kopia repository validate-provider You can then run all the standard kopia commands: `kopia snapshot create`, `kopia mount`... Everything should work out-of-the-box. - -## Plakar - -Create your key and bucket on Garage server: - -```bash -garage key create my-plakar-key -garage bucket create plakar-backups -garage bucket allow plakar-backups --read --write --key my-plakar-key -… -``` - -On Plakar server, add your Garage as a storage location: -```bash -plakar store add garageS3 s3://my-garage.tld/plakar-backups \ -region=garage # Or as you've specified in garage.toml \ -access_key= \ -secret_access_key= -``` - -Then create the repository. -```bash -plakar at @garageS3 create -plaintext # Unencrypted -# or -plakar at @garageS3 create #encrypted -``` - -If you encrypt your backups (Plakar default), you will need to define a strong passphrase. Do not forget to save your password safely. It will be needed to decrypt your backups. - - -After the repository has been created, check that everything works as expected (that might give an empty result as no file has been added yet, but no error message): -```bash -plakar at @garageS3 check -``` - -Now that everything is configure, you can use Garage as your backups storage. For instance sync it with a local backup storage: -```bash -$ plakar at ~/backups sync to @garageS3 -``` - -Or list the S3 storage content: -```bash -$ plakar at @garageS3 ls -``` - -More information in Plakar documentation: https://www.plakar.io/docs/main/quickstart/ diff --git a/doc/book/cookbook/ansible.md b/doc/book/cookbook/ansible.md index 8b0d2969..6d624c9c 100644 --- a/doc/book/cookbook/ansible.md +++ b/doc/book/cookbook/ansible.md @@ -8,18 +8,18 @@ have published Ansible roles. We list them and compare them below. ## Comparison of Ansible roles -| Feature | [ansible-role-garage](#zorun-ansible-role-garage) | [garage-docker-ansible-deploy](#moan0s-garage-docker-ansible-deploy) | [eddster ansible-role-garage](#eddster-ansible-role-garage) | -|------------------------------------|---------------------------------------------|---------------------------------------------------------------|---------------------------------| -| **Runtime** | Systemd | Docker | Systemd | -| **Target OS** | Any Linux | Any Linux | Any Linux | -| **Architecture** | amd64, arm64, i686 | amd64, arm64 | arm64, arm, 386, amd64 | -| **Additional software** | None | Traefik | Ngnix and Keepalived (optional) | -| **Automatic node connection** | ❌ | ✅ | ✅ | -| **Layout management** | ❌ | ✅ | ✅ | -| **Manage buckets & keys** | ❌ | ✅ (basic) | ✅ | -| **Allow custom Garage config** | ✅ | ❌ | ❌ | -| **Facilitate Garage upgrades** | ✅ | ❌ | ✅ | -| **Multiple instances on one host** | ✅ | ✅ | ❌ | +| Feature | [ansible-role-garage](#zorun-ansible-role-garage) | [garage-docker-ansible-deploy](#moan0s-garage-docker-ansible-deploy) | +|------------------------------------|---------------------------------------------|---------------------------------------------------------------| +| **Runtime** | Systemd | Docker | +| **Target OS** | Any Linux | Any Linux | +| **Architecture** | amd64, arm64, i686 | amd64, arm64 | +| **Additional software** | None | Traefik | +| **Automatic node connection** | ❌ | ✅ | +| **Layout management** | ❌ | ✅ | +| **Manage buckets & keys** | ❌ | ✅ (basic) | +| **Allow custom Garage config** | ✅ | ❌ | +| **Facilitate Garage upgrades** | ✅ | ❌ | +| **Multiple instances on one host** | ✅ | ✅ | ## zorun/ansible-role-garage @@ -49,15 +49,3 @@ structured DNS names, etc). As a result, this role makes it easier to start with Garage on Ansible, but is less flexible. - -## eddster2309/ansible-role-garage - -[Source code](https://github.com/eddster2309/ansible-role-garage), [Ansible galaxy](https://galaxy.ansible.com/ui/standalone/roles/eddster2309/garage/) - -This role is a opinionated but customisable role using the official Garage -static binaries and only requires Systemd. As such it should work on any -Linux based host. It includes all the nesscary configuration to -automatically setup a clustered Garage deployment. Most Garage -configuration options are exposed through Ansible variables so while you -can't provide a custom config you can get very close. It can optionally -installed a HA nginx deployment with Keepalived. diff --git a/doc/book/cookbook/binary-packages.md b/doc/book/cookbook/binary-packages.md index ce6beb7b..0a6ad8fc 100644 --- a/doc/book/cookbook/binary-packages.md +++ b/doc/book/cookbook/binary-packages.md @@ -15,10 +15,9 @@ Alpine Linux repositories (available since v3.17): apk add garage ``` -The default configuration file is installed to `/etc/garage/garage.toml`. You can run -Garage using: `rc-service garage start`. - -If you don't specify `rpc_secret`, it will be automatically replaced with a random string on the first start. +The default configuration file is installed to `/etc/garage.toml`. You can run +Garage using: `rc-service garage start`. If you don't specify `rpc_secret`, it +will be automatically replaced with a random string on the first start. Please note that this package is built without Consul discovery, Kubernetes discovery, OpenTelemetry exporter, and K2V features (K2V will be enabled once @@ -27,7 +26,7 @@ it's stable). ## Arch Linux -Garage is available in the official repositories under [extra](https://archlinux.org/packages/extra/x86_64/garage). +Garage is available in the [AUR](https://aur.archlinux.org/packages/garage). ## FreeBSD diff --git a/doc/book/cookbook/kubernetes.md b/doc/book/cookbook/kubernetes.md index f5bceec8..af04e94d 100644 --- a/doc/book/cookbook/kubernetes.md +++ b/doc/book/cookbook/kubernetes.md @@ -11,7 +11,7 @@ Firstly clone the repository: ```bash git clone https://git.deuxfleurs.fr/Deuxfleurs/garage -cd garage/script/helm +cd garage/scripts/helm ``` Deploy with default options: @@ -26,13 +26,6 @@ Or deploy with custom values: helm install --create-namespace --namespace garage garage ./garage -f values.override.yaml ``` -If you want to manage the CustomRessourceDefinition used by garage for its `kubernetes_discovery` outside of the helm chart, add `garage.kubernetesSkipCrd: true` to your custom values and use the kustomization before deploying the helm chart: - -```bash -kubectl apply -k ../k8s/crd -helm install --create-namespace --namespace garage garage ./garage -f values.override.yaml -``` - After deploying, cluster layout must be configured manually as described in [Creating a cluster layout](@/documentation/quick-start/_index.md#creating-a-cluster-layout). Use the following command to access garage CLI: ```bash diff --git a/doc/book/cookbook/real-world.md b/doc/book/cookbook/real-world.md index b9927c06..594f1905 100644 --- a/doc/book/cookbook/real-world.md +++ b/doc/book/cookbook/real-world.md @@ -96,14 +96,14 @@ to store 2 TB of data in total. ## Get a Docker image Our docker image is currently named `dxflrs/garage` and is stored on the [Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated). -We encourage you to use a fixed tag (eg. `v1.3.0`) and not the `latest` tag. -For this example, we will use the latest published version at the time of the writing which is `v1.3.0` but it's up to you +We encourage you to use a fixed tag (eg. `v1.1.0`) and not the `latest` tag. +For this example, we will use the latest published version at the time of the writing which is `v1.1.0` but it's up to you to check [the most recent versions on the Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated). For example: ``` -sudo docker pull dxflrs/garage:v1.3.0 +sudo docker pull dxflrs/garage:v1.1.0 ``` ## Deploying and configuring Garage @@ -171,7 +171,7 @@ docker run \ -v /etc/garage.toml:/etc/garage.toml \ -v /var/lib/garage/meta:/var/lib/garage/meta \ -v /var/lib/garage/data:/var/lib/garage/data \ - dxflrs/garage:v1.3.0 + dxflrs/garage:v1.1.0 ``` With this command line, Garage should be started automatically at each boot. @@ -185,7 +185,7 @@ If you want to use `docker-compose`, you may use the following `docker-compose.y version: "3" services: garage: - image: dxflrs/garage:v1.3.0 + image: dxflrs/garage:v1.1.0 network_mode: "host" restart: unless-stopped volumes: diff --git a/doc/book/cookbook/systemd.md b/doc/book/cookbook/systemd.md index ebff8c15..c0ed7d1f 100644 --- a/doc/book/cookbook/systemd.md +++ b/doc/book/cookbook/systemd.md @@ -28,7 +28,6 @@ StateDirectory=garage DynamicUser=true ProtectHome=true NoNewPrivileges=true -LimitNOFILE=42000 [Install] WantedBy=multi-user.target diff --git a/doc/book/quick-start/_index.md b/doc/book/quick-start/_index.md index 633b785a..2db4211b 100644 --- a/doc/book/quick-start/_index.md +++ b/doc/book/quick-start/_index.md @@ -132,7 +132,7 @@ docker run \ -v /path/to/garage.toml:/etc/garage.toml \ -v /path/to/garage/meta:/var/lib/garage/meta \ -v /path/to/garage/data:/var/lib/garage/data \ - dxflrs/garage:v1.3.0 + dxflrs/garage:v1.1.0 ``` Under Linux, you can substitute `--network host` for `-p 3900:3900 -p 3901:3901 -p 3902:3902 -p 3903:3903` @@ -182,12 +182,11 @@ ID Hostname Address Tag Zone Capacit ## Creating a cluster layout Creating a cluster layout for a Garage deployment means informing Garage -of the disk space available on each node of the cluster, `-c`, -as well as the name of the zone (e.g. datacenter), `-z`, each machine is located in. +of the disk space available on each node of the cluster +as well as the zone (e.g. datacenter) each machine is located in. -For our test deployment, we are have only one node with zone named `dc1` and a -capacity of `1G`, though the capacity is ignored for a single node deployment -and can be changed later when adding new nodes. +For our test deployment, we are using only one node. The way in which we configure +it does not matter, you can simply write: ```bash garage layout assign -z dc1 -c 1G diff --git a/doc/book/reference-manual/configuration.md b/doc/book/reference-manual/configuration.md index 1f583fe6..e0fc17bc 100644 --- a/doc/book/reference-manual/configuration.md +++ b/doc/book/reference-manual/configuration.md @@ -24,8 +24,7 @@ db_engine = "lmdb" block_size = "1M" block_ram_buffer_max = "256MiB" -block_max_concurrent_reads = 16 -block_max_concurrent_writes_per_request =10 + lmdb_map_size = "1T" compression_level = 1 @@ -47,7 +46,6 @@ bootstrap_peers = [ "212fd62eeaca72c122b45a7f4fa0f55e012aa5e24ac384a72a3016413fa724ff@[fc00:F::1]:3901", ] -allow_punycode = false [consul_discovery] api = "catalog" @@ -94,32 +92,29 @@ The following gives details about each available configuration option. [Environment variables](#env_variables). -Top-level configuration options, in alphabetical order: -[`allow_punycode`](#allow_punycode), +Top-level configuration options: [`allow_world_readable_secrets`](#allow_world_readable_secrets), -[`block_max_concurrent_reads`](`block_max_concurrent_reads), [`block_ram_buffer_max`](#block_ram_buffer_max), -[`block_max_concurrent_writes_per_request`](#block_max_concurrent_writes_per_request), [`block_size`](#block_size), [`bootstrap_peers`](#bootstrap_peers), [`compression_level`](#compression_level), -[`consistency_mode`](#consistency_mode), [`data_dir`](#data_dir), [`data_fsync`](#data_fsync), [`db_engine`](#db_engine), [`disable_scrub`](#disable_scrub), +[`use_local_tz`](#use_local_tz), [`lmdb_map_size`](#lmdb_map_size), [`metadata_auto_snapshot_interval`](#metadata_auto_snapshot_interval), [`metadata_dir`](#metadata_dir), [`metadata_fsync`](#metadata_fsync), [`metadata_snapshots_dir`](#metadata_snapshots_dir), [`replication_factor`](#replication_factor), +[`consistency_mode`](#consistency_mode), [`rpc_bind_addr`](#rpc_bind_addr), [`rpc_bind_outgoing`](#rpc_bind_outgoing), [`rpc_public_addr`](#rpc_public_addr), [`rpc_public_addr_subnet`](#rpc_public_addr_subnet) -[`rpc_secret`/`rpc_secret_file`](#rpc_secret), -[`use_local_tz`](#use_local_tz). +[`rpc_secret`/`rpc_secret_file`](#rpc_secret). The `[consul_discovery]` section: [`api`](#consul_api), @@ -156,17 +151,13 @@ The `[admin]` section: ### Environment variables {#env_variables} -The following configuration parameters must be specified as environment variables, -they do not exist in the configuration file: +The following configuration parameter must be specified as an environment +variable, it does not exist in the configuration file: - `GARAGE_LOG_TO_SYSLOG` (since `v0.9.4`): set this to `1` or `true` to make the Garage daemon send its logs to `syslog` (using the libc `syslog` function) instead of printing to stderr. -- `GARAGE_LOG_TO_JOURNALD` (since `v1.2.0`): set this to `1` or `true` to make the - Garage daemon send its logs to `journald` (using the native protocol of `systemd-journald`) - instead of printing to stderr. - The following environment variables can be used to override the corresponding values in the configuration file: @@ -178,7 +169,7 @@ values in the configuration file: ### Top-level configuration options -#### `replication_factor` (since `v1.0.0`) {#replication_factor} +#### `replication_factor` {#replication_factor} The replication factor can be any positive integer smaller or equal the node count in your cluster. The chosen replication factor has a big impact on the cluster's failure tolerancy and performance characteristics. @@ -226,7 +217,7 @@ is in progress. In theory, no data should be lost as rebalancing is a routine operation for Garage, although we cannot guarantee you that everything will go right in such an extreme scenario. -#### `consistency_mode` (since `v1.0.0`) {#consistency_mode} +#### `consistency_mode` {#consistency_mode} The consistency mode setting determines the read and write behaviour of your cluster. @@ -336,7 +327,6 @@ Since `v0.8.0`, Garage can use alternative storage backends as follows: | --------- | ----------------- | ------------- | | [LMDB](https://www.symas.com/lmdb) (since `v0.8.0`, default since `v0.9.0`) | `"lmdb"` | `/db.lmdb/` | | [Sqlite](https://sqlite.org) (since `v0.8.0`) | `"sqlite"` | `/db.sqlite` | -| [Fjall](https://github.com/fjall-rs/fjall) (**experimental support** since `v1.3.0`) | `"fjall"` | `/db.fjall/` | | [Sled](https://sled.rs) (old default, removed since `v1.0`) | `"sled"` | `/db/` | Sled was supported until Garage v0.9.x, and was removed in Garage v1.0. @@ -373,14 +363,6 @@ LMDB works very well, but is known to have the following limitations: so it is not the best choice for high-performance storage clusters, but it should work fine in many cases. -- Fjall: a storage engine based on LSM trees, which theoretically allow for - higher write throughput than other storage engines that are based on B-trees. - Using Fjall could potentially improve Garage's performance significantly in - write-heavy workloads. **Support for Fjall is experimental at this point**, - we have added it to Garage for evaluation purposes only. **Do not use it for - production-critical workloads.** - - It is possible to convert Garage's metadata directory from one format to another using the `garage convert-db` command, which should be used as follows: @@ -418,7 +400,6 @@ Here is how this option impacts the different database engines: |----------|------------------------------------|-------------------------------| | Sqlite | `PRAGMA synchronous = OFF` | `PRAGMA synchronous = NORMAL` | | LMDB | `MDB_NOMETASYNC` + `MDB_NOSYNC` | `MDB_NOMETASYNC` | -| Fjall | default options | not supported | Note that the Sqlite database is always ran in `WAL` mode (`PRAGMA journal_mode = WAL`). @@ -525,37 +506,6 @@ node. The default value is 256MiB. -#### `block_max_concurrent_reads` (since `v1.3.0` / `v2.1.0`) {#block_max_concurrent_reads} - -The maximum number of blocks (individual files in the data directory) open -simultaneously for reading. - -Reducing this number does not limit the number of data blocks that can be -transferred through the network simultaneously. This mechanism was just added -as a backpressure mechanism for HDD read speed: it helps avoid a situation -where too many requests are coming in and Garage is reading too many block -files simultaneously, thus not making timely progress on any of the reads. - -When a request to read a data block comes in through the network, the requests -awaits for one of the `block_max_concurrent_reads` slots to be available -(internally implemented using a Semaphore object). Once it acquired a read -slot, it reads the entire block file to RAM and frees the slot as soon as the -block file is finished reading. Only after the slot is released will the -block's data start being transferred over the network. If the request fails to -acquire a reading slot wihtin 15 seconds, it fails with a timeout error. -Timeout events can be monitored through the `block_read_semaphore_timeouts` -metric in Prometheus: a non-zero number of such events indicates an I/O -bottleneck on HDD read speed. - - -#### `block_max_concurrent_writes_per_request` (since `v2.1.0`) {#block_max_concurrent_writes_per_request} - -This parameter is designed to adapt to the concurrent write performance of -different storage media.Maximum number of parallel block writes per put request -Higher values improve throughput but increase memory usage. - -Default: 3, Recommended: 10-30 for NVMe, 3-10 for HDD - #### `lmdb_map_size` {#lmdb_map_size} This parameters can be used to set the map size used by LMDB, @@ -654,7 +604,7 @@ be obtained by running `garage node id` and then included directly in the key will be returned by `garage node id` and you will have to add the IP yourself. -#### `allow_world_readable_secrets` or `GARAGE_ALLOW_WORLD_READABLE_SECRETS` (env) {#allow_world_readable_secrets} +### `allow_world_readable_secrets` or `GARAGE_ALLOW_WORLD_READABLE_SECRETS` (env) {#allow_world_readable_secrets} Garage checks the permissions of your secret files to make sure they're not world-readable. In some cases, the check might fail and consider your files as @@ -666,13 +616,6 @@ permission verification. Alternatively, you can set the `GARAGE_ALLOW_WORLD_READABLE_SECRETS` environment variable to `true` to bypass the permissions check. -#### `allow_punycode` {#allow_punycode} - -Allow creating buckets with names containing punycode. When used for buckets served -as websites, this allows using almost any unicode character in the domain name. - -Default to `false`. - ### The `[consul_discovery]` section Garage supports discovering other nodes of the cluster using Consul. For this diff --git a/doc/book/reference-manual/s3-compatibility.md b/doc/book/reference-manual/s3-compatibility.md index b869b6f4..d2c47f3e 100644 --- a/doc/book/reference-manual/s3-compatibility.md +++ b/doc/book/reference-manual/s3-compatibility.md @@ -23,17 +23,17 @@ Feel free to open a PR to suggest fixes this table. Minio is missing because the - 2022-05-25 - Many Ceph S3 endpoints are not documented but implemented. Following a notification from the Ceph community, we added them. + ## High-level features | Feature | Garage | [Openstack Swift](https://docs.openstack.org/swift/latest/s3_compat.html) | [Ceph Object Gateway](https://docs.ceph.com/en/latest/radosgw/s3/) | [Riak CS](https://docs.riak.com/riak/cs/2.1.1/references/apis/storage/s3/index.html) | [OpenIO](https://docs.openio.io/latest/source/arch-design/s3_compliancy.html) | |------------------------------|----------------------------------|-----------------|---------------|---------|-----| -| [signature v2](https://docs.aws.amazon.com/AmazonS3/latest/API/Appendix-Sigv2.html) (deprecated) | ❌ Missing | ✅ | ✅ | ✅ | ✅ | +| [signature v2](https://docs.aws.amazon.com/general/latest/gr/signature-version-2.html) (deprecated) | ❌ Missing | ✅ | ✅ | ✅ | ✅ | | [signature v4](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) | ✅ Implemented | ✅ | ✅ | ❌ | ✅ | | [URL path-style](https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#path-style-access) (eg. `host.tld/bucket/key`) | ✅ Implemented | ✅ | ✅ | ❓| ✅ | | [URL vhost-style](https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#virtual-hosted-style-access) URL (eg. `bucket.host.tld/key`) | ✅ Implemented | ❌| ✅| ✅ | ✅ | | [Presigned URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html) | ✅ Implemented | ❌| ✅ | ✅ | ✅(❓) | | [SSE-C encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerSideEncryptionCustomerKeys.html) | ✅ Implemented | ❓ | ✅ | ❌ | ✅ | -| [Bucket versioning](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Versioning.html) | ❌ Missing | ✅ | ✅ | ❌ | ✅ | *Note:* OpenIO does not says if it supports presigned URLs. Because it is part of signature v4 and they claim they support it without additional precisions, diff --git a/doc/drafts/admin-api.md b/doc/drafts/admin-api.md index 3ee948cb..acceefab 100644 --- a/doc/drafts/admin-api.md +++ b/doc/drafts/admin-api.md @@ -70,7 +70,7 @@ Example response body: ```json { "node": "b10c110e4e854e5aa3f4637681befac755154b20059ec163254ddbfae86b09df", - "garageVersion": "v1.3.0", + "garageVersion": "v1.1.0", "garageFeatures": [ "k2v", "lmdb", diff --git a/flake.lock b/flake.lock index 211b70e0..2cfbfda4 100644 --- a/flake.lock +++ b/flake.lock @@ -50,17 +50,17 @@ }, "nixpkgs": { "locked": { - "lastModified": 1763977559, - "narHash": "sha256-g4MKqsIRy5yJwEsI+fYODqLUnAqIY4kZai0nldAP6EM=", + "lastModified": 1736692550, + "narHash": "sha256-7tk8xH+g0sJkKLTJFOxphJxxOjMDFMWv24nXslaU2ro=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "cfe2c7d5b5d3032862254e68c37a6576b633d632", + "rev": "7c4869c47090dd7f9f1bdfb49a22aea026996815", "type": "github" }, "original": { "owner": "NixOS", "repo": "nixpkgs", - "rev": "cfe2c7d5b5d3032862254e68c37a6576b633d632", + "rev": "7c4869c47090dd7f9f1bdfb49a22aea026996815", "type": "github" } }, @@ -80,17 +80,17 @@ ] }, "locked": { - "lastModified": 1763952169, - "narHash": "sha256-+PeDBD8P+NKauH+w7eO/QWCIp8Cx4mCfWnh9sJmy9CM=", + "lastModified": 1738549608, + "narHash": "sha256-GdyT9QEUSx5k/n8kILuNy83vxxdyUfJ8jL5mMpQZWfw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "ab726555a9a72e6dc80649809147823a813fa95b", + "rev": "35c6f8c4352f995ecd53896200769f80a3e8f22d", "type": "github" }, "original": { "owner": "oxalica", "repo": "rust-overlay", - "rev": "ab726555a9a72e6dc80649809147823a813fa95b", + "rev": "35c6f8c4352f995ecd53896200769f80a3e8f22d", "type": "github" } }, diff --git a/flake.nix b/flake.nix index 01a077c4..fc599e0b 100644 --- a/flake.nix +++ b/flake.nix @@ -2,13 +2,13 @@ description = "Garage, an S3-compatible distributed object store for self-hosted deployments"; - # Nixpkgs 25.05 as of 2025-11-24 + # Nixpkgs 24.11 as of 2025-01-12 inputs.nixpkgs.url = - "github:NixOS/nixpkgs/cfe2c7d5b5d3032862254e68c37a6576b633d632"; + "github:NixOS/nixpkgs/7c4869c47090dd7f9f1bdfb49a22aea026996815"; - # Rust overlay as of 2025-11-24 + # Rust overlay as of 2025-02-03 inputs.rust-overlay.url = - "github:oxalica/rust-overlay/ab726555a9a72e6dc80649809147823a813fa95b"; + "github:oxalica/rust-overlay/35c6f8c4352f995ecd53896200769f80a3e8f22d"; inputs.rust-overlay.inputs.nixpkgs.follows = "nixpkgs"; inputs.crane.url = "github:ipetkov/crane"; @@ -30,10 +30,6 @@ inherit system nixpkgs crane rust-overlay extraTestEnv; release = false; }).garage-test; - lints = (compile { - inherit system nixpkgs crane rust-overlay; - release = false; - }); in { packages = { @@ -57,13 +53,6 @@ tests-sqlite = testWith { GARAGE_TEST_INTEGRATION_DB_ENGINE = "sqlite"; }; - tests-fjall = testWith { - GARAGE_TEST_INTEGRATION_DB_ENGINE = "fjall"; - }; - - # lints (fmt, clippy) - fmt = lints.garage-cargo-fmt; - clippy = lints.garage-cargo-clippy; }; # ---- developpment shell, for making native builds only ---- diff --git a/nix/compile.nix b/nix/compile.nix index c6df9dbd..8cd88d01 100644 --- a/nix/compile.nix +++ b/nix/compile.nix @@ -48,7 +48,7 @@ let inherit (pkgs) lib stdenv; - toolchainFn = (p: p.rust-bin.stable."1.91.0".default.override { + toolchainFn = (p: p.rust-bin.stable."1.82.0".default.override { targets = lib.optionals (target != null) [ rustTarget ]; extensions = [ "rust-src" @@ -68,13 +68,12 @@ let rootFeatures = if features != null then features else - ([ "bundled-libs" "lmdb" "sqlite" "fjall" "k2v" ] ++ (lib.optionals release [ + ([ "bundled-libs" "lmdb" "sqlite" "k2v" ] ++ (lib.optionals release [ "consul-discovery" "kubernetes-discovery" "metrics" "telemetry-otlp" "syslog" - "journald" ])); featuresStr = lib.concatStringsSep "," rootFeatures; @@ -190,15 +189,4 @@ in rec { pkgs.cacert ]; } // extraTestEnv); - - # ---- source code linting ---- - - garage-cargo-fmt = craneLib.cargoFmt (commonArgs // { - cargoExtraArgs = ""; - }); - - garage-cargo-clippy = craneLib.cargoClippy (commonArgs // { - cargoArtifacts = garage-deps; - cargoClippyExtraArgs = "--all-targets -- -D warnings"; - }); } diff --git a/script/dev-env-aws.sh b/script/dev-env-aws.sh index 41f1fdde..808f9cf1 100644 --- a/script/dev-env-aws.sh +++ b/script/dev-env-aws.sh @@ -1,7 +1,6 @@ export AWS_ACCESS_KEY_ID=`cat /tmp/garage.s3 |cut -d' ' -f1` export AWS_SECRET_ACCESS_KEY=`cat /tmp/garage.s3 |cut -d' ' -f2` export AWS_DEFAULT_REGION='garage' -export AWS_REQUEST_CHECKSUM_CALCULATION='when_required' # FUTUREWORK: set AWS_ENDPOINT_URL instead, once nixpkgs bumps awscli to >=2.13.0. function aws { command aws --endpoint-url http://127.0.0.1:3911 $@ ; } diff --git a/script/helm/garage/Chart.yaml b/script/helm/garage/Chart.yaml index b3a7b921..7a89409e 100644 --- a/script/helm/garage/Chart.yaml +++ b/script/helm/garage/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: garage description: S3-compatible object store for small self-hosted geo-distributed deployments type: application -version: 0.7.3 -appVersion: "v1.3.1" +version: 0.7.0 +appVersion: "v1.1.0" home: https://garagehq.deuxfleurs.fr/ icon: https://garagehq.deuxfleurs.fr/images/garage-logo.svg @@ -15,4 +15,4 @@ keywords: sources: - https://git.deuxfleurs.fr/Deuxfleurs/garage.git -maintainers: [] +maintainers: [] \ No newline at end of file diff --git a/script/helm/garage/README.md b/script/helm/garage/README.md index bdf69ec4..fcf988ca 100644 --- a/script/helm/garage/README.md +++ b/script/helm/garage/README.md @@ -1,6 +1,6 @@ # garage -![Version: 0.7.3](https://img.shields.io/badge/Version-0.7.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.3.1](https://img.shields.io/badge/AppVersion-v1.3.1-informational?style=flat-square) +![Version: 0.7.0](https://img.shields.io/badge/Version-0.7.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.1.0](https://img.shields.io/badge/AppVersion-v1.1.0-informational?style=flat-square) S3-compatible object store for small self-hosted geo-distributed deployments diff --git a/script/helm/garage/templates/service.yaml b/script/helm/garage/templates/service.yaml index 887c90d0..37218872 100644 --- a/script/helm/garage/templates/service.yaml +++ b/script/helm/garage/templates/service.yaml @@ -4,10 +4,6 @@ metadata: name: {{ include "garage.fullname" . }} labels: {{- include "garage.labels" . | nindent 4 }} - {{- with .Values.service.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} spec: type: {{ .Values.service.type }} ports: @@ -41,4 +37,4 @@ spec: name: metrics selector: {{- include "garage.selectorLabels" . | nindent 4 }} -{{- end }} +{{- end }} \ No newline at end of file diff --git a/script/helm/garage/values.yaml b/script/helm/garage/values.yaml index 5e419fe2..bbb60db2 100644 --- a/script/helm/garage/values.yaml +++ b/script/helm/garage/values.yaml @@ -124,8 +124,6 @@ service: # - NodePort (+ Ingress) # - LoadBalancer type: ClusterIP - # -- Annotations to add to the service - annotations: {} s3: api: port: 3900 diff --git a/script/k8s/crd/garagenodes.deuxfleurs.fr.yaml b/script/k8s/crd/garagenodes.deuxfleurs.fr.yaml deleted file mode 100644 index cd0fb166..00000000 --- a/script/k8s/crd/garagenodes.deuxfleurs.fr.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: garagenodes.deuxfleurs.fr -spec: - conversion: - strategy: None - group: deuxfleurs.fr - names: - kind: GarageNode - listKind: GarageNodeList - plural: garagenodes - singular: garagenode - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: Auto-generated derived type for Node via `CustomResource` - properties: - spec: - properties: - address: - format: ip - type: string - hostname: - type: string - port: - format: uint16 - minimum: 0 - type: integer - required: - - address - - hostname - - port - type: object - required: - - spec - title: GarageNode - type: object - served: true - storage: true - subresources: {} \ No newline at end of file diff --git a/script/k8s/crd/kustomization.yaml b/script/k8s/crd/kustomization.yaml deleted file mode 100644 index 9f20eccf..00000000 --- a/script/k8s/crd/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: -- garagenodes.deuxfleurs.fr.yaml \ No newline at end of file diff --git a/shell.nix b/shell.nix index c3dedca8..cfccfe94 100644 --- a/shell.nix +++ b/shell.nix @@ -34,8 +34,6 @@ in jq ]; shellHook = '' - export AWS_REQUEST_CHECKSUM_CALCULATION='when_required' - function to_s3 { aws \ --endpoint-url https://garage.deuxfleurs.fr \ diff --git a/src/api/admin/Cargo.toml b/src/api/admin/Cargo.toml index 656c6825..7b1d65e1 100644 --- a/src/api/admin/Cargo.toml +++ b/src/api/admin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_api_admin" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -22,7 +22,7 @@ garage_api_common.workspace = true argon2.workspace = true async-trait.workspace = true -thiserror.workspace = true +err-derive.workspace = true hex.workspace = true tracing.workspace = true diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 207693b6..2537bfc9 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -277,7 +277,7 @@ pub async fn handle_create_bucket( let helper = garage.locked_helper().await; if let Some(ga) = &req.global_alias { - if !is_valid_bucket_name(ga, garage.config.allow_punycode) { + if !is_valid_bucket_name(ga) { return Err(Error::bad_request(format!( "{}: {}", ga, INVALID_BUCKET_NAME_MESSAGE @@ -292,7 +292,7 @@ pub async fn handle_create_bucket( } if let Some(la) = &req.local_alias { - if !is_valid_bucket_name(&la.alias, garage.config.allow_punycode) { + if !is_valid_bucket_name(&la.alias) { return Err(Error::bad_request(format!( "{}: {}", la.alias, INVALID_BUCKET_NAME_MESSAGE @@ -382,7 +382,7 @@ pub async fn handle_delete_bucket( for ((key_id, alias), _, active) in state.local_aliases.items().iter() { if *active { helper - .purge_local_bucket_alias(bucket.id, key_id, alias) + .unset_local_bucket_alias(bucket.id, key_id, alias) .await?; } } diff --git a/src/api/admin/error.rs b/src/api/admin/error.rs index 17d4c200..201f9b40 100644 --- a/src/api/admin/error.rs +++ b/src/api/admin/error.rs @@ -1,8 +1,8 @@ use std::convert::TryFrom; +use err_derive::Error; use hyper::header::HeaderValue; use hyper::{HeaderMap, StatusCode}; -use thiserror::Error; pub use garage_model::helper::error::Error as HelperError; @@ -16,17 +16,20 @@ use garage_api_common::helpers::*; /// Errors of this crate #[derive(Debug, Error)] pub enum Error { - #[error("{0}")] + #[error(display = "{}", _0)] /// Error from common error - Common(#[from] CommonError), + Common(#[error(source)] CommonError), // Category: cannot process /// The API access key does not exist - #[error("Access key not found: {0}")] + #[error(display = "Access key not found: {}", _0)] NoSuchAccessKey(String), /// In Import key, the key already exists - #[error("Key {0} already exists in data store. Even if it is deleted, we can't let you create a new key with the same ID. Sorry.")] + #[error( + display = "Key {} already exists in data store. Even if it is deleted, we can't let you create a new key with the same ID. Sorry.", + _0 + )] KeyAlreadyExists(String), } diff --git a/src/api/common/Cargo.toml b/src/api/common/Cargo.toml index df01d59a..6d906423 100644 --- a/src/api/common/Cargo.toml +++ b/src/api/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_api_common" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -24,10 +24,11 @@ chrono.workspace = true crc32fast.workspace = true crc32c.workspace = true crypto-common.workspace = true -thiserror.workspace = true +err-derive.workspace = true hex.workspace = true hmac.workspace = true md-5.workspace = true +idna.workspace = true tracing.workspace = true nom.workspace = true pin-project.workspace = true diff --git a/src/api/common/common_error.rs b/src/api/common/common_error.rs index e596a6e9..597a3511 100644 --- a/src/api/common/common_error.rs +++ b/src/api/common/common_error.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; +use err_derive::Error; use hyper::StatusCode; -use thiserror::Error; use garage_util::error::Error as GarageError; @@ -12,48 +12,48 @@ use garage_model::helper::error::Error as HelperError; pub enum CommonError { // ---- INTERNAL ERRORS ---- /// Error related to deeper parts of Garage - #[error("Internal error: {0}")] - InternalError(#[from] GarageError), + #[error(display = "Internal error: {}", _0)] + InternalError(#[error(source)] GarageError), /// Error related to Hyper - #[error("Internal error (Hyper error): {0}")] - Hyper(#[from] hyper::Error), + #[error(display = "Internal error (Hyper error): {}", _0)] + Hyper(#[error(source)] hyper::Error), /// Error related to HTTP - #[error("Internal error (HTTP error): {0}")] - Http(#[from] http::Error), + #[error(display = "Internal error (HTTP error): {}", _0)] + Http(#[error(source)] http::Error), // ---- GENERIC CLIENT ERRORS ---- /// Proper authentication was not provided - #[error("Forbidden: {0}")] + #[error(display = "Forbidden: {}", _0)] Forbidden(String), /// Generic bad request response with custom message - #[error("Bad request: {0}")] + #[error(display = "Bad request: {}", _0)] BadRequest(String), /// The client sent a header with invalid value - #[error("Invalid header value: {0}")] - InvalidHeader(#[from] hyper::header::ToStrError), + #[error(display = "Invalid header value: {}", _0)] + InvalidHeader(#[error(source)] hyper::header::ToStrError), // ---- SPECIFIC ERROR CONDITIONS ---- // These have to be error codes referenced in the S3 spec here: // https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ErrorCodeList /// The bucket requested don't exists - #[error("Bucket not found: {0}")] + #[error(display = "Bucket not found: {}", _0)] NoSuchBucket(String), /// Tried to create a bucket that already exist - #[error("Bucket already exists")] + #[error(display = "Bucket already exists")] BucketAlreadyExists, /// Tried to delete a non-empty bucket - #[error("Tried to delete a non-empty bucket")] + #[error(display = "Tried to delete a non-empty bucket")] BucketNotEmpty, // Category: bad request /// Bucket name is not valid according to AWS S3 specs - #[error("Invalid bucket name: {0}")] + #[error(display = "Invalid bucket name: {}", _0)] InvalidBucketName(String), } diff --git a/src/api/common/generic_server.rs b/src/api/common/generic_server.rs index 3f14c07d..6ddc2ff2 100644 --- a/src/api/common/generic_server.rs +++ b/src/api/common/generic_server.rs @@ -58,12 +58,6 @@ pub trait ApiHandler: Send + Sync + 'static { req: Request, endpoint: Self::Endpoint, ) -> impl Future>, Self::Error>> + Send; - - /// Returns the key id used to authenticate this request. The ID returned must be safe to - /// log. - fn key_id_from_request(&self, _req: &Request) -> Option { - None - } } pub struct ApiServer { @@ -148,20 +142,19 @@ impl ApiServer { ) -> Result>, http::Error> { let uri = req.uri().clone(); - let source = if let Ok(forwarded_for_ip_addr) = + if let Ok(forwarded_for_ip_addr) = forwarded_headers::handle_forwarded_for_headers(req.headers()) { - format!("{forwarded_for_ip_addr} (via {addr})") + info!( + "{} (via {}) {} {}", + forwarded_for_ip_addr, + addr, + req.method(), + uri + ); } else { - format!("{addr}") - }; - // we only do this to log the access key, so we can discard any error - let key = self - .api_handler - .key_id_from_request(&req) - .map(|k| format!("(key {k}) ")) - .unwrap_or_default(); - info!("{source} {key}{} {uri}", req.method()); + info!("{} {} {}", addr, req.method(), uri); + } debug!("{:?}", req); let tracer = opentelemetry::global::tracer("garage"); @@ -350,11 +343,7 @@ where while !*must_exit.borrow() { let (stream, client_addr) = tokio::select! { - acc = listener.accept() => match acc { - Ok(r) => r, - Err(e) if e.kind() == std::io::ErrorKind::ConnectionAborted => continue, - Err(e) => return Err(e.into()), - }, + acc = listener.accept() => acc?, _ = must_exit.changed() => continue, }; diff --git a/src/api/common/helpers.rs b/src/api/common/helpers.rs index 6fc4aa13..c8586de4 100644 --- a/src/api/common/helpers.rs +++ b/src/api/common/helpers.rs @@ -8,6 +8,7 @@ use hyper::{ body::{Body, Bytes}, Request, Response, }; +use idna::domain_to_unicode; use serde::{Deserialize, Serialize}; use garage_model::bucket_table::BucketParams; @@ -96,7 +97,7 @@ pub fn authority_to_host(authority: &str) -> Result { authority ))), }; - authority.map(|h| h.to_ascii_lowercase()) + authority.map(|h| domain_to_unicode(h).0) } /// Extract the bucket name and the key name from an HTTP path and possibly a bucket provided in diff --git a/src/api/common/signature/error.rs b/src/api/common/signature/error.rs index a1b353e1..b2f396b5 100644 --- a/src/api/common/signature/error.rs +++ b/src/api/common/signature/error.rs @@ -1,4 +1,4 @@ -use thiserror::Error; +use err_derive::Error; use crate::common_error::CommonError; pub use crate::common_error::{CommonErrorDerivative, OkOrBadRequest, OkOrInternalError}; @@ -6,21 +6,21 @@ pub use crate::common_error::{CommonErrorDerivative, OkOrBadRequest, OkOrInterna /// Errors of this crate #[derive(Debug, Error)] pub enum Error { - #[error("{0}")] + #[error(display = "{}", _0)] /// Error from common error Common(CommonError), /// Authorization Header Malformed - #[error("Authorization header malformed, unexpected scope: {0}")] + #[error(display = "Authorization header malformed, unexpected scope: {}", _0)] AuthorizationHeaderMalformed(String), // Category: bad request /// The request contained an invalid UTF-8 sequence in its path or in other parameters - #[error("Invalid UTF-8: {0}")] - InvalidUtf8Str(#[from] std::str::Utf8Error), + #[error(display = "Invalid UTF-8: {}", _0)] + InvalidUtf8Str(#[error(source)] std::str::Utf8Error), /// The provided digest (checksum) value was invalid - #[error("Invalid digest: {0}")] + #[error(display = "Invalid digest: {}", _0)] InvalidDigest(String), } diff --git a/src/api/common/signature/payload.rs b/src/api/common/signature/payload.rs index 3939da19..2d5f8603 100644 --- a/src/api/common/signature/payload.rs +++ b/src/api/common/signature/payload.rs @@ -104,7 +104,7 @@ async fn check_standard_signature( // Verify that all necessary request headers are included in signed_headers // The following must be included for all signatures: // - the Host header (mandatory) - // - all x-amz-* headers used in the request (except x-amz-content-sha256) + // - all x-amz-* headers used in the request // AWS also indicates that the Content-Type header should be signed if // it is used, but Minio client doesn't sign it so we don't check it for compatibility. let signed_headers = split_signed_headers(&authorization)?; @@ -151,7 +151,7 @@ async fn check_presigned_signature( // Verify that all necessary request headers are included in signed_headers // For AWSv4 pre-signed URLs, the following must be included: // - the Host header (mandatory) - // - all x-amz-* headers used in the request (except x-amz-content-sha256) + // - all x-amz-* headers used in the request let signed_headers = split_signed_headers(&authorization)?; verify_signed_headers(request.headers(), &signed_headers)?; @@ -268,9 +268,7 @@ fn verify_signed_headers(headers: &HeaderMap, signed_headers: &[HeaderName]) -> return Err(Error::bad_request("Header `Host` should be signed")); } for (name, _) in headers.iter() { - // Enforce signature of all x-amz-* headers, except x-amz-content-sh256 - // because it is included in the canonical request in all cases - if name.as_str().starts_with("x-amz-") && name != X_AMZ_CONTENT_SHA256 { + if name.as_str().starts_with("x-amz-") { if !signed_headers.contains(name) { return Err(Error::bad_request(format!( "Header `{}` should be signed", @@ -419,7 +417,7 @@ pub async fn verify_v4( // ============ Authorization header, or X-Amz-* query params ========= pub struct Authorization { - pub key_id: String, + key_id: String, scope: String, signed_headers: String, signature: String, @@ -428,7 +426,7 @@ pub struct Authorization { } impl Authorization { - pub fn parse_header(headers: &HeaderMap) -> Result { + fn parse_header(headers: &HeaderMap) -> Result { let authorization = headers .get(AUTHORIZATION) .ok_or_bad_request("Missing authorization header")? @@ -470,7 +468,8 @@ impl Authorization { let date = headers .get(X_AMZ_DATE) - .ok_or_bad_request("Missing X-Amz-Date field")? + .ok_or_bad_request("Missing X-Amz-Date field") + .map_err(Error::from)? .to_str()?; let date = parse_date(date)?; diff --git a/src/api/k2v/Cargo.toml b/src/api/k2v/Cargo.toml index 28f74ea3..385aef3b 100644 --- a/src/api/k2v/Cargo.toml +++ b/src/api/k2v/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_api_k2v" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -20,7 +20,7 @@ garage_util = { workspace = true, features = [ "k2v" ] } garage_api_common.workspace = true base64.workspace = true -thiserror.workspace = true +err-derive.workspace = true tracing.workspace = true futures.workspace = true diff --git a/src/api/k2v/api_server.rs b/src/api/k2v/api_server.rs index 8e10d9a6..de5775da 100644 --- a/src/api/k2v/api_server.rs +++ b/src/api/k2v/api_server.rs @@ -176,12 +176,6 @@ impl ApiHandler for K2VApiServer { Ok(resp_ok) } - - fn key_id_from_request(&self, req: &Request) -> Option { - garage_api_common::signature::payload::Authorization::parse_header(req.headers()) - .map(|auth| auth.key_id) - .ok() - } } impl ApiEndpoint for K2VApiEndpoint { diff --git a/src/api/k2v/error.rs b/src/api/k2v/error.rs index f1937fe5..257ff893 100644 --- a/src/api/k2v/error.rs +++ b/src/api/k2v/error.rs @@ -1,6 +1,6 @@ +use err_derive::Error; use hyper::header::HeaderValue; use hyper::{HeaderMap, StatusCode}; -use thiserror::Error; use garage_api_common::common_error::{commonErrorDerivative, CommonError}; pub(crate) use garage_api_common::common_error::{helper_error_as_internal, pass_helper_error}; @@ -14,38 +14,38 @@ use garage_api_common::signature::error::Error as SignatureError; /// Errors of this crate #[derive(Debug, Error)] pub enum Error { - #[error("{0}")] + #[error(display = "{}", _0)] /// Error from common error - Common(#[from] CommonError), + Common(#[error(source)] CommonError), // Category: cannot process /// Authorization Header Malformed - #[error("Authorization header malformed, unexpected scope: {0}")] + #[error(display = "Authorization header malformed, unexpected scope: {}", _0)] AuthorizationHeaderMalformed(String), /// The provided digest (checksum) value was invalid - #[error("Invalid digest: {0}")] + #[error(display = "Invalid digest: {}", _0)] InvalidDigest(String), /// The object requested don't exists - #[error("Key not found")] + #[error(display = "Key not found")] NoSuchKey, /// Some base64 encoded data was badly encoded - #[error("Invalid base64: {0}")] - InvalidBase64(#[from] base64::DecodeError), + #[error(display = "Invalid base64: {}", _0)] + InvalidBase64(#[error(source)] base64::DecodeError), /// Invalid causality token - #[error("Invalid causality token")] + #[error(display = "Invalid causality token")] InvalidCausalityToken, /// The client asked for an invalid return format (invalid Accept header) - #[error("Not acceptable: {0}")] + #[error(display = "Not acceptable: {}", _0)] NotAcceptable(String), /// The request contained an invalid UTF-8 sequence in its path or in other parameters - #[error("Invalid UTF-8: {0}")] - InvalidUtf8Str(#[from] std::str::Utf8Error), + #[error(display = "Invalid UTF-8: {}", _0)] + InvalidUtf8Str(#[error(source)] std::str::Utf8Error), } commonErrorDerivative!(Error); diff --git a/src/api/s3/Cargo.toml b/src/api/s3/Cargo.toml index 88630866..7b0cac94 100644 --- a/src/api/s3/Cargo.toml +++ b/src/api/s3/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_api_s3" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -29,7 +29,7 @@ bytes.workspace = true chrono.workspace = true crc32fast.workspace = true crc32c.workspace = true -thiserror.workspace = true +err-derive.workspace = true hex.workspace = true tracing.workspace = true md-5.workspace = true diff --git a/src/api/s3/api_server.rs b/src/api/s3/api_server.rs index acb0cf56..e26c2b65 100644 --- a/src/api/s3/api_server.rs +++ b/src/api/s3/api_server.rs @@ -226,7 +226,6 @@ impl ApiHandler for S3ApiServer { Endpoint::DeleteBucket {} => handle_delete_bucket(ctx).await, Endpoint::GetBucketLocation {} => handle_get_bucket_location(ctx), Endpoint::GetBucketVersioning {} => handle_get_bucket_versioning(), - Endpoint::GetBucketAcl {} => handle_get_bucket_acl(ctx), Endpoint::ListObjects { delimiter, encoding_type, @@ -343,12 +342,6 @@ impl ApiHandler for S3ApiServer { Ok(resp_ok) } - - fn key_id_from_request(&self, req: &Request) -> Option { - garage_api_common::signature::payload::Authorization::parse_header(req.headers()) - .map(|auth| auth.key_id) - .ok() - } } impl ApiEndpoint for S3ApiEndpoint { diff --git a/src/api/s3/bucket.rs b/src/api/s3/bucket.rs index 55caa6c8..3a09e769 100644 --- a/src/api/s3/bucket.rs +++ b/src/api/s3/bucket.rs @@ -5,7 +5,7 @@ use hyper::{Request, Response, StatusCode}; use garage_model::bucket_alias_table::*; use garage_model::bucket_table::Bucket; use garage_model::garage::Garage; -use garage_model::key_table::{Key, KeyParams}; +use garage_model::key_table::Key; use garage_model::permission::BucketKeyPerm; use garage_table::util::*; use garage_util::crdt::*; @@ -44,55 +44,6 @@ pub fn handle_get_bucket_versioning() -> Result, Error> { .body(string_body(xml))?) } -pub fn handle_get_bucket_acl(ctx: ReqCtx) -> Result, Error> { - let ReqCtx { - bucket_id, api_key, .. - } = ctx; - let key_p = api_key.params().ok_or_internal_error( - "Key should not be in deleted state at this point (in handle_get_bucket_acl)", - )?; - - let mut grants: Vec = vec![]; - let kp = api_key.bucket_permissions(&bucket_id); - - if kp.allow_owner { - grants.push(s3_xml::Grant { - grantee: create_grantee(&key_p, &api_key), - permission: s3_xml::Value("FULL_CONTROL".to_string()), - }); - } else { - if kp.allow_read { - grants.push(s3_xml::Grant { - grantee: create_grantee(&key_p, &api_key), - permission: s3_xml::Value("READ".to_string()), - }); - grants.push(s3_xml::Grant { - grantee: create_grantee(&key_p, &api_key), - permission: s3_xml::Value("READ_ACP".to_string()), - }); - } - if kp.allow_write { - grants.push(s3_xml::Grant { - grantee: create_grantee(&key_p, &api_key), - permission: s3_xml::Value("WRITE".to_string()), - }); - } - } - - let access_control_policy = s3_xml::AccessControlPolicy { - xmlns: (), - owner: None, - acl: s3_xml::AccessControlList { entries: grants }, - }; - - let xml = s3_xml::to_xml_with_header(&access_control_policy)?; - trace!("xml: {}", xml); - - Ok(Response::builder() - .header("Content-Type", "application/xml") - .body(string_body(xml))?) -} - pub async fn handle_list_buckets( garage: &Garage, api_key: &Key, @@ -221,7 +172,7 @@ pub async fn handle_create_bucket( } // Create the bucket! - if !is_valid_bucket_name(&bucket_name, garage.config.allow_punycode) { + if !is_valid_bucket_name(&bucket_name) { return Err(Error::bad_request(format!( "{}: {}", bucket_name, INVALID_BUCKET_NAME_MESSAGE @@ -290,11 +241,11 @@ pub async fn handle_delete_bucket(ctx: ReqCtx) -> Result, Erro // 1. delete bucket alias if is_local_alias { helper - .purge_local_bucket_alias(*bucket_id, &api_key.key_id, bucket_name) + .unset_local_bucket_alias(*bucket_id, &api_key.key_id, bucket_name) .await?; } else { helper - .purge_global_bucket_alias(*bucket_id, bucket_name) + .unset_global_bucket_alias(*bucket_id, bucket_name) .await?; } @@ -360,15 +311,6 @@ fn parse_create_bucket_xml(xml_bytes: &[u8]) -> Option> { Some(ret) } -fn create_grantee(key_params: &KeyParams, api_key: &Key) -> s3_xml::Grantee { - s3_xml::Grantee { - xmlns_xsi: (), - typ: "CanonicalUser".to_string(), - display_name: Some(s3_xml::Value(key_params.name.get().to_string())), - id: Some(s3_xml::Value(api_key.key_id.to_string())), - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/api/s3/copy.rs b/src/api/s3/copy.rs index 47a63c82..a5b2d706 100644 --- a/src/api/s3/copy.rs +++ b/src/api/s3/copy.rs @@ -26,10 +26,9 @@ use garage_api_common::signature::checksum::*; use crate::api_server::{ReqBody, ResBody}; use crate::encryption::EncryptionParams; use crate::error::*; -use crate::get::{check_version_not_deleted, full_object_byte_stream, PreconditionHeaders}; +use crate::get::{full_object_byte_stream, PreconditionHeaders}; use crate::multipart; use crate::put::{extract_metadata_headers, save_stream, ChecksumMode, SaveStreamResult}; -use crate::website::X_AMZ_WEBSITE_REDIRECT_LOCATION; use crate::xml::{self as s3_xml, xmlns_tag}; pub const X_AMZ_COPY_SOURCE_IF_MATCH: HeaderName = @@ -85,18 +84,7 @@ pub async fn handle_copy( Some(v) if v == hyper::header::HeaderValue::from_static("REPLACE") => { extract_metadata_headers(req.headers())? } - _ => { - // The x-amz-website-redirect-location header is not copied, instead - // it is replaced by the value from the request (or removed if no - // value was specified) - let is_redirect = - |(key, _): &(String, String)| key == X_AMZ_WEBSITE_REDIRECT_LOCATION.as_str(); - let mut headers: Vec<_> = source_object_meta_inner.headers.clone(); - headers.retain(|h| !is_redirect(h)); - let new_headers = extract_metadata_headers(req.headers())?; - headers.extend(new_headers.into_iter().filter(is_redirect)); - headers - } + _ => source_object_meta_inner.into_owned().headers, }, checksum: source_checksum, }; @@ -237,7 +225,6 @@ async fn handle_copy_metaonly( .get(&source_version.uuid, &EmptyKey) .await?; let source_version = source_version.ok_or(Error::NoSuchKey)?; - check_version_not_deleted(&source_version)?; // Write an "uploading" marker in Object table // This holds a reference to the object in the Version table @@ -429,7 +416,6 @@ pub async fn handle_upload_part_copy( .get(&source_object_version.uuid, &EmptyKey) .await? .ok_or(Error::NoSuchKey)?; - check_version_not_deleted(&source_version)?; // We want to reuse blocks from the source version as much as possible. // However, we still need to get the data from these blocks @@ -561,7 +547,6 @@ pub async fn handle_upload_part_copy( let mut current_offset = 0; let mut next_block = defragmenter.next().await?; - let mut blocks_to_dup = dest_version.clone(); // TODO this could be optimized similarly to read_and_put_blocks // low priority because uploadpartcopy is rarely used @@ -591,7 +576,8 @@ pub async fn handle_upload_part_copy( .unwrap()?; checksummer = checksummer_updated; - let (version_block_key, version_block) = ( + dest_version.blocks.clear(); + dest_version.blocks.put( VersionBlockKey { part_number, offset: current_offset, @@ -603,56 +589,37 @@ pub async fn handle_upload_part_copy( ); current_offset += data_len; - let next = if let Some(final_data) = data_to_upload { - dest_version.blocks.clear(); - dest_version.blocks.put(version_block_key, version_block); - let block_ref = BlockRef { - block: final_hash, - version: dest_version_id, - deleted: false.into(), - }; - let (_, _, _, next) = futures::try_join!( - // Thing 1: if the block is not exactly a block that existed before, - // we need to insert that data as a new block. - garage.block_manager.rpc_put_block( - final_hash, - final_data, - dest_encryption.is_encrypted(), - None - ), - // Thing 2: we need to insert the block in the version - garage.version_table.insert(&dest_version), - // Thing 3: we need to add a block reference - garage.block_ref_table.insert(&block_ref), - // Thing 4: we need to read the next block - defragmenter.next(), - )?; - next - } else { - blocks_to_dup.blocks.put(version_block_key, version_block); - defragmenter.next().await? + let block_ref = BlockRef { + block: final_hash, + version: dest_version_id, + deleted: false.into(), }; + + let (_, _, _, next) = futures::try_join!( + // Thing 1: if the block is not exactly a block that existed before, + // we need to insert that data as a new block. + async { + if let Some(final_data) = data_to_upload { + garage + .block_manager + .rpc_put_block(final_hash, final_data, dest_encryption.is_encrypted(), None) + .await + } else { + Ok(()) + } + }, + // Thing 2: we need to insert the block in the version + garage.version_table.insert(&dest_version), + // Thing 3: we need to add a block reference + garage.block_ref_table.insert(&block_ref), + // Thing 4: we need to read the next block + defragmenter.next(), + )?; next_block = next; } assert_eq!(current_offset, source_range.length); - // Put the duplicated blocks into the version & block_refs tables - let block_refs_to_put = blocks_to_dup - .blocks - .items() - .iter() - .map(|b| BlockRef { - block: b.1.hash, - version: dest_version_id, - deleted: false.into(), - }) - .collect::>(); - futures::try_join!( - garage.version_table.insert(&blocks_to_dup), - garage.block_ref_table.insert_many(&block_refs_to_put[..]), - )?; - let checksums = checksummer.finalize(); let etag = dest_encryption.etag_from_md5(&checksums.md5); let checksum = checksums.extract(dest_object_checksum_algorithm); diff --git a/src/api/s3/cors.rs b/src/api/s3/cors.rs index 1f365beb..fcfdb934 100644 --- a/src/api/s3/cors.rs +++ b/src/api/s3/cors.rs @@ -88,9 +88,7 @@ pub async fn handle_put_cors( pub struct CorsConfiguration { #[serde(serialize_with = "xmlns_tag", skip_deserializing)] pub xmlns: (), - // "default" is required to be able to parse an empty list of rules, - // cf https://docs.rs/quick-xml/latest/quick_xml/de/#sequences-xsall-and-xssequence-xml-schema-types - #[serde(rename = "CORSRule", default)] + #[serde(rename = "CORSRule")] pub cors_rules: Vec, } @@ -272,26 +270,4 @@ mod tests { Ok(()) } - - #[test] - fn test_deserialize_norules() -> Result<(), Error> { - let message = r#" -"#; - let conf: CorsConfiguration = from_str(message).unwrap(); - let ref_value = CorsConfiguration { - xmlns: (), - cors_rules: vec![], - }; - assert_eq! { - ref_value, - conf - }; - - let message2 = to_xml_with_header(&ref_value)?; - - let cleanup = |c: &str| c.replace(char::is_whitespace, ""); - assert_eq!(cleanup(message), cleanup(&message2)); - - Ok(()) - } } diff --git a/src/api/s3/error.rs b/src/api/s3/error.rs index 64112084..6d4b7a11 100644 --- a/src/api/s3/error.rs +++ b/src/api/s3/error.rs @@ -1,8 +1,8 @@ use std::convert::TryInto; +use err_derive::Error; use hyper::header::HeaderValue; use hyper::{HeaderMap, StatusCode}; -use thiserror::Error; use garage_model::helper::error::Error as HelperError; @@ -25,67 +25,67 @@ use crate::xml as s3_xml; /// Errors of this crate #[derive(Debug, Error)] pub enum Error { - #[error("{0}")] + #[error(display = "{}", _0)] /// Error from common error - Common(#[from] CommonError), + Common(#[error(source)] CommonError), // Category: cannot process /// Authorization Header Malformed - #[error("Authorization header malformed, unexpected scope: {0}")] + #[error(display = "Authorization header malformed, unexpected scope: {}", _0)] AuthorizationHeaderMalformed(String), /// The object requested don't exists - #[error("Key not found")] + #[error(display = "Key not found")] NoSuchKey, /// The multipart upload requested don't exists - #[error("Upload not found")] + #[error(display = "Upload not found")] NoSuchUpload, /// Precondition failed (e.g. x-amz-copy-source-if-match) - #[error("At least one of the preconditions you specified did not hold")] + #[error(display = "At least one of the preconditions you specified did not hold")] PreconditionFailed, /// Parts specified in CMU request do not match parts actually uploaded - #[error("Parts given to CompleteMultipartUpload do not match uploaded parts")] + #[error(display = "Parts given to CompleteMultipartUpload do not match uploaded parts")] InvalidPart, /// Parts given to CompleteMultipartUpload were not in ascending order - #[error("Parts given to CompleteMultipartUpload were not in ascending order")] + #[error(display = "Parts given to CompleteMultipartUpload were not in ascending order")] InvalidPartOrder, /// In CompleteMultipartUpload: not enough data /// (here we are more lenient than AWS S3) - #[error("Proposed upload is smaller than the minimum allowed object size")] + #[error(display = "Proposed upload is smaller than the minimum allowed object size")] EntityTooSmall, // Category: bad request /// The request contained an invalid UTF-8 sequence in its path or in other parameters - #[error("Invalid UTF-8: {0}")] - InvalidUtf8Str(#[from] std::str::Utf8Error), + #[error(display = "Invalid UTF-8: {}", _0)] + InvalidUtf8Str(#[error(source)] std::str::Utf8Error), /// The request used an invalid path - #[error("Invalid UTF-8: {0}")] - InvalidUtf8String(#[from] std::string::FromUtf8Error), + #[error(display = "Invalid UTF-8: {}", _0)] + InvalidUtf8String(#[error(source)] std::string::FromUtf8Error), /// The client sent invalid XML data - #[error("Invalid XML: {0}")] + #[error(display = "Invalid XML: {}", _0)] InvalidXml(String), /// The client sent a range header with invalid value - #[error("Invalid HTTP range: {0:?}")] - InvalidRange((http_range::HttpRangeParseError, u64)), + #[error(display = "Invalid HTTP range: {:?}", _0)] + InvalidRange(#[error(from)] (http_range::HttpRangeParseError, u64)), /// The client sent a range header with invalid value - #[error("Invalid encryption algorithm: {0:?}, should be AES256")] + #[error(display = "Invalid encryption algorithm: {:?}, should be AES256", _0)] InvalidEncryptionAlgorithm(String), /// The provided digest (checksum) value was invalid - #[error("Invalid digest: {0}")] + #[error(display = "Invalid digest: {}", _0)] InvalidDigest(String), /// The client sent a request for an action not supported by garage - #[error("Unimplemented action: {0}")] + #[error(display = "Unimplemented action: {}", _0)] NotImplemented(String), } @@ -99,12 +99,6 @@ impl From for Error { } } -impl From<(http_range::HttpRangeParseError, u64)> for Error { - fn from(err: (http_range::HttpRangeParseError, u64)) -> Error { - Error::InvalidRange(err) - } -} - impl From for Error { fn from(err: roxmltree::Error) -> Self { Self::InvalidXml(format!("{}", err)) diff --git a/src/api/s3/get.rs b/src/api/s3/get.rs index a1e4ce10..22076603 100644 --- a/src/api/s3/get.rs +++ b/src/api/s3/get.rs @@ -19,13 +19,12 @@ use garage_net::stream::ByteStream; use garage_rpc::rpc_helper::OrderTag; use garage_table::EmptyKey; use garage_util::data::*; -use garage_util::error::{Error as UtilError, OkOrMessage}; +use garage_util::error::OkOrMessage; use garage_model::garage::Garage; use garage_model::s3::object_table::*; use garage_model::s3::version_table::*; -use garage_api_common::common_error::CommonError; use garage_api_common::helpers::*; use garage_api_common::signature::checksum::{add_checksum_response_headers, X_AMZ_CHECKSUM_MODE}; @@ -216,7 +215,6 @@ pub async fn handle_head_without_ctx( .get(&object_version.uuid, &EmptyKey) .await? .ok_or(Error::NoSuchKey)?; - check_version_not_deleted(&version)?; let (part_offset, part_end) = calculate_part_bounds(&version, pn).ok_or(Error::InvalidPart)?; @@ -367,21 +365,6 @@ pub async fn handle_get_without_ctx( } } -pub(crate) fn check_version_not_deleted(version: &Version) -> Result<(), Error> { - if version.deleted.get() { - // the version was deleted between when the object_table was consulted - // and now, this could mean the object was deleted, or overriden. - // Rather than say the key doesn't exist, return a transient error - // to signal the client to try again. - return Err(CommonError::InternalError(UtilError::Message( - "conflict/inconsistency between object and version state, version is deleted" - .to_string(), - )) - .into()); - } - Ok(()) -} - async fn handle_get_full( garage: Arc, version: &ObjectVersion, @@ -448,7 +431,6 @@ pub fn full_object_byte_stream( .ok_or_message("channel closed")?; let version = version_fut.await.unwrap()?.ok_or(Error::NoSuchKey)?; - check_version_not_deleted(&version)?; for (i, (_, vb)) in version.blocks.items().iter().enumerate().skip(1) { let stream_block_i = encryption .get_block(&garage, &vb.hash, Some(order_stream.order(i as u64))) @@ -464,14 +446,6 @@ pub fn full_object_byte_stream( { Ok(()) => (), Err(e) => { - // TODO i think this is a bad idea, we should log - // an error and stop there. If the error happens to - // be exactly the size of what hasn't been streamed - // yet, the client will see the request as a - // success - // instead truncating the output notify the client - // something happened with their download, so that - // they can retry it let _ = tx.send(error_stream_item(e)).await; } } @@ -523,7 +497,7 @@ async fn handle_get_range( .get(&version.uuid, &EmptyKey) .await? .ok_or(Error::NoSuchKey)?; - check_version_not_deleted(&version)?; + let body = body_from_blocks_range(garage, encryption, version.blocks.items(), begin, end); Ok(resp_builder.body(body)?) @@ -574,8 +548,6 @@ async fn handle_get_part( .await? .ok_or(Error::NoSuchKey)?; - check_version_not_deleted(&version)?; - let (begin, end) = calculate_part_bounds(&version, part_number).ok_or(Error::InvalidPart)?; @@ -845,9 +817,7 @@ impl PreconditionHeaders { } fn check(&self, v: &ObjectVersion, etag: &str) -> Result, Error> { - // we store date with ms precision, but headers are precise to the second: truncate - // the timestamp to handle the same-second edge case - let v_date = UNIX_EPOCH + Duration::from_secs(v.timestamp / 1000); + let v_date = UNIX_EPOCH + Duration::from_millis(v.timestamp); // Implemented from https://datatracker.ietf.org/doc/html/rfc7232#section-6 diff --git a/src/api/s3/lifecycle.rs b/src/api/s3/lifecycle.rs index ccda6cfd..c140494e 100644 --- a/src/api/s3/lifecycle.rs +++ b/src/api/s3/lifecycle.rs @@ -27,7 +27,7 @@ pub async fn handle_get_lifecycle(ctx: ReqCtx) -> Result, Erro .body(string_body(xml))?) } else { Ok(Response::builder() - .status(StatusCode::NOT_FOUND) + .status(StatusCode::NO_CONTENT) .body(empty_body())?) } } diff --git a/src/api/s3/post_object.rs b/src/api/s3/post_object.rs index 09be7e7c..b9bccae6 100644 --- a/src/api/s3/post_object.rs +++ b/src/api/s3/post_object.rs @@ -141,26 +141,10 @@ pub async fn handle_post_object( let mut conditions = decoded_policy.into_conditions()?; - // If there are conditions on the bucket name, check these against the actual bucket_name rather - // than the one in params, which is allowed to be absent. - if let Some(conds) = conditions.params.remove("bucket") { - for cond in conds { - let ok = match cond { - Operation::Equal(s) => s.as_str() == bucket_name, - Operation::StartsWith(s) => bucket_name.starts_with(&s), - }; - if !ok { - return Err(Error::bad_request( - "Key 'bucket' has value not allowed in policy", - )); - } - } - } - for (param_key, value) in params.iter() { let param_key = param_key.as_str(); match param_key { - "policy" | "x-amz-signature" | "bucket" => (), // this is always accepted, as it's required to validate other fields + "policy" | "x-amz-signature" => (), // this is always accepted, as it's required to validate other fields "content-type" => { let conds = conditions.params.remove("content-type").ok_or_else(|| { Error::bad_request(format!("Key '{}' is not allowed in policy", param_key)) diff --git a/src/api/s3/put.rs b/src/api/s3/put.rs index b915f2ec..830a7998 100644 --- a/src/api/s3/put.rs +++ b/src/api/s3/put.rs @@ -39,6 +39,8 @@ use crate::encryption::EncryptionParams; use crate::error::*; use crate::website::X_AMZ_WEBSITE_REDIRECT_LOCATION; +const PUT_BLOCKS_MAX_PARALLEL: usize = 3; + pub(crate) struct SaveStreamResult { pub(crate) version_uuid: Uuid, pub(crate) version_timestamp: u64, @@ -491,7 +493,7 @@ pub(crate) async fn read_and_put_blocks> + }; let recv_next = async { // If more than a maximum number of writes are in progress, don't add more for now - if currently_running >= ctx.garage.config.block_max_concurrent_writes_per_request { + if currently_running >= PUT_BLOCKS_MAX_PARALLEL { futures::future::pending().await } else { block_rx3.recv().await diff --git a/src/api/s3/xml.rs b/src/api/s3/xml.rs index fdb36318..e8af3ec0 100644 --- a/src/api/s3/xml.rs +++ b/src/api/s3/xml.rs @@ -13,10 +13,6 @@ pub fn xmlns_tag(_v: &(), s: S) -> Result { s.serialize_str("http://s3.amazonaws.com/doc/2006-03-01/") } -pub fn xmlns_xsi_tag(_v: &(), s: S) -> Result { - s.serialize_str("http://www.w3.org/2001/XMLSchema-instance") -} - #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct Value(#[serde(rename = "$value")] pub String); @@ -323,42 +319,6 @@ pub struct PostObject { pub etag: Value, } -#[derive(Debug, Serialize, PartialEq, Eq)] -pub struct Grantee { - #[serde(rename = "xmlns:xsi", serialize_with = "xmlns_xsi_tag")] - pub xmlns_xsi: (), - #[serde(rename = "xsi:type")] - pub typ: String, - #[serde(rename = "DisplayName")] - pub display_name: Option, - #[serde(rename = "ID")] - pub id: Option, -} - -#[derive(Debug, Serialize, PartialEq, Eq)] -pub struct Grant { - #[serde(rename = "Grantee")] - pub grantee: Grantee, - #[serde(rename = "Permission")] - pub permission: Value, -} - -#[derive(Debug, Serialize, PartialEq, Eq)] -pub struct AccessControlList { - #[serde(rename = "Grant")] - pub entries: Vec, -} - -#[derive(Debug, Serialize, PartialEq, Eq)] -pub struct AccessControlPolicy { - #[serde(serialize_with = "xmlns_tag")] - pub xmlns: (), - #[serde(rename = "Owner")] - pub owner: Option, - #[serde(rename = "AccessControlList")] - pub acl: AccessControlList, -} - #[cfg(test)] mod tests { use super::*; @@ -467,43 +427,6 @@ mod tests { Ok(()) } - #[test] - fn get_bucket_acl_result() -> Result<(), ApiError> { - let grant = Grant { - grantee: Grantee { - xmlns_xsi: (), - typ: "CanonicalUser".to_string(), - display_name: Some(Value("owner_name".to_string())), - id: Some(Value("qsdfjklm".to_string())), - }, - permission: Value("FULL_CONTROL".to_string()), - }; - - let get_bucket_acl = AccessControlPolicy { - xmlns: (), - owner: None, - acl: AccessControlList { - entries: vec![grant], - }, - }; - assert_eq!( - to_xml_with_header(&get_bucket_acl)?, - "\ -\ - \ - \ - \ - owner_name\ - qsdfjklm\ - \ - FULL_CONTROL\ - \ - \ -" - ); - Ok(()) - } - #[test] fn delete_result() -> Result<(), ApiError> { let delete_result = DeleteResult { diff --git a/src/block/Cargo.toml b/src/block/Cargo.toml index c4dbba44..1f5558c5 100644 --- a/src/block/Cargo.toml +++ b/src/block/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_block" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" diff --git a/src/block/manager.rs b/src/block/manager.rs index 96ca9c90..18f9e198 100644 --- a/src/block/manager.rs +++ b/src/block/manager.rs @@ -17,6 +17,7 @@ use opentelemetry::{ Context, }; +use garage_net::endpoint::RpcInFlightLimiter; use garage_net::stream::{read_stream_to_end, stream_asyncread, ByteStream}; use garage_db as db; @@ -50,8 +51,6 @@ pub const INLINE_THRESHOLD: usize = 3072; // to delete the block locally. pub(crate) const BLOCK_GC_DELAY: Duration = Duration::from_secs(600); -const BLOCK_READ_SEMAPHORE_TIMEOUT: Duration = Duration::from_secs(15); - /// RPC messages used to share blocks of data between nodes #[derive(Debug, Serialize, Deserialize)] pub enum BlockRpc { @@ -89,7 +88,6 @@ pub struct BlockManager { disable_scrub: bool, mutation_lock: Vec>, - read_semaphore: Semaphore, pub rc: BlockRc, pub resync: BlockResyncManager, @@ -179,8 +177,6 @@ impl BlockManager { .iter() .map(|_| Mutex::new(BlockManagerLocked())) .collect::>(), - - read_semaphore: Semaphore::new(config.block_max_concurrent_reads), rc, resync, system, @@ -300,6 +296,7 @@ impl BlockManager { &node_id, BlockRpc::GetBlock(*hash, order_tag), priority, + RpcInFlightLimiter::TableWrite, ); tokio::select! { res = rpc => { @@ -413,8 +410,8 @@ impl BlockManager { } /// Get number of items in the refcount table - pub fn rc_approximate_len(&self) -> Result { - Ok(self.rc.rc_table.approximate_len()?) + pub fn rc_len(&self) -> Result { + Ok(self.rc.rc_table.len()?) } /// Send command to start/stop/manager scrub worker @@ -432,7 +429,7 @@ impl BlockManager { /// List all resync errors pub fn list_resync_errors(&self) -> Result, Error> { - let mut blocks = Vec::with_capacity(self.resync.errors.approximate_len()?); + let mut blocks = Vec::with_capacity(self.resync.errors.len()?); for ent in self.resync.errors.iter()? { let (hash, cnt) = ent?; let cnt = ErrorCounter::decode(&cnt); @@ -562,6 +559,9 @@ impl BlockManager { match self.find_block(hash).await { Some(p) => self.read_block_from(hash, &p).await, None => { + // Not found but maybe we should have had it ?? + self.resync + .put_to_resync(hash, 2 * self.system.rpc_helper().rpc_timeout())?; return Err(Error::Message(format!( "block {:?} not found on node", hash @@ -583,15 +583,6 @@ impl BlockManager { ) -> Result { let (header, path) = block_path.as_parts_ref(); - let permit = tokio::select! { - sem = self.read_semaphore.acquire() => sem.ok_or_message("acquire read semaphore")?, - _ = tokio::time::sleep(BLOCK_READ_SEMAPHORE_TIMEOUT) => { - self.metrics.block_read_semaphore_timeouts.add(1); - debug!("read block {:?}: read_semaphore acquire timeout", hash); - return Err(Error::Message("read block: read_semaphore acquire timeout".into())); - } - }; - let mut f = fs::File::open(&path).await?; let mut data = vec![]; f.read_to_end(&mut data).await?; @@ -616,8 +607,6 @@ impl BlockManager { return Err(Error::CorruptData(*hash)); } - drop(permit); - Ok(data) } @@ -783,7 +772,6 @@ impl BlockManagerLocked { let mut f = fs::File::create(&path_tmp).await?; f.write_all(data).await?; - f.flush().await?; mgr.metrics.bytes_written.add(data.len() as u64); if mgr.data_fsync { diff --git a/src/block/metrics.rs b/src/block/metrics.rs index 81021fe1..2d41e365 100644 --- a/src/block/metrics.rs +++ b/src/block/metrics.rs @@ -22,7 +22,6 @@ pub struct BlockManagerMetrics { pub(crate) bytes_read: BoundCounter, pub(crate) block_read_duration: BoundValueRecorder, - pub(crate) block_read_semaphore_timeouts: BoundCounter, pub(crate) bytes_written: BoundCounter, pub(crate) block_write_duration: BoundValueRecorder, pub(crate) delete_counter: BoundCounter, @@ -51,7 +50,7 @@ impl BlockManagerMetrics { .init(), _rc_size: meter .u64_value_observer("block.rc_size", move |observer| { - if let Ok(value) = rc_tree.approximate_len() { + if let Ok(value) = rc_tree.len() { observer.observe(value as u64, &[]) } }) @@ -59,7 +58,7 @@ impl BlockManagerMetrics { .init(), _resync_queue_len: meter .u64_value_observer("block.resync_queue_length", move |observer| { - if let Ok(value) = resync_queue.approximate_len() { + if let Ok(value) = resync_queue.len() { observer.observe(value as u64, &[]); } }) @@ -69,7 +68,7 @@ impl BlockManagerMetrics { .init(), _resync_errored_blocks: meter .u64_value_observer("block.resync_errored_blocks", move |observer| { - if let Ok(value) = resync_errors.approximate_len() { + if let Ok(value) = resync_errors.len() { observer.observe(value as u64, &[]); } }) @@ -120,11 +119,6 @@ impl BlockManagerMetrics { .with_description("Duration of block read operations") .init() .bind(&[]), - block_read_semaphore_timeouts: meter - .u64_counter("block.read_semaphore_timeouts") - .with_description("Number of block reads that failed due to semaphore acquire timeout") - .init() - .bind(&[]), bytes_written: meter .u64_counter("block.bytes_written") .with_description("Number of bytes written to disk") diff --git a/src/block/resync.rs b/src/block/resync.rs index 7056a828..b476a0b8 100644 --- a/src/block/resync.rs +++ b/src/block/resync.rs @@ -106,13 +106,13 @@ impl BlockResyncManager { } /// Get length of resync queue - pub fn queue_approximate_len(&self) -> Result { - Ok(self.queue.approximate_len()?) + pub fn queue_len(&self) -> Result { + Ok(self.queue.len()?) } /// Get number of blocks that have an error - pub fn errors_approximate_len(&self) -> Result { - Ok(self.errors.approximate_len()?) + pub fn errors_len(&self) -> Result { + Ok(self.errors.len()?) } /// Clear the error counter for a block and put it in queue immediately @@ -133,14 +133,6 @@ impl BlockResyncManager { ))) } - /// Clear the entire resync queue and list of errored blocks - /// Corresponds to `garage repair clear-resync-queue` - pub fn clear_resync_queue(&self) -> Result<(), Error> { - self.queue.clear()?; - self.errors.clear()?; - Ok(()) - } - pub fn register_bg_vars(&self, vars: &mut vars::BgVars) { let notify = self.notify.clone(); vars.register_rw( @@ -556,11 +548,9 @@ impl Worker for ResyncWorker { } WorkerStatus { - queue_length: Some(self.manager.resync.queue_approximate_len().unwrap_or(0) as u64), + queue_length: Some(self.manager.resync.queue_len().unwrap_or(0) as u64), tranquility: Some(tranquility), - persistent_errors: Some( - self.manager.resync.errors_approximate_len().unwrap_or(0) as u64 - ), + persistent_errors: Some(self.manager.resync.errors_len().unwrap_or(0) as u64), ..Default::default() } } diff --git a/src/db/Cargo.toml b/src/db/Cargo.toml index 9e860e7d..bfc9029c 100644 --- a/src/db/Cargo.toml +++ b/src/db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_db" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -12,18 +12,14 @@ readme = "../../README.md" path = "lib.rs" [dependencies] -thiserror.workspace = true +err-derive.workspace = true tracing.workspace = true heed = { workspace = true, optional = true } - rusqlite = { workspace = true, optional = true, features = ["backup"] } r2d2 = { workspace = true, optional = true } r2d2_sqlite = { workspace = true, optional = true } -fjall = { workspace = true, optional = true } -parking_lot = { workspace = true, optional = true } - [dev-dependencies] mktemp.workspace = true @@ -31,5 +27,4 @@ mktemp.workspace = true default = [ "lmdb", "sqlite" ] bundled-libs = [ "rusqlite?/bundled" ] lmdb = [ "heed" ] -fjall = [ "dep:fjall", "dep:parking_lot" ] sqlite = [ "rusqlite", "r2d2", "r2d2_sqlite" ] diff --git a/src/db/fjall_adapter.rs b/src/db/fjall_adapter.rs deleted file mode 100644 index 25913a1f..00000000 --- a/src/db/fjall_adapter.rs +++ /dev/null @@ -1,453 +0,0 @@ -use core::ops::Bound; - -use std::path::PathBuf; -use std::sync::Arc; - -use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; - -use fjall::{ - PartitionCreateOptions, PersistMode, TransactionalKeyspace, TransactionalPartitionHandle, - WriteTransaction, -}; - -use crate::{ - open::{Engine, OpenOpt}, - Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult, - TxResult, TxValueIter, Value, ValueIter, -}; - -pub use fjall; - -// -- - -pub(crate) fn open_db(path: &PathBuf, opt: &OpenOpt) -> Result { - info!("Opening Fjall database at: {}", path.display()); - if opt.fsync { - return Err(Error( - "metadata_fsync is not supported with the Fjall database engine".into(), - )); - } - let mut config = fjall::Config::new(path); - if let Some(block_cache_size) = opt.fjall_block_cache_size { - config = config.cache_size(block_cache_size as u64); - } - let keyspace = config.open_transactional()?; - Ok(FjallDb::init(keyspace)) -} - -// -- err - -impl From for Error { - fn from(e: fjall::Error) -> Error { - Error(format!("fjall: {}", e).into()) - } -} - -impl From for Error { - fn from(e: fjall::LsmError) -> Error { - Error(format!("fjall lsm_tree: {}", e).into()) - } -} - -impl From for TxOpError { - fn from(e: fjall::Error) -> TxOpError { - TxOpError(e.into()) - } -} - -// -- db - -pub struct FjallDb { - keyspace: TransactionalKeyspace, - trees: RwLock>, -} - -type ByteRefRangeBound<'r> = (Bound<&'r [u8]>, Bound<&'r [u8]>); - -impl FjallDb { - pub fn init(keyspace: TransactionalKeyspace) -> Db { - let s = Self { - keyspace, - trees: RwLock::new(Vec::new()), - }; - Db(Arc::new(s)) - } - - fn get_tree( - &self, - i: usize, - ) -> Result> { - RwLockReadGuard::try_map(self.trees.read(), |trees: &Vec<_>| { - trees.get(i).map(|tup| &tup.1) - }) - .map_err(|_| Error("invalid tree id".into())) - } -} - -impl IDb for FjallDb { - fn engine(&self) -> String { - "Fjall (EXPERIMENTAL!)".into() - } - - fn open_tree(&self, name: &str) -> Result { - let mut trees = self.trees.write(); - let safe_name = encode_name(name)?; - if let Some(i) = trees.iter().position(|(name, _)| *name == safe_name) { - Ok(i) - } else { - let tree = self - .keyspace - .open_partition(&safe_name, PartitionCreateOptions::default())?; - let i = trees.len(); - trees.push((safe_name, tree)); - Ok(i) - } - } - - fn list_trees(&self) -> Result> { - Ok(self - .keyspace - .list_partitions() - .iter() - .map(|n| decode_name(&n)) - .collect::>>()?) - } - - fn snapshot(&self, base_path: &PathBuf) -> Result<()> { - std::fs::create_dir_all(base_path)?; - let path = Engine::Fjall.db_path(base_path); - - let source_state = self.keyspace.read_tx(); - let copy_keyspace = fjall::Config::new(path).open()?; - - for partition_name in self.keyspace.list_partitions() { - let source_partition = self - .keyspace - .open_partition(&partition_name, PartitionCreateOptions::default())?; - let copy_partition = - copy_keyspace.open_partition(&partition_name, PartitionCreateOptions::default())?; - - for entry in source_state.iter(&source_partition) { - let (key, value) = entry?; - copy_partition.insert(key, value)?; - } - } - - copy_keyspace.persist(PersistMode::SyncAll)?; - Ok(()) - } - - // ---- - - fn get(&self, tree_idx: usize, key: &[u8]) -> Result> { - let tree = self.get_tree(tree_idx)?; - let tx = self.keyspace.read_tx(); - let val = tx.get(&tree, key)?; - match val { - None => Ok(None), - Some(v) => Ok(Some(v.to_vec())), - } - } - - fn approximate_len(&self, tree_idx: usize) -> Result { - let tree = self.get_tree(tree_idx)?; - Ok(tree.approximate_len()) - } - fn is_empty(&self, tree_idx: usize) -> Result { - let tree = self.get_tree(tree_idx)?; - let tx = self.keyspace.read_tx(); - Ok(tx.is_empty(&tree)?) - } - - fn insert(&self, tree_idx: usize, key: &[u8], value: &[u8]) -> Result<()> { - let tree = self.get_tree(tree_idx)?; - let mut tx = self.keyspace.write_tx(); - tx.insert(&tree, key, value); - tx.commit()?; - Ok(()) - } - - fn remove(&self, tree_idx: usize, key: &[u8]) -> Result<()> { - let tree = self.get_tree(tree_idx)?; - let mut tx = self.keyspace.write_tx(); - tx.remove(&tree, key); - tx.commit()?; - Ok(()) - } - - fn clear(&self, tree_idx: usize) -> Result<()> { - let mut trees = self.trees.write(); - - if tree_idx >= trees.len() { - return Err(Error("invalid tree id".into())); - } - let (name, tree) = trees.remove(tree_idx); - - self.keyspace.delete_partition(tree)?; - let tree = self - .keyspace - .open_partition(&name, PartitionCreateOptions::default())?; - trees.insert(tree_idx, (name, tree)); - - Ok(()) - } - - fn iter(&self, tree_idx: usize) -> Result> { - let tree = self.get_tree(tree_idx)?; - let tx = self.keyspace.read_tx(); - Ok(Box::new(tx.iter(&tree).map(iterator_remap))) - } - - fn iter_rev(&self, tree_idx: usize) -> Result> { - let tree = self.get_tree(tree_idx)?; - let tx = self.keyspace.read_tx(); - Ok(Box::new(tx.iter(&tree).rev().map(iterator_remap))) - } - - fn range<'r>( - &self, - tree_idx: usize, - low: Bound<&'r [u8]>, - high: Bound<&'r [u8]>, - ) -> Result> { - let tree = self.get_tree(tree_idx)?; - let tx = self.keyspace.read_tx(); - Ok(Box::new( - tx.range::<&'r [u8], ByteRefRangeBound>(&tree, (low, high)) - .map(iterator_remap), - )) - } - fn range_rev<'r>( - &self, - tree_idx: usize, - low: Bound<&'r [u8]>, - high: Bound<&'r [u8]>, - ) -> Result> { - let tree = self.get_tree(tree_idx)?; - let tx = self.keyspace.read_tx(); - Ok(Box::new( - tx.range::<&'r [u8], ByteRefRangeBound>(&tree, (low, high)) - .rev() - .map(iterator_remap), - )) - } - - // ---- - - fn transaction(&self, f: &dyn ITxFn) -> TxResult { - let trees = self.trees.read(); - let mut tx = FjallTx { - trees: &trees[..], - tx: self.keyspace.write_tx(), - }; - - let res = f.try_on(&mut tx); - match res { - TxFnResult::Ok(on_commit) => { - tx.tx.commit().map_err(Error::from).map_err(TxError::Db)?; - Ok(on_commit) - } - TxFnResult::Abort => { - tx.tx.rollback(); - Err(TxError::Abort(())) - } - TxFnResult::DbErr => { - tx.tx.rollback(); - Err(TxError::Db(Error( - "(this message will be discarded)".into(), - ))) - } - } - } -} - -// ---- - -struct FjallTx<'a> { - trees: &'a [(String, TransactionalPartitionHandle)], - tx: WriteTransaction<'a>, -} - -impl<'a> FjallTx<'a> { - fn get_tree(&self, i: usize) -> TxOpResult<&TransactionalPartitionHandle> { - self.trees.get(i).map(|tup| &tup.1).ok_or_else(|| { - TxOpError(Error( - "invalid tree id (it might have been openned after the transaction started)".into(), - )) - }) - } -} - -impl<'a> ITx for FjallTx<'a> { - fn get(&self, tree_idx: usize, key: &[u8]) -> TxOpResult> { - let tree = self.get_tree(tree_idx)?; - match self.tx.get(tree, key)? { - Some(v) => Ok(Some(v.to_vec())), - None => Ok(None), - } - } - fn len(&self, tree_idx: usize) -> TxOpResult { - let tree = self.get_tree(tree_idx)?; - Ok(self.tx.len(tree)? as usize) - } - - fn insert(&mut self, tree_idx: usize, key: &[u8], value: &[u8]) -> TxOpResult<()> { - let tree = self.get_tree(tree_idx)?.clone(); - self.tx.insert(&tree, key, value); - Ok(()) - } - fn remove(&mut self, tree_idx: usize, key: &[u8]) -> TxOpResult<()> { - let tree = self.get_tree(tree_idx)?.clone(); - self.tx.remove(&tree, key); - Ok(()) - } - fn clear(&mut self, _tree_idx: usize) -> TxOpResult<()> { - unimplemented!("LSM tree clearing in cross-partition transaction is not supported") - } - - fn iter(&self, tree_idx: usize) -> TxOpResult> { - let tree = self.get_tree(tree_idx)?.clone(); - Ok(Box::new(self.tx.iter(&tree).map(iterator_remap_tx))) - } - fn iter_rev(&self, tree_idx: usize) -> TxOpResult> { - let tree = self.get_tree(tree_idx)?.clone(); - Ok(Box::new(self.tx.iter(&tree).rev().map(iterator_remap_tx))) - } - - fn range<'r>( - &self, - tree_idx: usize, - low: Bound<&'r [u8]>, - high: Bound<&'r [u8]>, - ) -> TxOpResult> { - let tree = self.get_tree(tree_idx)?; - let low = clone_bound(low); - let high = clone_bound(high); - Ok(Box::new( - self.tx - .range::, ByteVecRangeBounds>(&tree, (low, high)) - .map(iterator_remap_tx), - )) - } - fn range_rev<'r>( - &self, - tree_idx: usize, - low: Bound<&'r [u8]>, - high: Bound<&'r [u8]>, - ) -> TxOpResult> { - let tree = self.get_tree(tree_idx)?; - let low = clone_bound(low); - let high = clone_bound(high); - Ok(Box::new( - self.tx - .range::, ByteVecRangeBounds>(&tree, (low, high)) - .rev() - .map(iterator_remap_tx), - )) - } -} - -// -- maps fjall's (k, v) to ours - -fn iterator_remap(r: fjall::Result<(fjall::Slice, fjall::Slice)>) -> Result<(Value, Value)> { - r.map(|(k, v)| (k.to_vec(), v.to_vec())) - .map_err(|e| e.into()) -} - -fn iterator_remap_tx(r: fjall::Result<(fjall::Slice, fjall::Slice)>) -> TxOpResult<(Value, Value)> { - r.map(|(k, v)| (k.to_vec(), v.to_vec())) - .map_err(|e| e.into()) -} - -// -- utils to deal with Garage's tightness on Bound lifetimes - -type ByteVecBound = Bound>; -type ByteVecRangeBounds = (ByteVecBound, ByteVecBound); - -fn clone_bound(bound: Bound<&[u8]>) -> ByteVecBound { - let value = match bound { - Bound::Excluded(v) | Bound::Included(v) => v.to_vec(), - Bound::Unbounded => vec![], - }; - - match bound { - Bound::Included(_) => Bound::Included(value), - Bound::Excluded(_) => Bound::Excluded(value), - Bound::Unbounded => Bound::Unbounded, - } -} - -// -- utils to encode table names -- - -fn encode_name(s: &str) -> Result { - let base = 'A' as u32; - - let mut ret = String::with_capacity(s.len() + 10); - for c in s.chars() { - if c.is_alphanumeric() || c == '_' || c == '-' || c == '#' { - ret.push(c); - } else if c <= u8::MAX as char { - ret.push('$'); - let c_hi = c as u32 / 16; - let c_lo = c as u32 % 16; - ret.push(char::from_u32(base + c_hi).unwrap()); - ret.push(char::from_u32(base + c_lo).unwrap()); - } else { - return Err(Error( - format!("table name {} could not be safely encoded", s).into(), - )); - } - } - Ok(ret) -} - -fn decode_name(s: &str) -> Result { - use std::convert::TryFrom; - - let errfn = || Error(format!("encoded table name {} is invalid", s).into()); - let c_map = |c: char| { - let c = c as u32; - let base = 'A' as u32; - if (base..base + 16).contains(&c) { - Some(c - base) - } else { - None - } - }; - - let mut ret = String::with_capacity(s.len()); - let mut it = s.chars(); - while let Some(c) = it.next() { - if c == '$' { - let c_hi = it.next().and_then(c_map).ok_or_else(errfn)?; - let c_lo = it.next().and_then(c_map).ok_or_else(errfn)?; - let c_dec = char::try_from(c_hi * 16 + c_lo).map_err(|_| errfn())?; - ret.push(c_dec); - } else { - ret.push(c); - } - } - Ok(ret) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_encdec_name() { - for name in [ - "testname", - "test_name", - "test name", - "test$name", - "test:name@help.me$get/this**right", - ] { - let encname = encode_name(name).unwrap(); - assert!(!encname.contains(' ')); - assert!(!encname.contains('.')); - assert!(!encname.contains('*')); - assert_eq!(*name, decode_name(&encname).unwrap()); - } - } -} diff --git a/src/db/lib.rs b/src/db/lib.rs index 2a467c7c..c55c8643 100644 --- a/src/db/lib.rs +++ b/src/db/lib.rs @@ -1,8 +1,6 @@ #[macro_use] extern crate tracing; -#[cfg(feature = "fjall")] -pub mod fjall_adapter; #[cfg(feature = "lmdb")] pub mod lmdb_adapter; #[cfg(feature = "sqlite")] @@ -20,7 +18,7 @@ use std::cell::Cell; use std::path::PathBuf; use std::sync::Arc; -use thiserror::Error; +use err_derive::Error; pub use open::*; @@ -44,7 +42,7 @@ pub type TxValueIter<'a> = Box); impl From for Error { @@ -56,7 +54,7 @@ impl From for Error { pub type Result = std::result::Result; #[derive(Debug, Error)] -#[error("{0}")] +#[error(display = "{}", _0)] pub struct TxOpError(pub(crate) Error); pub type TxOpResult = std::result::Result; @@ -106,44 +104,32 @@ impl Db { result: Cell::new(None), }; let tx_res = self.0.transaction(&f); - let fn_res = f.result.into_inner(); + let ret = f + .result + .into_inner() + .expect("Transaction did not store result"); - match (tx_res, fn_res) { - (Ok(on_commit), Some(Ok(value))) => { - // Transaction succeeded - // TxFn stored the value to return to the user in fn_res - // tx_res contains the on_commit list of callbacks, run them now - on_commit.into_iter().for_each(|f| f()); - Ok(value) - } - (Err(TxError::Abort(())), Some(Err(TxError::Abort(e)))) => { - // Transaction was aborted by user code - // The abort error value is stored in fn_res - Err(TxError::Abort(e)) - } - (Err(TxError::Db(_tx_e)), Some(Err(TxError::Db(fn_e)))) => { - // Transaction encountered a DB error in user code - // The error value encountered is the one in fn_res, - // tx_res contains only a dummy error message - Err(TxError::Db(fn_e)) - } - (Err(TxError::Db(tx_e)), None) => { - // Transaction encounterred a DB error when initializing the transaction, - // before user code was called - Err(TxError::Db(tx_e)) - } - (Err(TxError::Db(tx_e)), Some(Ok(_))) => { - // Transaction encounterred a DB error when commiting the transaction, - // after user code was called - Err(TxError::Db(tx_e)) - } - (tx_res, fn_res) => { - panic!( - "unexpected error case: tx_res={:?}, fn_res={:?}", - tx_res.map(|_| "..."), - fn_res.map(|x| x.map(|_| "...").map_err(|_| "...")) - ); - } + match tx_res { + Ok(on_commit) => match ret { + Ok(value) => { + on_commit.into_iter().for_each(|f| f()); + Ok(value) + } + _ => unreachable!(), + }, + Err(TxError::Abort(())) => match ret { + Err(TxError::Abort(e)) => Err(TxError::Abort(e)), + _ => unreachable!(), + }, + Err(TxError::Db(e2)) => match ret { + // Ok was stored -> the error occurred when finalizing + // transaction + Ok(_) => Err(TxError::Db(e2)), + // An error was already stored: that's the one we want to + // return + Err(TxError::Db(e)) => Err(TxError::Db(e)), + _ => unreachable!(), + }, } } @@ -166,7 +152,7 @@ impl Db { let tree_names = other.list_trees()?; for name in tree_names { let tree = self.open_tree(&name)?; - if !tree.is_empty()? { + if tree.len()? > 0 { return Err(Error(format!("tree {} already contains data", name).into())); } @@ -208,12 +194,8 @@ impl Tree { self.0.get(self.1, key.as_ref()) } #[inline] - pub fn approximate_len(&self) -> Result { - self.0.approximate_len(self.1) - } - #[inline] - pub fn is_empty(&self) -> Result { - self.0.is_empty(self.1) + pub fn len(&self) -> Result { + self.0.len(self.1) } #[inline] @@ -351,8 +333,7 @@ pub(crate) trait IDb: Send + Sync { fn snapshot(&self, path: &PathBuf) -> Result<()>; fn get(&self, tree: usize, key: &[u8]) -> Result>; - fn approximate_len(&self, tree: usize) -> Result; - fn is_empty(&self, tree: usize) -> Result; + fn len(&self, tree: usize) -> Result; fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>; fn remove(&self, tree: usize, key: &[u8]) -> Result<()>; diff --git a/src/db/lmdb_adapter.rs b/src/db/lmdb_adapter.rs index ac185ae9..259aa566 100644 --- a/src/db/lmdb_adapter.rs +++ b/src/db/lmdb_adapter.rs @@ -1,8 +1,8 @@ use core::ops::Bound; +use core::ptr::NonNull; use std::collections::HashMap; use std::convert::TryInto; -use std::marker::PhantomPinned; use std::path::PathBuf; use std::pin::Pin; use std::sync::{Arc, RwLock}; @@ -11,55 +11,12 @@ use heed::types::ByteSlice; use heed::{BytesDecode, Env, RoTxn, RwTxn, UntypedDatabase as Database}; use crate::{ - open::{Engine, OpenOpt}, Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult, TxResult, TxValueIter, Value, ValueIter, }; pub use heed; -// ---- top-level open function - -pub(crate) fn open_db(path: &PathBuf, opt: &OpenOpt) -> Result { - info!("Opening LMDB database at: {}", path.display()); - if let Err(e) = std::fs::create_dir_all(&path) { - return Err(Error( - format!("Unable to create LMDB data directory: {}", e).into(), - )); - } - - let map_size = match opt.lmdb_map_size { - None => recommended_map_size(), - Some(v) => v - (v % 4096), - }; - - let mut env_builder = heed::EnvOpenOptions::new(); - env_builder.max_dbs(100); - env_builder.map_size(map_size); - env_builder.max_readers(2048); - unsafe { - env_builder.flag(heed::flags::Flags::MdbNoRdAhead); - env_builder.flag(heed::flags::Flags::MdbNoMetaSync); - if !opt.fsync { - env_builder.flag(heed::flags::Flags::MdbNoSync); - } - } - match env_builder.open(&path) { - Err(heed::Error::Io(e)) if e.kind() == std::io::ErrorKind::OutOfMemory => { - return Err(Error( - "OutOfMemory error while trying to open LMDB database. This can happen \ - if your operating system is not allowing you to use sufficient virtual \ - memory address space. Please check that no limit is set (ulimit -v). \ - You may also try to set a smaller `lmdb_map_size` configuration parameter. \ - On 32-bit machines, you should probably switch to another database engine." - .into(), - )) - } - Err(e) => Err(Error(format!("Cannot open LMDB database: {}", e).into())), - Ok(db) => Ok(LmdbDb::init(db)), - } -} - // -- err impl From for Error { @@ -147,9 +104,10 @@ impl IDb for LmdbDb { Ok(ret2) } - fn snapshot(&self, base_path: &PathBuf) -> Result<()> { - std::fs::create_dir_all(base_path)?; - let path = Engine::Lmdb.db_path(base_path); + fn snapshot(&self, to: &PathBuf) -> Result<()> { + std::fs::create_dir_all(to)?; + let mut path = to.clone(); + path.push("data.mdb"); self.db .copy_to_path(path, heed::CompactionOption::Enabled)?; Ok(()) @@ -168,16 +126,11 @@ impl IDb for LmdbDb { } } - fn approximate_len(&self, tree: usize) -> Result { + fn len(&self, tree: usize) -> Result { let tree = self.get_tree(tree)?; let tx = self.db.read_txn()?; Ok(tree.len(&tx)?.try_into().unwrap()) } - fn is_empty(&self, tree: usize) -> Result { - let tree = self.get_tree(tree)?; - let tx = self.db.read_txn()?; - Ok(tree.is_empty(&tx)?) - } fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> { let tree = self.get_tree(tree)?; @@ -206,15 +159,13 @@ impl IDb for LmdbDb { fn iter(&self, tree: usize) -> Result> { let tree = self.get_tree(tree)?; let tx = self.db.read_txn()?; - // Safety: the cloture does not store its argument anywhere, - unsafe { TxAndIterator::make(tx, |tx| Ok(tree.iter(tx)?)) } + TxAndIterator::make(tx, |tx| Ok(tree.iter(tx)?)) } fn iter_rev(&self, tree: usize) -> Result> { let tree = self.get_tree(tree)?; let tx = self.db.read_txn()?; - // Safety: the cloture does not store its argument anywhere, - unsafe { TxAndIterator::make(tx, |tx| Ok(tree.rev_iter(tx)?)) } + TxAndIterator::make(tx, |tx| Ok(tree.rev_iter(tx)?)) } fn range<'r>( @@ -225,8 +176,7 @@ impl IDb for LmdbDb { ) -> Result> { let tree = self.get_tree(tree)?; let tx = self.db.read_txn()?; - // Safety: the cloture does not store its argument anywhere, - unsafe { TxAndIterator::make(tx, |tx| Ok(tree.range(tx, &(low, high))?)) } + TxAndIterator::make(tx, |tx| Ok(tree.range(tx, &(low, high))?)) } fn range_rev<'r>( &self, @@ -236,8 +186,7 @@ impl IDb for LmdbDb { ) -> Result> { let tree = self.get_tree(tree)?; let tx = self.db.read_txn()?; - // Safety: the cloture does not store its argument anywhere, - unsafe { TxAndIterator::make(tx, |tx| Ok(tree.rev_range(tx, &(low, high))?)) } + TxAndIterator::make(tx, |tx| Ok(tree.rev_range(tx, &(low, high))?)) } // ---- @@ -367,41 +316,28 @@ where { tx: RoTxn<'a>, iter: Option, - _pin: PhantomPinned, } impl<'a, I> TxAndIterator<'a, I> where I: Iterator> + 'a, { - fn iter(self: Pin<&mut Self>) -> &mut Option { - // Safety: iter is not structural - unsafe { &mut self.get_unchecked_mut().iter } - } - - /// Safety: iterfun must not store its argument anywhere but in its result. - unsafe fn make(tx: RoTxn<'a>, iterfun: F) -> Result> + fn make(tx: RoTxn<'a>, iterfun: F) -> Result> where F: FnOnce(&'a RoTxn<'a>) -> Result, { - let res = TxAndIterator { - tx, - iter: None, - _pin: PhantomPinned, - }; + let res = TxAndIterator { tx, iter: None }; let mut boxed = Box::pin(res); - let tx_lifetime_overextended: &'a RoTxn<'a> = { - let tx = &boxed.tx; - // Safety: Artificially extending the lifetime because - // this reference will only be stored and accessed from the - // returned ValueIter which guarantees that it is destroyed - // before the tx it is pointing to. - unsafe { &*&raw const *tx } - }; - let iter = iterfun(&tx_lifetime_overextended)?; + // This unsafe allows us to bypass lifetime checks + let tx = unsafe { NonNull::from(&boxed.tx).as_ref() }; + let iter = iterfun(tx)?; - *boxed.as_mut().iter() = Some(iter); + let mut_ref = Pin::as_mut(&mut boxed); + // This unsafe allows us to write in a field of the pinned struct + unsafe { + Pin::get_unchecked_mut(mut_ref).iter = Some(iter); + } Ok(Box::new(TxAndIteratorPin(boxed))) } @@ -412,10 +348,8 @@ where I: Iterator> + 'a, { fn drop(&mut self) { - // Safety: `new_unchecked` is okay because we know this value is never - // used again after being dropped. - let this = unsafe { Pin::new_unchecked(self) }; - drop(this.iter().take()); + // ensure the iterator is dropped before the RoTxn it references + drop(self.iter.take()); } } @@ -431,12 +365,13 @@ where fn next(&mut self) -> Option { let mut_ref = Pin::as_mut(&mut self.0); - let next = mut_ref.iter().as_mut()?.next()?; - let res = match next { - Err(e) => Err(e.into()), - Ok((k, v)) => Ok((k.to_vec(), v.to_vec())), - }; - Some(res) + // This unsafe allows us to mutably access the iterator field + let next = unsafe { Pin::get_unchecked_mut(mut_ref).iter.as_mut()?.next() }; + match next { + None => None, + Some(Err(e)) => Some(Err(e.into())), + Some(Ok((k, v))) => Some(Ok((k.to_vec(), v.to_vec()))), + } } } diff --git a/src/db/open.rs b/src/db/open.rs index 23391c61..ff3bc830 100644 --- a/src/db/open.rs +++ b/src/db/open.rs @@ -11,7 +11,6 @@ use crate::{Db, Error, Result}; pub enum Engine { Lmdb, Sqlite, - Fjall, } impl Engine { @@ -20,26 +19,8 @@ impl Engine { match self { Self::Lmdb => "lmdb", Self::Sqlite => "sqlite", - Self::Fjall => "fjall", } } - - /// Return engine-specific DB path from base path - pub fn db_path(&self, base_path: &PathBuf) -> PathBuf { - let mut ret = base_path.clone(); - match self { - Self::Lmdb => { - ret.push("db.lmdb"); - } - Self::Sqlite => { - ret.push("db.sqlite"); - } - Self::Fjall => { - ret.push("db.fjall"); - } - } - ret - } } impl std::fmt::Display for Engine { @@ -55,11 +36,10 @@ impl std::str::FromStr for Engine { match text { "lmdb" | "heed" => Ok(Self::Lmdb), "sqlite" | "sqlite3" | "rusqlite" => Ok(Self::Sqlite), - "fjall" => Ok(Self::Fjall), "sled" => Err(Error("Sled is no longer supported as a database engine. Converting your old metadata db can be done using an older Garage binary (e.g. v0.9.4).".into())), kind => Err(Error( format!( - "Invalid DB engine: {} (options are: lmdb, sqlite, fjall)", + "Invalid DB engine: {} (options are: lmdb, sqlite)", kind ) .into(), @@ -71,7 +51,6 @@ impl std::str::FromStr for Engine { pub struct OpenOpt { pub fsync: bool, pub lmdb_map_size: Option, - pub fjall_block_cache_size: Option, } impl Default for OpenOpt { @@ -79,7 +58,6 @@ impl Default for OpenOpt { Self { fsync: false, lmdb_map_size: None, - fjall_block_cache_size: None, } } } @@ -88,15 +66,53 @@ pub fn open_db(path: &PathBuf, engine: Engine, opt: &OpenOpt) -> Result { match engine { // ---- Sqlite DB ---- #[cfg(feature = "sqlite")] - Engine::Sqlite => crate::sqlite_adapter::open_db(path, opt), + Engine::Sqlite => { + info!("Opening Sqlite database at: {}", path.display()); + let manager = r2d2_sqlite::SqliteConnectionManager::file(path); + Ok(crate::sqlite_adapter::SqliteDb::new(manager, opt.fsync)?) + } // ---- LMDB DB ---- #[cfg(feature = "lmdb")] - Engine::Lmdb => crate::lmdb_adapter::open_db(path, opt), + Engine::Lmdb => { + info!("Opening LMDB database at: {}", path.display()); + if let Err(e) = std::fs::create_dir_all(&path) { + return Err(Error( + format!("Unable to create LMDB data directory: {}", e).into(), + )); + } - // ---- Fjall DB ---- - #[cfg(feature = "fjall")] - Engine::Fjall => crate::fjall_adapter::open_db(path, opt), + let map_size = match opt.lmdb_map_size { + None => crate::lmdb_adapter::recommended_map_size(), + Some(v) => v - (v % 4096), + }; + + let mut env_builder = heed::EnvOpenOptions::new(); + env_builder.max_dbs(100); + env_builder.map_size(map_size); + env_builder.max_readers(2048); + unsafe { + env_builder.flag(crate::lmdb_adapter::heed::flags::Flags::MdbNoRdAhead); + env_builder.flag(crate::lmdb_adapter::heed::flags::Flags::MdbNoMetaSync); + if !opt.fsync { + env_builder.flag(heed::flags::Flags::MdbNoSync); + } + } + match env_builder.open(&path) { + Err(heed::Error::Io(e)) if e.kind() == std::io::ErrorKind::OutOfMemory => { + return Err(Error( + "OutOfMemory error while trying to open LMDB database. This can happen \ + if your operating system is not allowing you to use sufficient virtual \ + memory address space. Please check that no limit is set (ulimit -v). \ + You may also try to set a smaller `lmdb_map_size` configuration parameter. \ + On 32-bit machines, you should probably switch to another database engine." + .into(), + )) + } + Err(e) => Err(Error(format!("Cannot open LMDB database: {}", e).into())), + Ok(db) => Ok(crate::lmdb_adapter::LmdbDb::init(db)), + } + } // Pattern is unreachable when all supported DB engines are compiled into binary. The allow // attribute is added so that we won't have to change this match in case stop building diff --git a/src/db/sqlite_adapter.rs b/src/db/sqlite_adapter.rs index a03ee8ef..ce6412b6 100644 --- a/src/db/sqlite_adapter.rs +++ b/src/db/sqlite_adapter.rs @@ -11,23 +11,12 @@ use r2d2_sqlite::SqliteConnectionManager; use rusqlite::{params, Rows, Statement, Transaction}; use crate::{ - open::{Engine, OpenOpt}, Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult, TxResult, TxValueIter, Value, ValueIter, }; pub use rusqlite; -// ---- top-level open function - -pub(crate) fn open_db(path: &PathBuf, opt: &OpenOpt) -> Result { - info!("Opening Sqlite database at: {}", path.display()); - let manager = r2d2_sqlite::SqliteConnectionManager::file(path); - Ok(SqliteDb::new(manager, opt.fsync)?) -} - -// ---- - type Connection = r2d2::PooledConnection; // --- err @@ -150,18 +139,17 @@ impl IDb for SqliteDb { Ok(trees) } - fn snapshot(&self, base_path: &PathBuf) -> Result<()> { - std::fs::create_dir_all(base_path)?; - let path = Engine::Sqlite - .db_path(&base_path) - .into_os_string() - .into_string() - .map_err(|_| Error("invalid sqlite path string".into()))?; - - info!("Start sqlite VACUUM INTO `{}`", path); - self.db.get()?.execute("VACUUM INTO ?1", params![path])?; - info!("Finished sqlite VACUUM INTO `{}`", path); - + fn snapshot(&self, to: &PathBuf) -> Result<()> { + fn progress(p: rusqlite::backup::Progress) { + let percent = (p.pagecount - p.remaining) * 100 / p.pagecount; + info!("Sqlite snapshot progress: {}%", percent); + } + std::fs::create_dir_all(to)?; + let mut path = to.clone(); + path.push("db.sqlite"); + self.db + .get()? + .backup(rusqlite::DatabaseName::Main, path, Some(progress))?; Ok(()) } @@ -172,7 +160,7 @@ impl IDb for SqliteDb { self.internal_get(&self.db.get()?, &tree, key) } - fn approximate_len(&self, tree: usize) -> Result { + fn len(&self, tree: usize) -> Result { let tree = self.get_tree(tree)?; let db = self.db.get()?; @@ -184,10 +172,6 @@ impl IDb for SqliteDb { } } - fn is_empty(&self, tree: usize) -> Result { - Ok(self.approximate_len(tree)? == 0) - } - fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> { let tree = self.get_tree(tree)?; let db = self.db.get()?; diff --git a/src/db/test.rs b/src/db/test.rs index 977dc965..26b816b8 100644 --- a/src/db/test.rs +++ b/src/db/test.rs @@ -1,7 +1,7 @@ use crate::*; fn test_suite(db: Db) { - let tree = db.open_tree("tree:this_is_a_tree").unwrap(); + let tree = db.open_tree("tree").unwrap(); let ka: &[u8] = &b"test"[..]; let kb: &[u8] = &b"zwello"[..]; @@ -14,7 +14,7 @@ fn test_suite(db: Db) { assert!(tree.insert(ka, va).is_ok()); assert_eq!(tree.get(ka).unwrap().unwrap(), va); - assert_eq!(tree.iter().unwrap().count(), 1); + assert_eq!(tree.len().unwrap(), 1); // ---- test transaction logic ---- @@ -148,15 +148,3 @@ fn test_sqlite_db() { let db = SqliteDb::new(manager, false).unwrap(); test_suite(db); } - -#[test] -#[cfg(feature = "fjall")] -fn test_fjall_db() { - use crate::fjall_adapter::{fjall, FjallDb}; - - let path = mktemp::Temp::new_dir().unwrap(); - let config = fjall::Config::new(path).temporary(true); - let keyspace = config.open_transactional().unwrap(); - let db = FjallDb::init(keyspace); - test_suite(db); -} diff --git a/src/garage/Cargo.toml b/src/garage/Cargo.toml index a4f695a4..f03c7331 100644 --- a/src/garage/Cargo.toml +++ b/src/garage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -57,7 +57,6 @@ opentelemetry.workspace = true opentelemetry-prometheus = { workspace = true, optional = true } opentelemetry-otlp = { workspace = true, optional = true } syslog-tracing = { workspace = true, optional = true } -tracing-journald = { workspace = true, optional = true } [dev-dependencies] garage_api_common.workspace = true @@ -91,7 +90,6 @@ k2v = [ "garage_util/k2v", "garage_api_k2v" ] # Database engines lmdb = [ "garage_model/lmdb" ] sqlite = [ "garage_model/sqlite" ] -fjall = [ "garage_model/fjall" ] # Automatic registration and discovery via Consul API consul-discovery = [ "garage_rpc/consul-discovery" ] @@ -103,8 +101,6 @@ metrics = [ "garage_api_admin/metrics", "opentelemetry-prometheus" ] telemetry-otlp = [ "opentelemetry-otlp" ] # Logging to syslog syslog = [ "syslog-tracing" ] -# Logging to journald -journald = [ "tracing-journald" ] # NOTE: bundled-libs and system-libs should be treat as mutually exclusive; # exactly one of them should be enabled. diff --git a/src/garage/admin/block.rs b/src/garage/admin/block.rs index 5f908ce4..edeb88c0 100644 --- a/src/garage/admin/block.rs +++ b/src/garage/admin/block.rs @@ -101,7 +101,6 @@ impl AdminRpcHandler { let mut obj_dels = 0; let mut mpu_dels = 0; let mut ver_dels = 0; - let mut br_dels = 0; for hash in blocks { let hash = hex::decode(hash).ok_or_bad_request("invalid hash")?; @@ -132,19 +131,12 @@ impl AdminRpcHandler { ver_dels += 1; } } - if !br.deleted.get() { - let mut br = br; - br.deleted.set(); - self.garage.block_ref_table.insert(&br).await?; - br_dels += 1; - } } } Ok(AdminRpc::Ok(format!( - "Purged {} blocks: marked {} block refs, {} versions, {} objects and {} multipart uploads as deleted", + "Purged {} blocks, {} versions, {} objects, {} multipart uploads", blocks.len(), - br_dels, ver_dels, obj_dels, mpu_dels, diff --git a/src/garage/admin/bucket.rs b/src/garage/admin/bucket.rs index 073329c1..1bdc6086 100644 --- a/src/garage/admin/bucket.rs +++ b/src/garage/admin/bucket.rs @@ -126,7 +126,7 @@ impl AdminRpcHandler { #[allow(clippy::ptr_arg)] async fn handle_create_bucket(&self, name: &String) -> Result { - if !is_valid_bucket_name(name, self.garage.config.allow_punycode) { + if !is_valid_bucket_name(name) { return Err(Error::BadRequest(format!( "{}: {}", name, INVALID_BUCKET_NAME_MESSAGE diff --git a/src/garage/admin/mod.rs b/src/garage/admin/mod.rs index 6ae8fa88..321326e0 100644 --- a/src/garage/admin/mod.rs +++ b/src/garage/admin/mod.rs @@ -13,6 +13,8 @@ use serde::{Deserialize, Serialize}; use format_table::format_table_to_string; +use garage_net::endpoint::RpcInFlightLimiter; + use garage_util::background::BackgroundRunner; use garage_util::data::*; use garage_util::error::Error as GarageError; @@ -118,6 +120,7 @@ impl AdminRpcHandler { &node, AdminRpc::LaunchRepair(opt_to_send.clone()), PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, ) .await; if !matches!(resp, Ok(Ok(_))) { @@ -164,7 +167,12 @@ impl AdminRpcHandler { let node_id = (*node).into(); match self .endpoint - .call(&node_id, AdminRpc::Stats(opt), PRIO_NORMAL) + .call( + &node_id, + AdminRpc::Stats(opt), + PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, + ) .await { Ok(Ok(AdminRpc::Ok(s))) => writeln!(&mut ret, "{}", s).unwrap(), @@ -219,7 +227,7 @@ impl AdminRpcHandler { // Gather block manager statistics writeln!(&mut ret, "\nBlock manager stats:").unwrap(); - let rc_len = self.garage.block_manager.rc_approximate_len()?.to_string(); + let rc_len = self.garage.block_manager.rc_len()?.to_string(); writeln!( &mut ret, @@ -230,13 +238,13 @@ impl AdminRpcHandler { writeln!( &mut ret, " resync queue length: {}", - self.garage.block_manager.resync.queue_approximate_len()? + self.garage.block_manager.resync.queue_len()? ) .unwrap(); writeln!( &mut ret, " blocks with resync errors: {}", - self.garage.block_manager.resync.errors_approximate_len()? + self.garage.block_manager.resync.errors_len()? ) .unwrap(); @@ -346,21 +354,16 @@ impl AdminRpcHandler { F: TableSchema + 'static, R: TableReplication + 'static, { - let data_len = t - .data - .store - .approximate_len() - .map_err(GarageError::from)? - .to_string(); - let mkl_len = t.merkle_updater.merkle_tree_approximate_len()?.to_string(); + let data_len = t.data.store.len().map_err(GarageError::from)?.to_string(); + let mkl_len = t.merkle_updater.merkle_tree_len()?.to_string(); Ok(format!( " {}\t{}\t{}\t{}\t{}", F::TABLE_NAME, data_len, mkl_len, - t.merkle_updater.todo_approximate_len()?, - t.data.gc_todo_approximate_len()? + t.merkle_updater.todo_len()?, + t.data.gc_todo_len()? )) } @@ -412,6 +415,7 @@ impl AdminRpcHandler { variable: variable.clone(), }), PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, ) .await?? { @@ -461,6 +465,7 @@ impl AdminRpcHandler { value: value.to_string(), }), PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, ) .await?? { @@ -493,6 +498,7 @@ impl AdminRpcHandler { &to, AdminRpc::MetaOperation(MetaOperation::Snapshot { all: false }), PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, ) .await? })) diff --git a/src/garage/cli/cmd.rs b/src/garage/cli/cmd.rs index 44d3d96c..22449b41 100644 --- a/src/garage/cli/cmd.rs +++ b/src/garage/cli/cmd.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::time::Duration; use format_table::format_table; +use garage_net::endpoint::RpcInFlightLimiter; use garage_util::error::*; use garage_rpc::layout::*; @@ -200,7 +201,12 @@ pub async fn cmd_connect( args: ConnectNodeOpt, ) -> Result<(), Error> { match rpc_cli - .call(&rpc_host, SystemRpc::Connect(args.node), PRIO_NORMAL) + .call( + &rpc_host, + SystemRpc::Connect(args.node), + PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, + ) .await?? { SystemRpc::Ok => { @@ -216,7 +222,10 @@ pub async fn cmd_admin( rpc_host: NodeID, args: AdminRpc, ) -> Result<(), HelperError> { - match rpc_cli.call(&rpc_host, args, PRIO_NORMAL).await?? { + match rpc_cli + .call(&rpc_host, args, PRIO_NORMAL, RpcInFlightLimiter::NoLimit) + .await?? + { AdminRpc::Ok(msg) => { println!("{}", msg); } @@ -271,7 +280,12 @@ pub async fn fetch_status( rpc_host: NodeID, ) -> Result, Error> { match rpc_cli - .call(&rpc_host, SystemRpc::GetKnownNodes, PRIO_NORMAL) + .call( + &rpc_host, + SystemRpc::GetKnownNodes, + PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, + ) .await?? { SystemRpc::ReturnKnownNodes(nodes) => Ok(nodes), diff --git a/src/garage/cli/layout.rs b/src/garage/cli/layout.rs index f053eef4..554d7096 100644 --- a/src/garage/cli/layout.rs +++ b/src/garage/cli/layout.rs @@ -1,6 +1,7 @@ use bytesize::ByteSize; use format_table::format_table; +use garage_net::endpoint::RpcInFlightLimiter; use garage_util::crdt::Crdt; use garage_util::error::*; @@ -45,7 +46,12 @@ pub async fn cmd_assign_role( args: AssignRoleOpt, ) -> Result<(), Error> { let status = match rpc_cli - .call(&rpc_host, SystemRpc::GetKnownNodes, PRIO_NORMAL) + .call( + &rpc_host, + SystemRpc::GetKnownNodes, + PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, + ) .await?? { SystemRpc::ReturnKnownNodes(nodes) => nodes, @@ -475,7 +481,12 @@ pub async fn fetch_layout( rpc_host: NodeID, ) -> Result { match rpc_cli - .call(&rpc_host, SystemRpc::PullClusterLayout, PRIO_NORMAL) + .call( + &rpc_host, + SystemRpc::PullClusterLayout, + PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, + ) .await?? { SystemRpc::AdvertiseClusterLayout(t) => Ok(t), @@ -493,6 +504,7 @@ pub async fn send_layout( &rpc_host, SystemRpc::AdvertiseClusterLayout(layout), PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, ) .await??; Ok(()) diff --git a/src/garage/cli/structs.rs b/src/garage/cli/structs.rs index 386a213b..4ec35e68 100644 --- a/src/garage/cli/structs.rs +++ b/src/garage/cli/structs.rs @@ -466,10 +466,6 @@ pub enum RepairWhat { /// Repair (resync/rebalance) the set of stored blocks in the cluster #[structopt(name = "blocks", version = garage_version())] Blocks, - /// Clear the block resync queue. The list of blocks in errored state - /// is cleared as well. You MUST run `garage repair blocks` after invoking this. - #[structopt(name = "clear-resync-queue", version = garage_version())] - ClearResyncQueue, /// Repropagate object deletions to the version table #[structopt(name = "versions", version = garage_version())] Versions, @@ -482,9 +478,6 @@ pub enum RepairWhat { /// Recalculate block reference counters #[structopt(name = "block-rc", version = garage_version())] BlockRc, - /// Fix inconsistency in bucket aliases (WARNING: EXPERIMENTAL) - #[structopt(name = "aliases", version = garage_version())] - Aliases, /// Verify integrity of all blocks on disc #[structopt(name = "scrub", version = garage_version())] Scrub { diff --git a/src/garage/main.rs b/src/garage/main.rs index 2703bedd..04f34d72 100644 --- a/src/garage/main.rs +++ b/src/garage/main.rs @@ -208,43 +208,6 @@ fn init_logging(opt: &Opt) { } } - if std::env::var("GARAGE_LOG_TO_JOURNALD") - .map(|x| x == "1" || x == "true") - .unwrap_or(false) - { - #[cfg(feature = "journald")] - { - use tracing_journald::{Priority, PriorityMappings}; - use tracing_subscriber::layer::SubscriberExt; - use tracing_subscriber::util::SubscriberInitExt; - - let registry = tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer().with_writer(std::io::sink)) - .with(env_filter); - match tracing_journald::layer() { - Ok(layer) => { - registry - .with(layer.with_priority_mappings(PriorityMappings { - info: Priority::Informational, - debug: Priority::Debug, - ..PriorityMappings::new() - })) - .init(); - } - Err(e) => { - eprintln!("Couldn't connect to journald: {}.", e); - std::process::exit(1); - } - } - return; - } - #[cfg(not(feature = "journald"))] - { - eprintln!("Journald support is not enabled in this build."); - std::process::exit(1); - } - } - tracing_subscriber::fmt() .with_writer(std::io::stderr) .with_env_filter(env_filter) @@ -281,7 +244,7 @@ async fn cli_command(opt: Opt) -> Result<(), Error> { // Generate a temporary keypair for our RPC client let (_pk, sk) = sodiumoxide::crypto::sign::ed25519::gen_keypair(); - let netapp = NetApp::new(GARAGE_VERSION_TAG, network_key, sk, None); + let netapp = NetApp::new(GARAGE_VERSION_TAG, network_key, sk, None, None); // Find and parse the address of the target host let (id, addr, is_default_addr) = if let Some(h) = opt.rpc_host { diff --git a/src/garage/repair/online.rs b/src/garage/repair/online.rs index 6a7dafcf..47883f97 100644 --- a/src/garage/repair/online.rs +++ b/src/garage/repair/online.rs @@ -88,15 +88,6 @@ pub async fn launch_online_repair( garage.block_manager.clone(), )); } - RepairWhat::Aliases => { - info!("Repairing bucket aliases (foreground)"); - garage.locked_helper().await.repair_aliases().await?; - } - RepairWhat::ClearResyncQueue => { - let garage = garage.clone(); - tokio::task::spawn_blocking(move || garage.block_manager.resync.clear_resync_queue()) - .await?? - } } Ok(()) } diff --git a/src/garage/server.rs b/src/garage/server.rs index b81ae334..1dc86fd3 100644 --- a/src/garage/server.rs +++ b/src/garage/server.rs @@ -183,21 +183,10 @@ fn watch_shutdown_signal() -> watch::Receiver { let mut sigterm = signal(SignalKind::terminate()).expect("Failed to install SIGTERM handler"); let mut sighup = signal(SignalKind::hangup()).expect("Failed to install SIGHUP handler"); - loop { - tokio::select! { - _ = sigint.recv() => { - info!("Received SIGINT, shutting down."); - break - } - _ = sigterm.recv() => { - info!("Received SIGTERM, shutting down."); - break - } - _ = sighup.recv() => { - info!("Received SIGHUP, reload not supported."); - continue - } - } + tokio::select! { + _ = sigint.recv() => info!("Received SIGINT, shutting down."), + _ = sigterm.recv() => info!("Received SIGTERM, shutting down."), + _ = sighup.recv() => info!("Received SIGHUP, shutting down."), } send_cancel.send(true).unwrap(); }); diff --git a/src/garage/tests/common/garage.rs b/src/garage/tests/common/garage.rs index 2b0a381c..8d71504f 100644 --- a/src/garage/tests/common/garage.rs +++ b/src/garage/tests/common/garage.rs @@ -63,8 +63,6 @@ rpc_bind_addr = "127.0.0.1:{rpc_port}" rpc_public_addr = "127.0.0.1:{rpc_port}" rpc_secret = "{secret}" -allow_punycode = true - [s3_api] s3_region = "{region}" api_bind_addr = "127.0.0.1:{s3_port}" diff --git a/src/garage/tests/s3/objects.rs b/src/garage/tests/s3/objects.rs index 53e8231d..d63ac000 100644 --- a/src/garage/tests/s3/objects.rs +++ b/src/garage/tests/s3/objects.rs @@ -198,7 +198,6 @@ async fn test_precondition() { ); } let older_date = DateTime::from_secs_f64(last_modified.as_secs_f64() - 10.0); - let same_date = DateTime::from_secs_f64(last_modified.as_secs_f64()); let newer_date = DateTime::from_secs_f64(last_modified.as_secs_f64() + 10.0); { let err = ctx @@ -213,18 +212,6 @@ async fn test_precondition() { matches!(err, Err(SdkError::ServiceError(se)) if se.raw().status().as_u16() == 304) ); - let err = ctx - .client - .get_object() - .bucket(&bucket) - .key(STD_KEY) - .if_modified_since(same_date) - .send() - .await; - assert!( - matches!(err, Err(SdkError::ServiceError(se)) if se.raw().status().as_u16() == 304) - ); - let o = ctx .client .get_object() @@ -249,17 +236,6 @@ async fn test_precondition() { matches!(err, Err(SdkError::ServiceError(se)) if se.raw().status().as_u16() == 412) ); - let o = ctx - .client - .get_object() - .bucket(&bucket) - .key(STD_KEY) - .if_unmodified_since(same_date) - .send() - .await - .unwrap(); - assert_eq!(o.e_tag.as_ref().unwrap().as_str(), etag); - let o = ctx .client .get_object() diff --git a/src/garage/tests/s3/website.rs b/src/garage/tests/s3/website.rs index bbac3de5..9a9e29f2 100644 --- a/src/garage/tests/s3/website.rs +++ b/src/garage/tests/s3/website.rs @@ -533,118 +533,3 @@ async fn test_website_check_domain() { }) ); } - -#[tokio::test] -async fn test_website_puny() { - const BCKT_NAME: &str = "xn--pda.eu"; - let ctx = common::context(); - let bucket = ctx.create_bucket(BCKT_NAME); - - let data = ByteStream::from_static(BODY); - - ctx.client - .put_object() - .bucket(&bucket) - .key("index.html") - .body(data) - .send() - .await - .unwrap(); - - let client = Client::builder(TokioExecutor::new()).build_http(); - - let req = |suffix| { - Request::builder() - .method("GET") - .uri(format!("http://127.0.0.1:{}/", ctx.garage.web_port)) - .header("Host", format!("{}{}", BCKT_NAME, suffix)) - .body(Body::new(Bytes::new())) - .unwrap() - }; - - ctx.garage - .command() - .args(["bucket", "website", "--allow", BCKT_NAME]) - .quiet() - .expect_success_status("Could not allow website on bucket"); - - let mut resp = client.request(req("")).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.into_body().collect().await.unwrap().to_bytes(), - BODY.as_ref() - ); - - resp = client.request(req(".web.garage")).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.into_body().collect().await.unwrap().to_bytes(), - BODY.as_ref() - ); - - for bname in [ - BCKT_NAME.to_string(), - format!("{BCKT_NAME}.web.garage"), - format!("{BCKT_NAME}.s3.garage"), - ] { - let admin_req = || { - Request::builder() - .method("GET") - .uri(format!( - "http://127.0.0.1:{0}/check?domain={1}", - ctx.garage.admin_port, bname - )) - .body(Body::new(Bytes::new())) - .unwrap() - }; - - let admin_resp = client.request(admin_req()).await.unwrap(); - assert_eq!(admin_resp.status(), StatusCode::OK); - assert_eq!( - admin_resp.into_body().collect().await.unwrap().to_bytes(), - format!("Domain '{bname}' is managed by Garage").as_bytes() - ); - } -} - -#[tokio::test] -async fn test_website_object_not_found() { - const BCKT_NAME: &str = "not-found"; - let ctx = common::context(); - let _bucket = ctx.create_bucket(BCKT_NAME); - - let client = Client::builder(TokioExecutor::new()).build_http(); - - let req = |suffix| { - Request::builder() - .method("GET") - .uri(format!("http://127.0.0.1:{}/", ctx.garage.web_port)) - .header("Host", format!("{}{}", BCKT_NAME, suffix)) - .body(Body::new(Bytes::new())) - .unwrap() - }; - - ctx.garage - .command() - .args(["bucket", "website", "--allow", BCKT_NAME]) - .quiet() - .expect_success_status("Could not allow website on bucket"); - - let resp = client.request(req("")).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - // the error we return by default are *not* xml - assert_eq!( - resp.headers().get(http::header::CONTENT_TYPE).unwrap(), - "text/html; charset=utf-8" - ); - let result = String::from_utf8( - resp.into_body() - .collect() - .await - .unwrap() - .to_bytes() - .to_vec(), - ) - .unwrap(); - assert!(result.contains("not found")); -} diff --git a/src/k2v-client/lib.rs b/src/k2v-client/lib.rs index fe8fd3e0..9cf8d902 100644 --- a/src/k2v-client/lib.rs +++ b/src/k2v-client/lib.rs @@ -72,16 +72,6 @@ impl K2vClient { .enable_http2() .build(); let client = HttpClient::builder(TokioExecutor::new()).build(connector); - Self::new_with_client(config, client) - } - - /// Create a new K2V client with an external client. - /// Useful for example if you plan on creating many clients but you want to mutualize the - /// underlying thread pools & co. - pub fn new_with_client( - config: K2vClientConfig, - client: HttpClient, Body>, - ) -> Result { let user_agent: std::borrow::Cow = match &config.user_agent { Some(ua) => ua.into(), None => format!("k2v/{}", env!("CARGO_PKG_VERSION")).into(), diff --git a/src/model/Cargo.toml b/src/model/Cargo.toml index 289c0024..42ec8537 100644 --- a/src/model/Cargo.toml +++ b/src/model/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_model" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -24,7 +24,7 @@ garage_net.workspace = true async-trait.workspace = true blake2.workspace = true chrono.workspace = true -thiserror.workspace = true +err-derive.workspace = true hex.workspace = true http.workspace = true base64.workspace = true @@ -44,4 +44,3 @@ default = [ "lmdb", "sqlite" ] k2v = [ "garage_util/k2v" ] lmdb = [ "garage_db/lmdb" ] sqlite = [ "garage_db/sqlite" ] -fjall = [ "garage_db/fjall" ] diff --git a/src/model/bucket_alias_table.rs b/src/model/bucket_alias_table.rs index 276d0d1c..8bbe4118 100644 --- a/src/model/bucket_alias_table.rs +++ b/src/model/bucket_alias_table.rs @@ -22,10 +22,14 @@ mod v08 { pub use v08::*; impl BucketAlias { - pub fn new(name: String, ts: u64, bucket_id: Option) -> Self { - BucketAlias { - name, - state: crdt::Lww::raw(ts, bucket_id), + pub fn new(name: String, ts: u64, bucket_id: Option) -> Option { + if !is_valid_bucket_name(&name) { + None + } else { + Some(BucketAlias { + name, + state: crdt::Lww::raw(ts, bucket_id), + }) } } @@ -76,7 +80,7 @@ impl TableSchema for BucketAliasTable { /// In the case of Garage, bucket names must not be hex-encoded /// 32 byte string, which is excluded thanks to the /// maximum length of 63 bytes given in the spec. -pub fn is_valid_bucket_name(n: &str, puny: bool) -> bool { +pub fn is_valid_bucket_name(n: &str) -> bool { // Bucket names must be between 3 and 63 characters n.len() >= 3 && n.len() <= 63 // Bucket names must be composed of lowercase letters, numbers, @@ -88,9 +92,7 @@ pub fn is_valid_bucket_name(n: &str, puny: bool) -> bool { // Bucket names must not be formatted as an IP address && n.parse::().is_err() // Bucket names must not start with "xn--" - && (!n.starts_with("xn--") || puny) - // We are a bit stricter, to properly restrict punycode in all labels - && (!n.contains(".xn--") || puny) + && !n.starts_with("xn--") // Bucket names must not end with "-s3alias" && !n.ends_with("-s3alias") } diff --git a/src/model/garage.rs b/src/model/garage.rs index f4f6f693..10a36ac1 100644 --- a/src/model/garage.rs +++ b/src/model/garage.rs @@ -116,17 +116,21 @@ impl Garage { info!("Opening database..."); let db_engine = db::Engine::from_str(&config.db_engine) .ok_or_message("Invalid `db_engine` value in configuration file")?; - let db_path = db_engine.db_path(&config.metadata_dir); + let mut db_path = config.metadata_dir.clone(); + match db_engine { + db::Engine::Sqlite => { + db_path.push("db.sqlite"); + } + db::Engine::Lmdb => { + db_path.push("db.lmdb"); + } + } let db_opt = db::OpenOpt { fsync: config.metadata_fsync, lmdb_map_size: match config.lmdb_map_size { v if v == usize::default() => None, v => Some(v), }, - fjall_block_cache_size: match config.fjall_block_cache_size { - v if v == usize::default() => None, - v => Some(v), - }, }; let db = db::open_db(&db_path, db_engine, &db_opt) .ok_or_message("Unable to open metadata db")?; @@ -171,7 +175,13 @@ impl Garage { // ---- admin tables ---- info!("Initialize bucket_table..."); - let bucket_table = Table::new(BucketTable, control_rep_param.clone(), system.clone(), &db); + let bucket_table = Table::new( + BucketTable, + control_rep_param.clone(), + system.clone(), + &db, + &config.experimental.merkle_backpressure, + ); info!("Initialize bucket_alias_table..."); let bucket_alias_table = Table::new( @@ -179,9 +189,16 @@ impl Garage { control_rep_param.clone(), system.clone(), &db, + &config.experimental.merkle_backpressure, ); info!("Initialize key_table_table..."); - let key_table = Table::new(KeyTable, control_rep_param, system.clone(), &db); + let key_table = Table::new( + KeyTable, + control_rep_param, + system.clone(), + &db, + &config.experimental.merkle_backpressure, + ); // ---- S3 tables ---- info!("Initialize block_ref_table..."); @@ -192,6 +209,7 @@ impl Garage { meta_rep_param.clone(), system.clone(), &db, + &config.experimental.merkle_backpressure, ); info!("Initialize version_table..."); @@ -202,10 +220,12 @@ impl Garage { meta_rep_param.clone(), system.clone(), &db, + &config.experimental.merkle_backpressure, ); info!("Initialize multipart upload counter table..."); - let mpu_counter_table = IndexCounter::new(system.clone(), meta_rep_param.clone(), &db); + let mpu_counter_table = + IndexCounter::new(system.clone(), meta_rep_param.clone(), &db, &config); info!("Initialize multipart upload table..."); let mpu_table = Table::new( @@ -216,10 +236,12 @@ impl Garage { meta_rep_param.clone(), system.clone(), &db, + &config.experimental.merkle_backpressure, ); info!("Initialize object counter table..."); - let object_counter_table = IndexCounter::new(system.clone(), meta_rep_param.clone(), &db); + let object_counter_table = + IndexCounter::new(system.clone(), meta_rep_param.clone(), &db, &config); info!("Initialize object_table..."); #[allow(clippy::redundant_clone)] @@ -232,6 +254,7 @@ impl Garage { meta_rep_param.clone(), system.clone(), &db, + &config.experimental.merkle_backpressure, ); info!("Load lifecycle worker state..."); @@ -241,7 +264,7 @@ impl Garage { // ---- K2V ---- #[cfg(feature = "k2v")] - let k2v = GarageK2V::new(system.clone(), &db, meta_rep_param); + let k2v = GarageK2V::new(system.clone(), &db, meta_rep_param, &config); // ---- setup block refcount recalculation ---- // this function can be used to fix inconsistencies in the RC table @@ -315,15 +338,15 @@ impl Garage { Ok(()) } - pub fn bucket_helper(&self) -> helper::bucket::BucketHelper<'_> { + pub fn bucket_helper(&self) -> helper::bucket::BucketHelper { helper::bucket::BucketHelper(self) } - pub fn key_helper(&self) -> helper::key::KeyHelper<'_> { + pub fn key_helper(&self) -> helper::key::KeyHelper { helper::key::KeyHelper(self) } - pub async fn locked_helper(&self) -> helper::locked::LockedHelper<'_> { + pub async fn locked_helper(&self) -> helper::locked::LockedHelper { let lock = self.bucket_lock.lock().await; helper::locked::LockedHelper(self, Some(lock)) } @@ -331,9 +354,14 @@ impl Garage { #[cfg(feature = "k2v")] impl GarageK2V { - fn new(system: Arc, db: &db::Db, meta_rep_param: TableShardedReplication) -> Self { + fn new( + system: Arc, + db: &db::Db, + meta_rep_param: TableShardedReplication, + config: &Config, + ) -> Self { info!("Initialize K2V counter table..."); - let counter_table = IndexCounter::new(system.clone(), meta_rep_param.clone(), db); + let counter_table = IndexCounter::new(system.clone(), meta_rep_param.clone(), db, config); info!("Initialize K2V subscription manager..."); let subscriptions = Arc::new(SubscriptionManager::new()); @@ -347,6 +375,7 @@ impl GarageK2V { meta_rep_param, system.clone(), db, + &config.experimental.merkle_backpressure, ); info!("Initialize K2V RPC handler..."); diff --git a/src/model/helper/error.rs b/src/model/helper/error.rs index bc483c7d..e2ffdd68 100644 --- a/src/model/helper/error.rs +++ b/src/model/helper/error.rs @@ -1,24 +1,24 @@ +use err_derive::Error; use serde::{Deserialize, Serialize}; -use thiserror::Error; use garage_util::error::Error as GarageError; #[derive(Debug, Error, Serialize, Deserialize)] pub enum Error { - #[error("Internal error: {0}")] - Internal(#[from] GarageError), + #[error(display = "Internal error: {}", _0)] + Internal(#[error(source)] GarageError), - #[error("Bad request: {0}")] + #[error(display = "Bad request: {}", _0)] BadRequest(String), /// Bucket name is not valid according to AWS S3 specs - #[error("Invalid bucket name: {0}")] + #[error(display = "Invalid bucket name: {}", _0)] InvalidBucketName(String), - #[error("Access key not found: {0}")] + #[error(display = "Access key not found: {}", _0)] NoSuchAccessKey(String), - #[error("Bucket not found: {0}")] + #[error(display = "Bucket not found: {}", _0)] NoSuchBucket(String), } diff --git a/src/model/helper/locked.rs b/src/model/helper/locked.rs index 98344b63..482e91b0 100644 --- a/src/model/helper/locked.rs +++ b/src/model/helper/locked.rs @@ -1,7 +1,3 @@ -use std::collections::{HashMap, HashSet}; - -use garage_db as db; - use garage_util::crdt::*; use garage_util::data::*; use garage_util::error::{Error as GarageError, OkOrMessage}; @@ -51,10 +47,6 @@ impl<'a> LockedHelper<'a> { KeyHelper(self.0) } - // ================================================ - // global bucket aliases - // ================================================ - /// Sets a new alias for a bucket in global namespace. /// This function fails if: /// - alias name is not valid according to S3 spec @@ -65,7 +57,7 @@ impl<'a> LockedHelper<'a> { bucket_id: Uuid, alias_name: &String, ) -> Result<(), Error> { - if !is_valid_bucket_name(alias_name, self.0.config.allow_punycode) { + if !is_valid_bucket_name(alias_name) { return Err(Error::InvalidBucketName(alias_name.to_string())); } @@ -96,7 +88,8 @@ impl<'a> LockedHelper<'a> { // writes are now done and all writes use timestamp alias_ts let alias = match alias { - None => BucketAlias::new(alias_name.clone(), alias_ts, Some(bucket_id)), + None => BucketAlias::new(alias_name.clone(), alias_ts, Some(bucket_id)) + .ok_or_else(|| Error::InvalidBucketName(alias_name.clone()))?, Some(mut a) => { a.state = Lww::raw(alias_ts, Some(bucket_id)); a @@ -187,14 +180,13 @@ impl<'a> LockedHelper<'a> { .ok_or_else(|| Error::NoSuchBucket(alias_name.to_string()))?; // Checks ok, remove alias - let alias_ts = increment_logical_clock_2( - alias.state.timestamp(), - bucket - .state - .as_option() - .map(|p| p.aliases.get_timestamp(alias_name)) - .unwrap_or(0), - ); + let alias_ts = match bucket.state.as_option() { + Some(bucket_state) => increment_logical_clock_2( + alias.state.timestamp(), + bucket_state.aliases.get_timestamp(alias_name), + ), + None => increment_logical_clock(alias.state.timestamp()), + }; // ---- timestamp-ensured causality barrier ---- // writes are now done and all writes use timestamp alias_ts @@ -212,10 +204,6 @@ impl<'a> LockedHelper<'a> { Ok(()) } - // ================================================ - // local bucket aliases - // ================================================ - /// Sets a new alias for a bucket in the local namespace of a key. /// This function fails if: /// - alias name is not valid according to S3 spec @@ -228,12 +216,14 @@ impl<'a> LockedHelper<'a> { key_id: &String, alias_name: &String, ) -> Result<(), Error> { - if !is_valid_bucket_name(alias_name, self.0.config.allow_punycode) { + let key_helper = KeyHelper(self.0); + + if !is_valid_bucket_name(alias_name) { return Err(Error::InvalidBucketName(alias_name.to_string())); } let mut bucket = self.bucket().get_existing_bucket(bucket_id).await?; - let mut key = self.key().get_existing_key(key_id).await?; + let mut key = key_helper.get_existing_key(key_id).await?; let key_param = key.state.as_option_mut().unwrap(); @@ -282,13 +272,23 @@ impl<'a> LockedHelper<'a> { key_id: &String, alias_name: &String, ) -> Result<(), Error> { - let mut bucket = self.bucket().get_existing_bucket(bucket_id).await?; - let mut key = self.key().get_existing_key(key_id).await?; + let key_helper = KeyHelper(self.0); + + let mut bucket = self.bucket().get_existing_bucket(bucket_id).await?; + let mut key = key_helper.get_existing_key(key_id).await?; - let key_p = key.state.as_option().unwrap(); let bucket_p = bucket.state.as_option_mut().unwrap(); - if key_p.local_aliases.get(alias_name).cloned().flatten() != Some(bucket_id) { + if key + .state + .as_option() + .unwrap() + .local_aliases + .get(alias_name) + .cloned() + .flatten() + != Some(bucket_id) + { return Err(GarageError::Message(format!( "Bucket {:?} does not have alias {} in namespace of key {}", bucket_id, alias_name, key_id @@ -305,17 +305,17 @@ impl<'a> LockedHelper<'a> { .local_aliases .items() .iter() - .any(|((k, n), _, active)| (*k != key.key_id || n != alias_name) && *active); - + .any(|((k, n), _, active)| *k == key.key_id && n == alias_name && *active); if !has_other_global_aliases && !has_other_local_aliases { return Err(Error::BadRequest(format!("Bucket {} doesn't have other aliases, please delete it instead of just unaliasing.", alias_name))); } // Checks ok, remove alias + let key_param = key.state.as_option_mut().unwrap(); let bucket_p_local_alias_key = (key.key_id.clone(), alias_name.clone()); let alias_ts = increment_logical_clock_2( - key_p.local_aliases.get_timestamp(alias_name), + key_param.local_aliases.get_timestamp(alias_name), bucket_p .local_aliases .get_timestamp(&bucket_p_local_alias_key), @@ -324,8 +324,7 @@ impl<'a> LockedHelper<'a> { // ---- timestamp-ensured causality barrier ---- // writes are now done and all writes use timestamp alias_ts - key.state.as_option_mut().unwrap().local_aliases = - LwwMap::raw_item(alias_name.clone(), alias_ts, None); + key_param.local_aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, None); self.0.key_table.insert(&key).await?; bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, false); @@ -334,68 +333,21 @@ impl<'a> LockedHelper<'a> { Ok(()) } - /// Ensures a bucket does not have a certain local alias. - /// Contrarily to unset_local_bucket_alias, this does not - /// fail on any condition other than: - /// - bucket cannot be found (its fine if it is in deleted state) - /// - key cannot be found (its fine if alias in key points to nothing - /// or to another bucket) - pub async fn purge_local_bucket_alias( - &self, - bucket_id: Uuid, - key_id: &String, - alias_name: &String, - ) -> Result<(), Error> { - let mut bucket = self.bucket().get_internal_bucket(bucket_id).await?; - let mut key = self.key().get_internal_key(key_id).await?; - - let bucket_p_local_alias_key = (key.key_id.clone(), alias_name.clone()); - - let alias_ts = increment_logical_clock_2( - key.state - .as_option() - .map(|p| p.local_aliases.get_timestamp(alias_name)) - .unwrap_or(0), - bucket - .state - .as_option() - .map(|p| p.local_aliases.get_timestamp(&bucket_p_local_alias_key)) - .unwrap_or(0), - ); - - // ---- timestamp-ensured causality barrier ---- - // writes are now done and all writes use timestamp alias_ts - - if let Some(kp) = key.state.as_option_mut() { - kp.local_aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, None); - self.0.key_table.insert(&key).await?; - } - - if let Some(bp) = bucket.state.as_option_mut() { - bp.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, false); - self.0.bucket_table.insert(&bucket).await?; - } - - Ok(()) - } - - // ================================================ - // permissions - // ================================================ - /// Sets permissions for a key on a bucket. /// This function fails if: /// - bucket or key cannot be found at all (its ok if they are in deleted state) - /// - bucket or key is in deleted state and we are trying to set - /// permissions other than "deny all" + /// - bucket or key is in deleted state and we are trying to set permissions other than "deny + /// all" pub async fn set_bucket_key_permissions( &self, bucket_id: Uuid, key_id: &String, mut perm: BucketKeyPerm, ) -> Result<(), Error> { + let key_helper = KeyHelper(self.0); + let mut bucket = self.bucket().get_internal_bucket(bucket_id).await?; - let mut key = self.key().get_internal_key(key_id).await?; + let mut key = key_helper.get_internal_key(key_id).await?; if let Some(bstate) = bucket.state.as_option() { if let Some(kp) = bstate.authorized_keys.get(key_id) { @@ -432,20 +384,21 @@ impl<'a> LockedHelper<'a> { Ok(()) } - // ================================================ - // keys - // ================================================ + // ---- /// Deletes an API access key pub async fn delete_key(&self, key: &mut Key) -> Result<(), Error> { let state = key.state.as_option_mut().unwrap(); // --- done checking, now commit --- + // (the step at unset_local_bucket_alias will fail if a bucket + // does not have another alias, the deletion will be + // interrupted in the middle if that happens) // 1. Delete local aliases for (alias, _, to) in state.local_aliases.items().iter() { if let Some(bucket_id) = to { - self.purge_local_bucket_alias(*bucket_id, &key.key_id, alias) + self.unset_local_bucket_alias(*bucket_id, &key.key_id, alias) .await?; } } @@ -462,193 +415,4 @@ impl<'a> LockedHelper<'a> { Ok(()) } - - // ================================================ - // repair procedure - // ================================================ - - pub async fn repair_aliases(&self) -> Result<(), GarageError> { - self.0.db.transaction(|tx| { - info!("--- begin repair_aliases transaction ----"); - - // 1. List all non-deleted buckets, so that we can fix bad aliases - let mut all_buckets: HashSet = HashSet::new(); - - for item in tx.range::<&[u8], _>(&self.0.bucket_table.data.store, ..)? { - let bucket = self - .0 - .bucket_table - .data - .decode_entry(&(item?.1)) - .map_err(db::TxError::Abort)?; - if !bucket.is_deleted() { - all_buckets.insert(bucket.id); - } - } - - info!("number of buckets: {}", all_buckets.len()); - - // 2. List all aliases declared in bucket_alias_table and key_table - // Take note of aliases that point to non-existing buckets - let mut global_aliases: HashMap = HashMap::new(); - - { - let mut delete_global = vec![]; - for item in tx.range::<&[u8], _>(&self.0.bucket_alias_table.data.store, ..)? { - let mut alias = self - .0 - .bucket_alias_table - .data - .decode_entry(&(item?.1)) - .map_err(db::TxError::Abort)?; - if let Some(id) = alias.state.get() { - if all_buckets.contains(id) { - // keep aliases - global_aliases.insert(alias.name().to_string(), *id); - } else { - // delete alias - warn!( - "global alias: remove {} -> {:?} (bucket is deleted)", - alias.name(), - id - ); - alias.state.update(None); - delete_global.push(alias); - } - } - } - - info!("number of global aliases: {}", global_aliases.len()); - - info!("global alias table: {} entries fixed", delete_global.len()); - for ga in delete_global { - debug!("Enqueue update to global alias table: {:?}", ga); - self.0.bucket_alias_table.queue_insert(tx, &ga)?; - } - } - - let mut local_aliases: HashMap<(String, String), Uuid> = HashMap::new(); - - { - let mut delete_local = vec![]; - - for item in tx.range::<&[u8], _>(&self.0.key_table.data.store, ..)? { - let mut key = self - .0 - .key_table - .data - .decode_entry(&(item?.1)) - .map_err(db::TxError::Abort)?; - let Some(p) = key.state.as_option_mut() else { - continue; - }; - let mut has_changes = false; - for (name, _, to) in p.local_aliases.items().to_vec() { - if let Some(id) = to { - if all_buckets.contains(&id) { - local_aliases.insert((key.key_id.clone(), name), id); - } else { - warn!( - "local alias: remove ({}, {}) -> {:?} (bucket is deleted)", - key.key_id, name, id - ); - p.local_aliases.update_in_place(name, None); - has_changes = true; - } - } - } - if has_changes { - delete_local.push(key); - } - } - - info!("number of local aliases: {}", local_aliases.len()); - - info!("key table: {} entries fixed", delete_local.len()); - for la in delete_local { - debug!("Enqueue update to key table: {:?}", la); - self.0.key_table.queue_insert(tx, &la)?; - } - } - - // 4. Reverse the alias maps to determine the aliases per-bucket - let mut bucket_global: HashMap> = HashMap::new(); - let mut bucket_local: HashMap> = HashMap::new(); - - for (name, bucket) in global_aliases { - bucket_global.entry(bucket).or_default().push(name); - } - for ((key, name), bucket) in local_aliases { - bucket_local.entry(bucket).or_default().push((key, name)); - } - - // 5. Fix the bucket table to ensure consistency - let mut bucket_updates = vec![]; - - for item in tx.range::<&[u8], _>(&self.0.bucket_table.data.store, ..)? { - let bucket = self - .0 - .bucket_table - .data - .decode_entry(&(item?.1)) - .map_err(db::TxError::Abort)?; - let mut bucket2 = bucket.clone(); - let Some(param) = bucket2.state.as_option_mut() else { - continue; - }; - - // fix global aliases - { - let ga = bucket_global.remove(&bucket.id).unwrap_or_default(); - for (name, _, active) in param.aliases.items().to_vec() { - if active && !ga.contains(&name) { - warn!("bucket {:?}: remove global alias {}", bucket.id, name); - param.aliases.update_in_place(name, false); - } - } - for name in ga { - if param.aliases.get(&name).copied() != Some(true) { - warn!("bucket {:?}: add global alias {}", bucket.id, name); - param.aliases.update_in_place(name, true); - } - } - } - - // fix local aliases - { - let la = bucket_local.remove(&bucket.id).unwrap_or_default(); - for (pair, _, active) in param.local_aliases.items().to_vec() { - if active && !la.contains(&pair) { - warn!("bucket {:?}: remove local alias {:?}", bucket.id, pair); - param.local_aliases.update_in_place(pair, false); - } - } - for pair in la { - if param.local_aliases.get(&pair).copied() != Some(true) { - warn!("bucket {:?}: add local alias {:?}", bucket.id, pair); - param.local_aliases.update_in_place(pair, true); - } - } - } - - if bucket2 != bucket { - bucket_updates.push(bucket2); - } - } - - info!("bucket table: {} entries fixed", bucket_updates.len()); - for b in bucket_updates { - debug!("Enqueue update to bucket table: {:?}", b); - self.0.bucket_table.queue_insert(tx, &b)?; - } - - info!("--- end repair_aliases transaction ----"); - - Ok(()) - })?; - - info!("repair_aliases is done"); - - Ok(()) - } } diff --git a/src/model/index_counter.rs b/src/model/index_counter.rs index aa13ee7b..e4f79753 100644 --- a/src/model/index_counter.rs +++ b/src/model/index_counter.rs @@ -10,6 +10,7 @@ use garage_db as db; use garage_rpc::layout::LayoutHelper; use garage_rpc::system::System; use garage_util::background::BackgroundRunner; +use garage_util::config::Config; use garage_util::data::*; use garage_util::error::*; use garage_util::migrate::Migrate; @@ -173,6 +174,7 @@ impl IndexCounter { system: Arc, replication: TableShardedReplication, db: &db::Db, + config: &Config, ) -> Arc { Arc::new(Self { this_node: system.id, @@ -186,6 +188,7 @@ impl IndexCounter { replication, system, db, + &config.experimental.merkle_backpressure, ), }) } diff --git a/src/model/s3/lifecycle_worker.rs b/src/model/s3/lifecycle_worker.rs index af00437e..bb10ba48 100644 --- a/src/model/s3/lifecycle_worker.rs +++ b/src/model/s3/lifecycle_worker.rs @@ -121,13 +121,13 @@ impl Worker for LifecycleWorker { mpu_aborted, .. } => { - let n_objects = self.garage.object_table.data.store.approximate_len().ok(); + let n_objects = self.garage.object_table.data.store.len().ok(); let progress = match n_objects { - Some(total) if total > 0 => format!( + None => "...".to_string(), + Some(total) => format!( "~{:.2}%", 100. * std::cmp::min(*counter, total) as f32 / total as f32 ), - _ => "...".to_string(), }; WorkerStatus { progress: Some(progress), diff --git a/src/net/Cargo.toml b/src/net/Cargo.toml index 71f42c68..3a8986a3 100644 --- a/src/net/Cargo.toml +++ b/src/net/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_net" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -30,7 +30,7 @@ rand.workspace = true log.workspace = true arc-swap.workspace = true -thiserror.workspace = true +err-derive.workspace = true bytes.workspace = true cfg-if.workspace = true @@ -39,6 +39,7 @@ kuska-handshake.workspace = true opentelemetry = { workspace = true, optional = true } opentelemetry-contrib = { workspace = true, optional = true } +tracing.workspace = true [dev-dependencies] pretty_env_logger.workspace = true diff --git a/src/net/client.rs b/src/net/client.rs index 20e1dacd..e0c09cf5 100644 --- a/src/net/client.rs +++ b/src/net/client.rs @@ -4,6 +4,7 @@ use std::pin::Pin; use std::sync::atomic::{self, AtomicU32}; use std::sync::{Arc, Mutex}; use std::task::Poll; +use tracing::*; use arc_swap::ArcSwapOption; use bytes::Bytes; @@ -14,7 +15,7 @@ use futures::Stream; use kuska_handshake::async_std::{handshake_client, BoxStream}; use tokio::net::TcpStream; use tokio::select; -use tokio::sync::{mpsc, oneshot, watch}; +use tokio::sync::{mpsc, oneshot, watch, Semaphore}; use tokio_util::compat::*; #[cfg(feature = "telemetry")] @@ -25,6 +26,7 @@ use opentelemetry::{ #[cfg(feature = "telemetry")] use opentelemetry_contrib::trace::propagator::binary::*; +use crate::endpoint::RpcInFlightLimiter; use crate::error::*; use crate::message::*; use crate::netapp::*; @@ -41,6 +43,7 @@ pub(crate) struct ClientConn { next_query_number: AtomicU32, inflight: Mutex>>, + rpc_table_write_inflight_limiter: Option, } impl ClientConn { @@ -98,8 +101,14 @@ impl ClientConn { next_query_number: AtomicU32::from(RequestID::default()), query_send: ArcSwapOption::new(Some(Arc::new(query_send))), inflight: Mutex::new(HashMap::new()), + rpc_table_write_inflight_limiter: netapp.max_in_flight_table_write.map(Semaphore::new), }); + info!( + "Created conn with table write limit set to {}", + netapp.max_in_flight_table_write.unwrap_or(0) + ); + netapp.connected_as_client(peer_id, conn.clone()); let debug_name = format!("CLI {}", hex::encode(&peer_id[..8])); @@ -144,10 +153,21 @@ impl ClientConn { req: Req, path: &str, prio: RequestPriority, + limiter: RpcInFlightLimiter, ) -> Result, Error> where T: Message, { + let _permit = match (limiter, &self.rpc_table_write_inflight_limiter) { + (RpcInFlightLimiter::TableWrite, Some(sem)) => { + info!( + "Available RPC table write slots: {}", + sem.available_permits() + ); + Some(sem.acquire().await.unwrap()) + } + _ => None, + }; let query_send = self.query_send.load_full().ok_or(Error::ConnectionClosed)?; let id = self @@ -212,6 +232,7 @@ impl ClientConn { let stream = Box::pin(canceller.for_stream(stream)); let resp_enc = RespEnc::decode(stream).await?; + drop(_permit); debug!("client: got response to request {} (path {})", id, path); Resp::from_enc(resp_enc) } diff --git a/src/net/endpoint.rs b/src/net/endpoint.rs index 3ab1048a..a4dee42f 100644 --- a/src/net/endpoint.rs +++ b/src/net/endpoint.rs @@ -57,6 +57,13 @@ where } } +#[derive(Debug, Copy, Clone, Default)] +pub enum RpcInFlightLimiter { + #[default] + NoLimit, + TableWrite, +} + // ---- /// This struct represents an endpoint for message of type `M`. @@ -114,6 +121,7 @@ where target: &NodeID, req: T, prio: RequestPriority, + limiter: RpcInFlightLimiter, ) -> Result, Error> where T: IntoReq, @@ -136,7 +144,10 @@ where "Not connected: {}", hex::encode(&target[..8]) ))), - Some(c) => c.call(req.into_req()?, self.path.as_str(), prio).await, + Some(c) => { + c.call(req.into_req()?, self.path.as_str(), prio, limiter) + .await + } } } } @@ -149,8 +160,12 @@ where target: &NodeID, req: M, prio: RequestPriority, + limiter: RpcInFlightLimiter, ) -> Result<::Response, Error> { - Ok(self.call_streaming(target, req, prio).await?.into_msg()) + Ok(self + .call_streaming(target, req, prio, limiter) + .await? + .into_msg()) } } @@ -159,7 +174,7 @@ where pub(crate) type DynEndpoint = Box; pub(crate) trait GenericEndpoint { - fn handle(&self, req_enc: ReqEnc, from: NodeID) -> BoxFuture<'_, Result>; + fn handle(&self, req_enc: ReqEnc, from: NodeID) -> BoxFuture>; fn drop_handler(&self); fn clone_endpoint(&self) -> DynEndpoint; } @@ -175,7 +190,7 @@ where M: Message, H: StreamingEndpointHandler + 'static, { - fn handle(&self, req_enc: ReqEnc, from: NodeID) -> BoxFuture<'_, Result> { + fn handle(&self, req_enc: ReqEnc, from: NodeID) -> BoxFuture> { async move { match self.0.handler.load_full() { None => Err(Error::NoHandler), diff --git a/src/net/error.rs b/src/net/error.rs index f67794ed..cddb1eaa 100644 --- a/src/net/error.rs +++ b/src/net/error.rs @@ -1,49 +1,49 @@ use std::io; +use err_derive::Error; use log::error; -use thiserror::Error; #[derive(Debug, Error)] pub enum Error { - #[error("IO error: {0}")] - Io(#[from] io::Error), + #[error(display = "IO error: {}", _0)] + Io(#[error(source)] io::Error), - #[error("Messagepack encode error: {0}")] - RMPEncode(#[from] rmp_serde::encode::Error), - #[error("Messagepack decode error: {0}")] - RMPDecode(#[from] rmp_serde::decode::Error), + #[error(display = "Messagepack encode error: {}", _0)] + RMPEncode(#[error(source)] rmp_serde::encode::Error), + #[error(display = "Messagepack decode error: {}", _0)] + RMPDecode(#[error(source)] rmp_serde::decode::Error), - #[error("Tokio join error: {0}")] - TokioJoin(#[from] tokio::task::JoinError), + #[error(display = "Tokio join error: {}", _0)] + TokioJoin(#[error(source)] tokio::task::JoinError), - #[error("oneshot receive error: {0}")] - OneshotRecv(#[from] tokio::sync::oneshot::error::RecvError), + #[error(display = "oneshot receive error: {}", _0)] + OneshotRecv(#[error(source)] tokio::sync::oneshot::error::RecvError), - #[error("Handshake error: {0}")] - Handshake(#[from] kuska_handshake::async_std::Error), + #[error(display = "Handshake error: {}", _0)] + Handshake(#[error(source)] kuska_handshake::async_std::Error), - #[error("UTF8 error: {0}")] - UTF8(#[from] std::string::FromUtf8Error), + #[error(display = "UTF8 error: {}", _0)] + UTF8(#[error(source)] std::string::FromUtf8Error), - #[error("Framing protocol error")] + #[error(display = "Framing protocol error")] Framing, - #[error("Remote error ({0:?}): {1}")] + #[error(display = "Remote error ({:?}): {}", _0, _1)] Remote(io::ErrorKind, String), - #[error("Request ID collision")] + #[error(display = "Request ID collision")] IdCollision, - #[error("{0}")] + #[error(display = "{}", _0)] Message(String), - #[error("No handler / shutting down")] + #[error(display = "No handler / shutting down")] NoHandler, - #[error("Connection closed")] + #[error(display = "Connection closed")] ConnectionClosed, - #[error("Version mismatch: {0}")] + #[error(display = "Version mismatch: {}", _0)] VersionMismatch(String), } diff --git a/src/net/netapp.rs b/src/net/netapp.rs index 36c6fc88..928951fd 100644 --- a/src/net/netapp.rs +++ b/src/net/netapp.rs @@ -74,6 +74,8 @@ pub struct NetApp { pub id: NodeID, /// Private key associated with our peer ID pub privkey: ed25519::SecretKey, + /// Config related to netapp + pub(crate) max_in_flight_table_write: Option, pub(crate) server_conns: RwLock>>, pub(crate) client_conns: RwLock>>, @@ -101,6 +103,7 @@ impl NetApp { netid: auth::Key, privkey: ed25519::SecretKey, bind_outgoing_to: Option, + max_in_flight_table_write: Option, ) -> Arc { let mut version_tag = [0u8; 16]; version_tag[0..8].copy_from_slice(&u64::to_be_bytes(NETAPP_VERSION_TAG)[..]); @@ -114,6 +117,7 @@ impl NetApp { netid, id, privkey, + max_in_flight_table_write, server_conns: RwLock::new(HashMap::new()), client_conns: RwLock::new(HashMap::new()), endpoints: RwLock::new(HashMap::new()), @@ -427,6 +431,7 @@ impl NetApp { server_port, }, PRIO_NORMAL, + RpcInFlightLimiter::NoLimit, ) .await .map(|_| ()) diff --git a/src/net/peering.rs b/src/net/peering.rs index 08378a08..bca0e07f 100644 --- a/src/net/peering.rs +++ b/src/net/peering.rs @@ -406,7 +406,7 @@ impl PeeringManager { ping_time ); let ping_response = select! { - r = self.ping_endpoint.call(&id, ping_msg, PRIO_HIGH) => r, + r = self.ping_endpoint.call(&id, ping_msg, PRIO_HIGH, RpcInFlightLimiter::NoLimit) => r, _ = tokio::time::sleep(ping_timeout) => Err(Error::Message("Ping timeout".into())), }; @@ -458,7 +458,12 @@ impl PeeringManager { let pex_message = PeerListMessage { list: peer_list }; match self .peer_list_endpoint - .call(id, pex_message, PRIO_BACKGROUND) + .call( + id, + pex_message, + PRIO_BACKGROUND, + RpcInFlightLimiter::NoLimit, + ) .await { Err(e) => warn!("Error doing peer exchange: {}", e), diff --git a/src/rpc/Cargo.toml b/src/rpc/Cargo.toml index e23f4bca..e6466001 100644 --- a/src/rpc/Cargo.toml +++ b/src/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_rpc" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -33,7 +33,7 @@ async-trait.workspace = true serde.workspace = true serde_bytes.workspace = true serde_json.workspace = true -thiserror = { workspace = true, optional = true } +err-derive = { workspace = true, optional = true } # newer version requires rust edition 2021 kube = { workspace = true, optional = true } @@ -49,5 +49,5 @@ opentelemetry.workspace = true [features] kubernetes-discovery = [ "kube", "k8s-openapi", "schemars" ] -consul-discovery = [ "reqwest", "thiserror" ] +consul-discovery = [ "reqwest", "err-derive" ] system-libs = [ "sodiumoxide/use-pkg-config" ] diff --git a/src/rpc/consul.rs b/src/rpc/consul.rs index 760e9fcb..f088bf3f 100644 --- a/src/rpc/consul.rs +++ b/src/rpc/consul.rs @@ -3,8 +3,8 @@ use std::fs::File; use std::io::Read; use std::net::{IpAddr, SocketAddr}; +use err_derive::Error; use serde::{Deserialize, Serialize}; -use thiserror::Error; use garage_net::NodeID; @@ -219,12 +219,12 @@ impl ConsulDiscovery { /// Regroup all Consul discovery errors #[derive(Debug, Error)] pub enum ConsulError { - #[error("IO error: {0}")] - Io(#[from] std::io::Error), - #[error("HTTP error: {0}")] - Reqwest(#[from] reqwest::Error), - #[error("Invalid Consul TLS configuration")] + #[error(display = "IO error: {}", _0)] + Io(#[error(source)] std::io::Error), + #[error(display = "HTTP error: {}", _0)] + Reqwest(#[error(source)] reqwest::Error), + #[error(display = "Invalid Consul TLS configuration")] InvalidTLSConfig, - #[error("Token error: {0}")] - Token(#[from] reqwest::header::InvalidHeaderValue), + #[error(display = "Token error: {}", _0)] + Token(#[error(source)] reqwest::header::InvalidHeaderValue), } diff --git a/src/rpc/layout/manager.rs b/src/rpc/layout/manager.rs index bb8000bd..21907ec7 100644 --- a/src/rpc/layout/manager.rs +++ b/src/rpc/layout/manager.rs @@ -229,11 +229,13 @@ impl LayoutManager { } /// Save cluster layout data to disk - async fn save_cluster_layout(&self) { + async fn save_cluster_layout(&self) -> Result<(), Error> { let layout = self.layout.read().unwrap().inner().clone(); - if let Err(e) = self.persist_cluster_layout.save_async(&layout).await { - error!("Failed to save cluster_layout: {}", e); - } + self.persist_cluster_layout + .save_async(&layout) + .await + .expect("Cannot save current cluster layout"); + Ok(()) } fn broadcast_update(self: &Arc, rpc: SystemRpc) { @@ -311,7 +313,7 @@ impl LayoutManager { self.change_notify.notify_waiters(); self.broadcast_update(SystemRpc::AdvertiseClusterLayout(new_layout)); - self.save_cluster_layout().await; + self.save_cluster_layout().await?; } Ok(SystemRpc::Ok) @@ -326,7 +328,7 @@ impl LayoutManager { if let Some(new_trackers) = self.merge_layout_trackers(trackers) { self.change_notify.notify_waiters(); self.broadcast_update(SystemRpc::AdvertiseClusterLayoutTrackers(new_trackers)); - self.save_cluster_layout().await; + self.save_cluster_layout().await?; } Ok(SystemRpc::Ok) diff --git a/src/rpc/layout/version.rs b/src/rpc/layout/version.rs index a02fce89..b7902898 100644 --- a/src/rpc/layout/version.rs +++ b/src/rpc/layout/version.rs @@ -507,7 +507,7 @@ impl LayoutVersion { g.compute_maximal_flow()?; if g.get_flow_value()? < (NB_PARTITIONS * self.replication_factor) as i64 { return Err(Error::Message( - "The storage capacity of the cluster is too small. It is \ + "The storage capacity of he cluster is to small. It is \ impossible to store partitions of size 1." .into(), )); diff --git a/src/rpc/rpc_helper.rs b/src/rpc/rpc_helper.rs index 2505c2ce..62efe15d 100644 --- a/src/rpc/rpc_helper.rs +++ b/src/rpc/rpc_helper.rs @@ -6,6 +6,7 @@ use std::time::Duration; use futures::future::join_all; use futures::stream::futures_unordered::FuturesUnordered; use futures::stream::StreamExt; +use garage_net::endpoint::RpcInFlightLimiter; use tokio::select; use opentelemetry::KeyValue; @@ -44,6 +45,8 @@ pub struct RequestStrategy { rs_timeout: Timeout, /// Data to drop when everything completes rs_drop_on_complete: T, + /// RPC In Flight Limiter + rs_inflight_limiter: RpcInFlightLimiter, } #[derive(Copy, Clone)] @@ -61,6 +64,7 @@ impl Clone for RequestStrategy<()> { rs_priority: self.rs_priority, rs_timeout: self.rs_timeout, rs_drop_on_complete: (), + rs_inflight_limiter: self.rs_inflight_limiter, } } } @@ -74,6 +78,7 @@ impl RequestStrategy<()> { rs_priority: prio, rs_timeout: Timeout::Default, rs_drop_on_complete: (), + rs_inflight_limiter: RpcInFlightLimiter::NoLimit, } } /// Add an item to be dropped on completion @@ -84,6 +89,7 @@ impl RequestStrategy<()> { rs_priority: self.rs_priority, rs_timeout: self.rs_timeout, rs_drop_on_complete: drop_on_complete, + rs_inflight_limiter: RpcInFlightLimiter::NoLimit, } } } @@ -109,6 +115,10 @@ impl RequestStrategy { self.rs_timeout = Timeout::Custom(timeout); self } + pub fn with_write_limiter(mut self) -> Self { + self.rs_inflight_limiter = RpcInFlightLimiter::TableWrite; + self + } /// Extract drop_on_complete item fn extract_drop_on_complete(self) -> (RequestStrategy<()>, T) { ( @@ -118,6 +128,7 @@ impl RequestStrategy { rs_priority: self.rs_priority, rs_timeout: self.rs_timeout, rs_drop_on_complete: (), + rs_inflight_limiter: self.rs_inflight_limiter, }, self.rs_drop_on_complete, ) @@ -185,7 +196,7 @@ impl RpcHelper { let node_id = to.into(); let rpc_call = endpoint - .call_streaming(&node_id, msg, strat.rs_priority) + .call_streaming(&node_id, msg, strat.rs_priority, strat.rs_inflight_limiter) .with_context(Context::current_with_span(span)) .record_duration(&self.0.metrics.rpc_duration, &metric_tags); diff --git a/src/rpc/system.rs b/src/rpc/system.rs index 2a52ae5d..6d6af809 100644 --- a/src/rpc/system.rs +++ b/src/rpc/system.rs @@ -21,7 +21,7 @@ use garage_net::{NetApp, NetworkKey, NodeID, NodeKey}; #[cfg(feature = "kubernetes-discovery")] use garage_util::config::KubernetesDiscoveryConfig; -use garage_util::config::{Config, DataDirEnum}; +use garage_util::config::{Config, DataDirEnum, RpcInFlightLimiterEnum}; use garage_util::data::*; use garage_util::error::*; use garage_util::persister::Persister; @@ -256,7 +256,17 @@ impl System { let bind_outgoing_to = Some(config) .filter(|x| x.rpc_bind_outgoing) .map(|x| x.rpc_bind_addr.ip()); - let netapp = NetApp::new(GARAGE_VERSION_TAG, network_key, node_key, bind_outgoing_to); + let maybe_max_table_write = match &config.experimental.rpc_in_flight_limiters { + RpcInFlightLimiterEnum::None => None, + RpcInFlightLimiterEnum::FixedSize(v) => Some(v.max_table_write), + }; + let netapp = NetApp::new( + GARAGE_VERSION_TAG, + network_key, + node_key, + bind_outgoing_to, + maybe_max_table_write, + ); let system_endpoint = netapp.endpoint(SYSTEM_RPC_PATH.into()); // ---- setup netapp public listener and full mesh peering strategy ---- diff --git a/src/table/Cargo.toml b/src/table/Cargo.toml index 478dbd18..ef7b44e4 100644 --- a/src/table/Cargo.toml +++ b/src/table/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_table" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" diff --git a/src/table/data.rs b/src/table/data.rs index 1d0308ce..af001748 100644 --- a/src/table/data.rs +++ b/src/table/data.rs @@ -3,10 +3,12 @@ use std::convert::TryInto; use std::sync::Arc; use serde_bytes::ByteBuf; -use tokio::sync::Notify; +use tokio::sync::SemaphorePermit; +use tokio::sync::{Notify, Semaphore}; use garage_db as db; +use garage_util::config::MerkleBackpressureEnum; use garage_util::data::*; use garage_util::error::*; use garage_util::migrate::Migrate; @@ -20,6 +22,67 @@ use crate::replication::*; use crate::schema::*; use crate::util::*; +pub(crate) struct MerkleTodo { + merkle_todo: db::Tree, + merkle_todo_notify: Notify, + merkle_todo_bounded_queue: Option>, +} +impl Clone for MerkleTodo { + fn clone(&self) -> Self { + Self { + merkle_todo: self.merkle_todo.clone(), + merkle_todo_notify: Notify::new(), + merkle_todo_bounded_queue: self.merkle_todo_bounded_queue.clone(), + } + } +} +impl MerkleTodo { + fn new(db: &db::Db, config: &MerkleBackpressureEnum) -> Self { + let merkle_todo = db + .open_tree(format!("{}:merkle_todo", F::TABLE_NAME)) + .expect("Unable to open DB Merkle TODO tree"); + + let merkle_todo_bounded_queue = match config { + MerkleBackpressureEnum::None => None, + MerkleBackpressureEnum::FixedQueue(p) => { + Some(Arc::new(Semaphore::new(p.max_queue_size))) + } + }; + + Self { + merkle_todo, + merkle_todo_notify: Notify::new(), + merkle_todo_bounded_queue, + } + } + + pub(crate) fn len(&self) -> Result { + self.merkle_todo.len() + } + + pub(crate) async fn with_db(&self, f: F) { + let bounded = self + .merkle_todo_bounded_queue + .clone() + .unwrap_or(Arc::new(Semaphore::new(1))); + let permit = bounded.acquire().await.unwrap(); + f(&self.merkle_todo, permit); + } + + pub(crate) fn appended(&self, permit: SemaphorePermit) { + permit.forget(); + self.merkle_todo_notify.notify_one(); + } + + pub(crate) fn processed(&self) { + let bounded = self + .merkle_todo_bounded_queue + .clone() + .unwrap_or(Arc::new(Semaphore::new(1))); + bounded.add_permits(1); + } +} + pub struct TableData { system: Arc, @@ -29,8 +92,7 @@ pub struct TableData { pub store: db::Tree, pub(crate) merkle_tree: db::Tree, - pub(crate) merkle_todo: db::Tree, - pub(crate) merkle_todo_notify: Notify, + pub(crate) merkle_todo: MerkleTodo, pub(crate) insert_queue: db::Tree, pub(crate) insert_queue_notify: Arc, @@ -38,10 +100,18 @@ pub struct TableData { pub(crate) gc_todo: db::Tree, pub(crate) metrics: TableMetrics, + + pub(crate) config: MerkleBackpressureEnum, } impl TableData { - pub fn new(system: Arc, instance: F, replication: R, db: &db::Db) -> Arc { + pub fn new( + system: Arc, + instance: F, + replication: R, + db: &db::Db, + config: &MerkleBackpressureEnum, + ) -> Arc { let store = db .open_tree(format!("{}:table", F::TABLE_NAME)) .expect("Unable to open DB tree"); @@ -49,9 +119,8 @@ impl TableData { let merkle_tree = db .open_tree(format!("{}:merkle_tree", F::TABLE_NAME)) .expect("Unable to open DB Merkle tree tree"); - let merkle_todo = db - .open_tree(format!("{}:merkle_todo", F::TABLE_NAME)) - .expect("Unable to open DB Merkle TODO tree"); + + let merkle_todo = MerkleTodo::new::(db, config); let insert_queue = db .open_tree(format!("{}:insert_queue", F::TABLE_NAME)) @@ -76,11 +145,11 @@ impl TableData { store, merkle_tree, merkle_todo, - merkle_todo_notify: Notify::new(), insert_queue, insert_queue_notify: Arc::new(Notify::new()), gc_todo, metrics, + config: config.clone(), }) } @@ -167,6 +236,8 @@ impl TableData { // - When an entry is modified or deleted, add it to the merkle updater's todo list. // This has to be done atomically with the modification for the merkle updater // to maintain consistency. The merkle updater must then be notified with todo_notify. + // Also to avoid overloading the merkle updater, you need to sleep a given amount of + // time to enable backpressure (ie. slow down clients). // - When an entry is updated to be a tombstone, add it to the gc_todo tree pub(crate) fn update_many>(&self, entries: &[T]) -> Result<(), Error> { @@ -201,6 +272,7 @@ impl TableData { ) -> Result, Error> { let tree_key = self.tree_key(partition_key, sort_key); + // transaction begins let changed = self.store.db().transaction(|tx| { let (old_entry, old_bytes, new_entry) = match tx.get(&self.store, &tree_key)? { Some(old_bytes) => { @@ -238,31 +310,44 @@ impl TableData { Ok(None) } })?; + // transaction ends - if let Some((new_entry, new_bytes_hash)) = changed { - self.metrics.internal_update_counter.add(1); - - let is_tombstone = new_entry.is_tombstone(); - self.merkle_todo_notify.notify_one(); - if is_tombstone { - // We are only responsible for GC'ing this item if we are the - // "leader" of the partition, i.e. the first node in the - // set of nodes that replicates this partition. - // This avoids GC loops and does not change the termination properties - // of the GC algorithm, as in all cases GC is suspended if - // any node of the partition is unavailable. - let pk_hash = Hash::try_from(&tree_key[..32]).unwrap(); - // TODO: this probably breaks when the layout changes - let nodes = self.replication.storage_nodes(&pk_hash); - if nodes.first() == Some(&self.system.id) { - GcTodoEntry::new(tree_key, new_bytes_hash).save(&self.gc_todo)?; + // early return if nothing changed + let (new_entry, new_bytes_hash) = match changed { + Some((e, b)) => (e, b), + None => { + let maybe_bound = self.merkle_todo_bounded_queue.clone(); + if let Some(b) = &maybe_bound { + b.add_permits(1); } + return Ok(None); } + }; - Ok(Some(new_entry)) - } else { - Ok(None) + // Handle GC in case of tombstone + let is_tombstone = new_entry.is_tombstone(); + if is_tombstone { + // We are only responsible for GC'ing this item if we are the + // "leader" of the partition, i.e. the first node in the + // set of nodes that replicates this partition. + // This avoids GC loops and does not change the termination properties + // of the GC algorithm, as in all cases GC is suspended if + // any node of the partition is unavailable. + let pk_hash = Hash::try_from(&tree_key[..32]).unwrap(); + // TODO: this probably breaks when the layout changes + let nodes = self.replication.storage_nodes(&pk_hash); + if nodes.first() == Some(&self.system.id) { + GcTodoEntry::new(tree_key, new_bytes_hash).save(&self.gc_todo)?; + } } + + // Collect metrics + self.metrics.internal_update_counter.add(1); + + // Synchronize with the Merkle Worker + self.merkle_todo_notify.notify_one(); // Wake-up it + + Ok(Some(new_entry)) } pub(crate) fn delete_if_equal(self: &Arc, k: &[u8], v: &[u8]) -> Result { @@ -282,10 +367,16 @@ impl TableData { _ => Ok(false), })?; - if removed { - self.metrics.internal_delete_counter.add(1); - self.merkle_todo_notify.notify_one(); + if !removed { + let maybe_bound = self.merkle_todo_bounded_queue.clone(); + if let Some(b) = &maybe_bound { + b.add_permits(1); + } + return Ok(false); } + + self.metrics.internal_delete_counter.add(1); + self.merkle_todo_notify.notify_one(); Ok(removed) } @@ -310,11 +401,18 @@ impl TableData { _ => Ok(false), })?; - if removed { - self.metrics.internal_delete_counter.add(1); - self.merkle_todo_notify.notify_one(); + if !removed { + let maybe_bound = self.merkle_todo_bounded_queue.clone(); + if let Some(b) = &maybe_bound { + b.add_permits(1); + } + return Ok(false); } - Ok(removed) + + self.metrics.internal_delete_counter.add(1); + self.merkle_todo_notify.notify_one(); + + Ok(true) } // ---- Insert queue functions ---- @@ -367,7 +465,7 @@ impl TableData { } } - pub fn gc_todo_approximate_len(&self) -> Result { - Ok(self.gc_todo.approximate_len()?) + pub fn gc_todo_len(&self) -> Result { + Ok(self.gc_todo.len()?) } } diff --git a/src/table/gc.rs b/src/table/gc.rs index 1f30bd76..2154d93c 100644 --- a/src/table/gc.rs +++ b/src/table/gc.rs @@ -262,7 +262,8 @@ impl TableGc { // GC has been successful for all of these entries. // We now remove them all from our local table and from the GC todo list. for item in items { - self.data + let _is_removed = self + .data .delete_if_equal_hash(&item.key[..], item.value_hash) .err_context("GC: local delete tombstones")?; item.remove_if_equal(&self.data.gc_todo) @@ -275,14 +276,21 @@ impl TableGc { impl EndpointHandler for TableGc { async fn handle(self: &Arc, message: &GcRpc, _from: NodeID) -> Result { + let maybe_bounded = self.data.merkle_todo_bounded_queue.clone(); match message { GcRpc::Update(items) => { + if let Some(b) = maybe_bounded { + b.acquire_many(items.len() as u32).await.unwrap().forget(); + } self.data.update_many(items)?; Ok(GcRpc::Ok) } GcRpc::DeleteIfEqualHash(items) => { + if let Some(b) = maybe_bounded { + b.acquire_many(items.len() as u32).await.unwrap().forget(); + } for (key, vhash) in items.iter() { - self.data.delete_if_equal_hash(&key[..], *vhash)?; + let _is_removed = self.data.delete_if_equal_hash(&key[..], *vhash)?; } Ok(GcRpc::Ok) } @@ -313,7 +321,7 @@ impl Worker for GcWorker { fn status(&self) -> WorkerStatus { WorkerStatus { - queue_length: Some(self.gc.data.gc_todo_approximate_len().unwrap_or(0) as u64), + queue_length: Some(self.gc.data.gc_todo_len().unwrap_or(0) as u64), ..Default::default() } } @@ -329,7 +337,6 @@ impl Worker for GcWorker { } async fn wait_for_work(&mut self) -> WorkerState { - tokio::time::sleep(self.wait_delay).await; WorkerState::Busy } } diff --git a/src/table/merkle.rs b/src/table/merkle.rs index 7ba1f007..f1241ed0 100644 --- a/src/table/merkle.rs +++ b/src/table/merkle.rs @@ -9,6 +9,7 @@ use tokio::sync::watch; use garage_db as db; use garage_util::background::*; +use garage_util::config::MerkleBackpressureEnum; use garage_util::data::*; use garage_util::encode::{nonversioned_decode, nonversioned_encode}; use garage_util::error::Error; @@ -70,6 +71,15 @@ impl MerkleUpdater { pub(crate) fn new(data: Arc>) -> Arc { let empty_node_hash = blake2sum(&nonversioned_encode(&MerkleNode::Empty).unwrap()[..]); + // @FIXME: move in worker + match &data.config { + MerkleBackpressureEnum::None => info!("Merkle Backpressure is not activated"), + MerkleBackpressureEnum::FixedQueue(v) => info!( + "Merkle backpressure with a fixed queue size (qlen={}) is activated.", + v.max_queue_size + ), + } + Arc::new(Self { data, empty_node_hash, @@ -125,6 +135,11 @@ impl MerkleUpdater { k ); } + + let maybe_bound = self.data.merkle_todo_bounded_queue.clone(); + if let Some(b) = &maybe_bound { + b.add_permits(1); + } Ok(()) } @@ -287,12 +302,12 @@ impl MerkleUpdater { MerkleNode::decode_opt(&ent) } - pub fn merkle_tree_approximate_len(&self) -> Result { - Ok(self.data.merkle_tree.approximate_len()?) + pub fn merkle_tree_len(&self) -> Result { + Ok(self.data.merkle_tree.len()?) } - pub fn todo_approximate_len(&self) -> Result { - Ok(self.data.merkle_todo.approximate_len()?) + pub fn todo_len(&self) -> Result { + Ok(self.data.merkle_todo.len()?) } } @@ -306,7 +321,7 @@ impl Worker for MerkleWorker { fn status(&self) -> WorkerStatus { WorkerStatus { - queue_length: Some(self.0.todo_approximate_len().unwrap_or(0) as u64), + queue_length: Some(self.0.todo_len().unwrap_or(0) as u64), ..Default::default() } } diff --git a/src/table/metrics.rs b/src/table/metrics.rs index 78593202..6b05a21c 100644 --- a/src/table/metrics.rs +++ b/src/table/metrics.rs @@ -1,12 +1,16 @@ use opentelemetry::{global, metrics::*, KeyValue}; +use std::convert::TryInto; use garage_db as db; +use crate::data::MerkleTodo; + /// TableMetrics reference all counter used for metrics pub struct TableMetrics { pub(crate) _table_size: ValueObserver, pub(crate) _merkle_tree_size: ValueObserver, pub(crate) _merkle_todo_len: ValueObserver, + pub(crate) _merkle_todo_bounded_queue_free: ValueObserver, pub(crate) _gc_todo_len: ValueObserver, pub(crate) get_request_counter: BoundCounter, @@ -25,7 +29,7 @@ impl TableMetrics { table_name: &'static str, store: db::Tree, merkle_tree: db::Tree, - merkle_todo: db::Tree, + merkle_todo: MerkleTodo, gc_todo: db::Tree, ) -> Self { let meter = global::meter(table_name); @@ -34,7 +38,7 @@ impl TableMetrics { .u64_value_observer( "table.size", move |observer| { - if let Ok(value) = store.approximate_len() { + if let Ok(value) = store.len() { observer.observe( value as u64, &[KeyValue::new("table_name", table_name)], @@ -48,7 +52,7 @@ impl TableMetrics { .u64_value_observer( "table.merkle_tree_size", move |observer| { - if let Ok(value) = merkle_tree.approximate_len() { + if let Ok(value) = merkle_tree.len() { observer.observe( value as u64, &[KeyValue::new("table_name", table_name)], @@ -62,7 +66,7 @@ impl TableMetrics { .u64_value_observer( "table.merkle_updater_todo_queue_length", move |observer| { - if let Ok(v) = merkle_todo.approximate_len() { + if let Ok(v) = merkle_todo.len() { observer.observe( v as u64, &[KeyValue::new("table_name", table_name)], @@ -72,11 +76,25 @@ impl TableMetrics { ) .with_description("Merkle tree updater TODO queue length") .init(), + _merkle_todo_bounded_queue_free: meter + .u64_value_observer( + "table.merkle_todo_bounded_queue_free", + move |observer| { + let maybe_bounded = merkle_todo_bounded_queue.clone(); + let free: u64 = match &maybe_bounded { + Some(v) => v.available_permits().try_into().unwrap(), + None => 0, + }; + observer.observe(free, &[KeyValue::new("table_name", table_name)]) + } + ) + .with_description("Merkle TODO queue free slots") + .init(), _gc_todo_len: meter .u64_value_observer( "table.gc_todo_queue_length", move |observer| { - if let Ok(value) = gc_todo.approximate_len() { + if let Ok(value) = gc_todo.len() { observer.observe( value as u64, &[KeyValue::new("table_name", table_name)], diff --git a/src/table/queue.rs b/src/table/queue.rs index 7ef1f16e..ffe0a4a7 100644 --- a/src/table/queue.rs +++ b/src/table/queue.rs @@ -27,7 +27,7 @@ impl Worker for InsertQueueWorker { fn status(&self) -> WorkerStatus { WorkerStatus { - queue_length: Some(self.0.data.insert_queue.approximate_len().unwrap_or(0) as u64), + queue_length: Some(self.0.data.insert_queue.len().unwrap_or(0) as u64), ..Default::default() } } diff --git a/src/table/sync.rs b/src/table/sync.rs index 2d43b9fc..2f83aca2 100644 --- a/src/table/sync.rs +++ b/src/table/sync.rs @@ -244,8 +244,14 @@ impl TableSyncer { // All remote nodes have written those items, now we can delete them locally let mut not_removed = 0; + let maybe_bounded = self.data.merkle_todo_bounded_queue.clone(); + if let Some(b) = maybe_bounded { + b.acquire_many(items.len() as u32).await.unwrap().forget(); + } + for (k, v) in items.iter() { - if !self.data.delete_if_equal(&k[..], &v[..])? { + let removed = self.data.delete_if_equal(&k[..], &v[..])?; + if !removed { not_removed += 1; } } diff --git a/src/table/table.rs b/src/table/table.rs index c96f4731..3fad75cf 100644 --- a/src/table/table.rs +++ b/src/table/table.rs @@ -14,6 +14,7 @@ use opentelemetry::{ use garage_db as db; use garage_util::background::BackgroundRunner; +use garage_util::config::MerkleBackpressureEnum; use garage_util::data::*; use garage_util::error::Error; use garage_util::metrics::RecordDuration; @@ -68,12 +69,18 @@ impl Rpc for TableRpc { impl Table { // =============== PUBLIC INTERFACE FUNCTIONS (new, insert, get, etc) =============== - pub fn new(instance: F, replication: R, system: Arc, db: &db::Db) -> Arc { + pub fn new( + instance: F, + replication: R, + system: Arc, + db: &db::Db, + config: &MerkleBackpressureEnum, + ) -> Arc { let endpoint = system .netapp .endpoint(format!("garage_table/table.rs/Rpc:{}", F::TABLE_NAME)); - let data = TableData::new(system.clone(), instance, replication, db); + let data = TableData::new(system.clone(), instance, replication, db, config); let merkle_updater = MerkleUpdater::new(data.clone()); @@ -131,7 +138,8 @@ impl Table { who.as_ref(), rpc, RequestStrategy::with_priority(PRIO_NORMAL) - .with_quorum(self.data.replication.write_quorum()), + .with_quorum(self.data.replication.write_quorum()) + .with_write_limiter(), ) .await?; @@ -527,6 +535,10 @@ impl EndpointHandler> for Table Ok(TableRpc::Update(values)) } TableRpc::Update(pairs) => { + let maybe_bounded = self.data.merkle_todo_bounded_queue.clone(); + if let Some(b) = maybe_bounded { + b.acquire_many(pairs.len() as u32).await.unwrap().forget(); + } self.data.update_many(pairs)?; Ok(TableRpc::Ok) } diff --git a/src/util/Cargo.toml b/src/util/Cargo.toml index 46fa6590..123406db 100644 --- a/src/util/Cargo.toml +++ b/src/util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_util" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat "] edition = "2018" license = "AGPL-3.0" @@ -21,7 +21,7 @@ arc-swap.workspace = true async-trait.workspace = true blake2.workspace = true bytesize.workspace = true -thiserror.workspace = true +err-derive.workspace = true hexdump.workspace = true xxhash-rust.workspace = true hex.workspace = true diff --git a/src/util/background/worker.rs b/src/util/background/worker.rs index 3c938b7e..76fb14e8 100644 --- a/src/util/background/worker.rs +++ b/src/util/background/worker.rs @@ -115,39 +115,32 @@ impl WorkerProcessor { trace!("{} (TID {}): {:?}", worker.worker.name(), worker.task_id, worker.state); // Save worker info - { - let mut wi = self.worker_info.lock().unwrap(); - match wi.get_mut(&worker.task_id) { - Some(i) => { - i.state = worker.state; - i.status = worker.worker.status(); - i.errors = worker.errors; - i.consecutive_errors = worker.consecutive_errors; - if worker.last_error.is_some() { - i.last_error = worker.last_error.take(); - } - } - None => { - wi.insert(worker.task_id, WorkerInfo { - name: worker.worker.name(), - state: worker.state, - status: worker.worker.status(), - errors: worker.errors, - consecutive_errors: worker.consecutive_errors, - last_error: worker.last_error.take(), - }); + let mut wi = self.worker_info.lock().unwrap(); + match wi.get_mut(&worker.task_id) { + Some(i) => { + i.state = worker.state; + i.status = worker.worker.status(); + i.errors = worker.errors; + i.consecutive_errors = worker.consecutive_errors; + if worker.last_error.is_some() { + i.last_error = worker.last_error.take(); } } + None => { + wi.insert(worker.task_id, WorkerInfo { + name: worker.worker.name(), + state: worker.state, + status: worker.worker.status(), + errors: worker.errors, + consecutive_errors: worker.consecutive_errors, + last_error: worker.last_error.take(), + }); + } } if worker.state == WorkerState::Done { info!("Worker {} (TID {}) exited", worker.worker.name(), worker.task_id); } else { - // Yield to the Tokio scheduler between consecutive Busy steps so - // that a worker which never suspends on its own cannot starve other tasks. - if worker.state == WorkerState::Busy { - tokio::task::yield_now().await; - } workers.push(async move { worker.step().await; worker diff --git a/src/util/config.rs b/src/util/config.rs index eb889ebe..b58047b0 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -45,11 +45,6 @@ pub struct Config { )] pub block_size: usize, - /// Maximum number of parallel block writes per PUT request - /// Higher values improve throughput but increase memory usage - /// Default: 3, Recommended: 10-30 for NVMe, 3-10 for HDD - #[serde(default = "default_block_max_concurrent_writes_per_request")] - pub block_max_concurrent_writes_per_request: usize, /// Number of replicas. Can be any positive integer, but uneven numbers are more favorable. /// - 1 for single-node clusters, or to disable replication /// - 3 is the recommended and supported setting. @@ -80,10 +75,6 @@ pub struct Config { )] pub block_ram_buffer_max: usize, - /// Maximum number of concurrent reads of block files on disk - #[serde(default = "default_block_max_concurrent_reads")] - pub block_max_concurrent_reads: usize, - /// Skip the permission check of secret files. Useful when /// POSIX ACLs (or more complex chmods) are used. #[serde(default)] @@ -131,10 +122,6 @@ pub struct Config { #[serde(deserialize_with = "deserialize_capacity", default)] pub lmdb_map_size: usize, - /// Fjall block cache size - #[serde(deserialize_with = "deserialize_capacity", default)] - pub fjall_block_cache_size: usize, - // -- APIs /// Configuration for S3 api pub s3_api: S3ApiConfig, @@ -149,9 +136,9 @@ pub struct Config { #[serde(default = "Default::default")] pub admin: AdminConfig, - /// Allow punycode in bucket names - #[serde(default)] - pub allow_punycode: bool, + /// --- Experimental + #[serde(default = "Default::default")] + pub experimental: ExperimentalConfig, } /// Value for data_dir: either a single directory or a list of dirs with attributes @@ -272,9 +259,40 @@ pub struct KubernetesDiscoveryConfig { pub skip_crd: bool, } -pub fn default_block_max_concurrent_writes_per_request() -> usize { - 3 +#[derive(Deserialize, Debug, Clone, Default)] +pub struct ExperimentalConfig { + pub merkle_backpressure: MerkleBackpressureEnum, + pub rpc_in_flight_limiters: RpcInFlightLimiterEnum, } + +#[derive(Deserialize, Debug, Clone, Default)] +#[serde(rename_all = "lowercase", tag = "kind")] +pub enum MerkleBackpressureEnum { + #[default] + None, + FixedQueue(MerkleFixedQueue), +} + +#[derive(Deserialize, Debug, Clone, Default)] +#[serde(rename_all = "lowercase", tag = "kind")] +pub enum RpcInFlightLimiterEnum { + #[default] + None, + FixedSize(InFlightFixedSize), +} + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct InFlightFixedSize { + #[serde(default = "default_max_table_write")] + pub max_table_write: usize, +} + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct MerkleFixedQueue { + #[serde(default = "default_max_queue_size")] + pub max_queue_size: usize, +} + /// Read and parse configuration pub fn read_config(config_file: PathBuf) -> Result { let config = std::fs::read_to_string(config_file)?; @@ -292,9 +310,6 @@ fn default_block_size() -> usize { fn default_block_ram_buffer_max() -> usize { 256 * 1024 * 1024 } -fn default_block_max_concurrent_reads() -> usize { - 16 -} fn default_consistency_mode() -> String { "consistent".into() @@ -304,6 +319,14 @@ fn default_compression() -> Option { Some(1) } +fn default_max_table_write() -> usize { + 64 +} + +fn default_max_queue_size() -> usize { + 256 +} + fn deserialize_compression<'de, D>(deserializer: D) -> Result, D::Error> where D: de::Deserializer<'de>, diff --git a/src/util/error.rs b/src/util/error.rs index 170d2687..75fd3f9c 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -2,7 +2,7 @@ use std::fmt; use std::io; -use thiserror::Error; +use err_derive::Error; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; @@ -12,61 +12,68 @@ use crate::encode::debug_serialize; /// Regroup all Garage errors #[derive(Debug, Error)] pub enum Error { - #[error("IO error: {0}")] - Io(#[from] io::Error), + #[error(display = "IO error: {}", _0)] + Io(#[error(source)] io::Error), - #[error("Hyper error: {0}")] - Hyper(#[from] hyper::Error), + #[error(display = "Hyper error: {}", _0)] + Hyper(#[error(source)] hyper::Error), - #[error("HTTP error: {0}")] - Http(#[from] http::Error), + #[error(display = "HTTP error: {}", _0)] + Http(#[error(source)] http::Error), - #[error("Invalid HTTP header value: {0}")] - HttpHeader(#[from] http::header::ToStrError), + #[error(display = "Invalid HTTP header value: {}", _0)] + HttpHeader(#[error(source)] http::header::ToStrError), - #[error("Network error: {0}")] - Net(#[from] garage_net::error::Error), + #[error(display = "Network error: {}", _0)] + Net(#[error(source)] garage_net::error::Error), - #[error("DB error: {0}")] - Db(#[from] garage_db::Error), + #[error(display = "DB error: {}", _0)] + Db(#[error(source)] garage_db::Error), - #[error("Messagepack encode error: {0}")] - RmpEncode(#[from] rmp_serde::encode::Error), - #[error("Messagepack decode error: {0}")] - RmpDecode(#[from] rmp_serde::decode::Error), - #[error("JSON error: {0}")] - Json(#[from] serde_json::error::Error), - #[error("TOML decode error: {0}")] - TomlDecode(#[from] toml::de::Error), + #[error(display = "Messagepack encode error: {}", _0)] + RmpEncode(#[error(source)] rmp_serde::encode::Error), + #[error(display = "Messagepack decode error: {}", _0)] + RmpDecode(#[error(source)] rmp_serde::decode::Error), + #[error(display = "JSON error: {}", _0)] + Json(#[error(source)] serde_json::error::Error), + #[error(display = "TOML decode error: {}", _0)] + TomlDecode(#[error(source)] toml::de::Error), - #[error("Tokio join error: {0}")] - TokioJoin(#[from] tokio::task::JoinError), + #[error(display = "Tokio join error: {}", _0)] + TokioJoin(#[error(source)] tokio::task::JoinError), - #[error("Tokio semaphore acquire error: {0}")] - TokioSemAcquire(#[from] tokio::sync::AcquireError), + #[error(display = "Tokio semaphore acquire error: {}", _0)] + TokioSemAcquire(#[error(source)] tokio::sync::AcquireError), - #[error("Tokio broadcast receive error: {0}")] - TokioBcastRecv(#[from] tokio::sync::broadcast::error::RecvError), + #[error(display = "Tokio broadcast receive error: {}", _0)] + TokioBcastRecv(#[error(source)] tokio::sync::broadcast::error::RecvError), - #[error("Remote error: {0}")] + #[error(display = "Remote error: {}", _0)] RemoteError(String), - #[error("Timeout")] + #[error(display = "Timeout")] Timeout, - #[error("Could not reach quorum of {0} (sets={1:?}). {2} of {3} request succeeded, others returned errors: {4:?}")] + #[error( + display = "Could not reach quorum of {} (sets={:?}). {} of {} request succeeded, others returned errors: {:?}", + _0, + _1, + _2, + _3, + _4 + )] Quorum(usize, Option, usize, usize, Vec), - #[error("Unexpected RPC message: {0}")] + #[error(display = "Unexpected RPC message: {}", _0)] UnexpectedRpcMessage(String), - #[error("Corrupt data: does not match hash {0:?}")] + #[error(display = "Corrupt data: does not match hash {:?}", _0)] CorruptData(Hash), - #[error("Missing block {0:?}: no node returned a valid block")] + #[error(display = "Missing block {:?}: no node returned a valid block", _0)] MissingBlock(Hash), - #[error("{0}")] + #[error(display = "{}", _0)] Message(String), } diff --git a/src/web/Cargo.toml b/src/web/Cargo.toml index e0cb317f..c4fdbc0e 100644 --- a/src/web/Cargo.toml +++ b/src/web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_web" -version = "1.3.1" +version = "1.1.0" authors = ["Alex Auvolat ", "Quentin Dufour "] edition = "2018" license = "AGPL-3.0" @@ -20,7 +20,7 @@ garage_model.workspace = true garage_util.workspace = true garage_table.workspace = true -thiserror.workspace = true +err-derive.workspace = true tracing.workspace = true percent-encoding.workspace = true diff --git a/src/web/error.rs b/src/web/error.rs index aef74923..7e6d4542 100644 --- a/src/web/error.rs +++ b/src/web/error.rs @@ -1,6 +1,6 @@ +use err_derive::Error; use hyper::header::HeaderValue; use hyper::{HeaderMap, StatusCode}; -use thiserror::Error; use garage_api_common::generic_server::ApiError; @@ -8,15 +8,15 @@ use garage_api_common::generic_server::ApiError; #[derive(Debug, Error)] pub enum Error { /// An error received from the API crate - #[error("API error: {0}")] + #[error(display = "API error: {}", _0)] ApiError(garage_api_s3::error::Error), /// The file does not exist - #[error("Not found")] + #[error(display = "Not found")] NotFound, /// The client sent a request without host, or with unsupported method - #[error("Bad request: {0}")] + #[error(display = "Bad request: {}", _0)] BadRequest(String), } diff --git a/src/web/web_server.rs b/src/web/web_server.rs index ea02ab0f..242f7801 100644 --- a/src/web/web_server.rs +++ b/src/web/web_server.rs @@ -397,30 +397,10 @@ fn error_to_res(e: Error) -> Response> { // was a HEAD request or we couldn't get the error document) // We do NOT enter this code path when returning the bucket's // error document (this is handled in serve_file) - let mut body_str = format!( - r"{http_code} {code_text} -

{http_code} {code_text}

", - http_code = e.http_status_code().as_u16(), - code_text = e.http_status_code().canonical_reason().unwrap_or("Unknown"), - ); - if let Error::ApiError(ref err) = e { - body_str.push_str(&format!( - r" -
    -
  • Code: {s3_code}
  • -
  • Message: {s3_message}.
  • -
", - s3_code = err.aws_code(), - s3_message = err, - )); - } - let mut http_error = Response::new(string_body(body_str)); + let body = string_body(format!("{}\n", e)); + let mut http_error = Response::new(body); *http_error.status_mut() = e.http_status_code(); e.add_headers(http_error.headers_mut()); - http_error.headers_mut().insert( - http::header::CONTENT_TYPE, - "text/html; charset=utf-8".parse().unwrap(), - ); http_error }