Merge branch 'main-v1' into merge-v1

This commit is contained in:
Alex Auvolat
2026-01-24 09:47:11 +01:00
17 changed files with 115 additions and 50 deletions
+7 -7
View File
@@ -12,32 +12,32 @@ when:
steps: steps:
- name: check formatting - name: check formatting
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-shell --attr devShell --run "cargo fmt -- --check" - nix-build -j4 --attr flakePackages.fmt
- name: build - name: build
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-build -j4 --attr flakePackages.dev - nix-build -j4 --attr flakePackages.dev
- name: unit + func tests (lmdb) - name: unit + func tests (lmdb)
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-build -j4 --attr flakePackages.tests-lmdb - nix-build -j4 --attr flakePackages.tests-lmdb
- name: unit + func tests (sqlite) - name: unit + func tests (sqlite)
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-build -j4 --attr flakePackages.tests-sqlite - nix-build -j4 --attr flakePackages.tests-sqlite
- name: unit + func tests (fjall) - name: unit + func tests (fjall)
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-build -j4 --attr flakePackages.tests-fjall - nix-build -j4 --attr flakePackages.tests-fjall
- name: integration tests - name: integration tests
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-build -j4 --attr flakePackages.dev - nix-build -j4 --attr flakePackages.dev
- nix-shell --attr ci --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) - nix-shell --attr ci --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
+2 -2
View File
@@ -11,7 +11,7 @@ depends_on:
steps: steps:
- name: refresh-index - name: refresh-index
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
environment: environment:
AWS_ACCESS_KEY_ID: AWS_ACCESS_KEY_ID:
from_secret: garagehq_aws_access_key_id from_secret: garagehq_aws_access_key_id
@@ -22,7 +22,7 @@ steps:
- nix-shell --attr ci --run "refresh_index" - nix-shell --attr ci --run "refresh_index"
- name: multiarch-docker - name: multiarch-docker
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
environment: environment:
DOCKER_AUTH: DOCKER_AUTH:
from_secret: docker_auth from_secret: docker_auth
+7 -7
View File
@@ -19,17 +19,17 @@ matrix:
steps: steps:
- name: build - name: build
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-build --attr releasePackages.${ARCH} --argstr git_version ${CI_COMMIT_TAG:-$CI_COMMIT_SHA} - nix-build --attr releasePackages.${ARCH} --argstr git_version ${CI_COMMIT_TAG:-$CI_COMMIT_SHA}
- name: check is static binary - name: check is static binary
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-shell --attr ci --run "./script/not-dynamic.sh result/bin/garage" - nix-shell --attr ci --run "./script/not-dynamic.sh result/bin/garage"
- name: integration tests - name: integration tests
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-shell --attr ci --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) - nix-shell --attr ci --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
when: when:
@@ -39,7 +39,7 @@ steps:
ARCH: i386 ARCH: i386
- name: upgrade tests from v1.0.0 - name: upgrade tests from v1.0.0
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-shell --attr ci --run "./script/test-upgrade.sh v1.0.0 x86_64-unknown-linux-musl" || (cat /tmp/garage.log; false) - nix-shell --attr ci --run "./script/test-upgrade.sh v1.0.0 x86_64-unknown-linux-musl" || (cat /tmp/garage.log; false)
when: when:
@@ -47,7 +47,7 @@ steps:
ARCH: amd64 ARCH: amd64
- name: upgrade tests from v0.8.4 - name: upgrade tests from v0.8.4
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
commands: commands:
- nix-shell --attr ci --run "./script/test-upgrade.sh v0.8.4 x86_64-unknown-linux-musl" || (cat /tmp/garage.log; false) - nix-shell --attr ci --run "./script/test-upgrade.sh v0.8.4 x86_64-unknown-linux-musl" || (cat /tmp/garage.log; false)
when: when:
@@ -55,7 +55,7 @@ steps:
ARCH: amd64 ARCH: amd64
- name: push static binary - name: push static binary
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
environment: environment:
TARGET: "${TARGET}" TARGET: "${TARGET}"
AWS_ACCESS_KEY_ID: AWS_ACCESS_KEY_ID:
@@ -66,7 +66,7 @@ steps:
- nix-shell --attr ci --run "to_s3" - nix-shell --attr ci --run "to_s3"
- name: docker build and publish - name: docker build and publish
image: nixpkgs/nix:nixos-22.05 image: nixpkgs/nix:nixos-24.05
environment: environment:
DOCKER_PLATFORM: "linux/${ARCH}" DOCKER_PLATFORM: "linux/${ARCH}"
CONTAINER_NAME: "dxflrs/${ARCH}_garage" CONTAINER_NAME: "dxflrs/${ARCH}_garage"
+4 -3
View File
@@ -15,9 +15,10 @@ Alpine Linux repositories (available since v3.17):
apk add garage apk add garage
``` ```
The default configuration file is installed to `/etc/garage.toml`. You can run 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 Garage using: `rc-service garage start`.
will be automatically replaced with a random string on the first 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 Please note that this package is built without Consul discovery, Kubernetes
discovery, OpenTelemetry exporter, and K2V features (K2V will be enabled once discovery, OpenTelemetry exporter, and K2V features (K2V will be enabled once
+10 -1
View File
@@ -25,7 +25,7 @@ db_engine = "lmdb"
block_size = "1M" block_size = "1M"
block_ram_buffer_max = "256MiB" block_ram_buffer_max = "256MiB"
block_max_concurrent_reads = 16 block_max_concurrent_reads = 16
block_max_concurrent_writes_per_request =10
lmdb_map_size = "1T" lmdb_map_size = "1T"
compression_level = 1 compression_level = 1
@@ -103,6 +103,7 @@ Top-level configuration options, in alphabetical order:
[`allow_world_readable_secrets`](#allow_world_readable_secrets), [`allow_world_readable_secrets`](#allow_world_readable_secrets),
[`block_max_concurrent_reads`](#block_max_concurrent_reads), [`block_max_concurrent_reads`](#block_max_concurrent_reads),
[`block_ram_buffer_max`](#block_ram_buffer_max), [`block_ram_buffer_max`](#block_ram_buffer_max),
[`block_max_concurrent_writes_per_request`](#block_max_concurrent_writes_per_request),
[`block_size`](#block_size), [`block_size`](#block_size),
[`bootstrap_peers`](#bootstrap_peers), [`bootstrap_peers`](#bootstrap_peers),
[`compression_level`](#compression_level), [`compression_level`](#compression_level),
@@ -559,6 +560,14 @@ metric in Prometheus: a non-zero number of such events indicates an I/O
bottleneck on HDD read speed. 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} #### `lmdb_map_size` {#lmdb_map_size}
This parameters can be used to set the map size used by LMDB, This parameters can be used to set the map size used by LMDB,
@@ -27,7 +27,7 @@ Feel free to open a PR to suggest fixes this table. Minio is missing because the
| 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) | | 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/general/latest/gr/signature-version-2.html) (deprecated) | ❌ Missing | ✅ | ✅ | ✅ | ✅ | | [signature v2](https://docs.aws.amazon.com/AmazonS3/latest/API/Appendix-Sigv2.html) (deprecated) | ❌ Missing | ✅ | ✅ | ✅ | ✅ |
| [signature v4](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) | ✅ Implemented | ✅ | ✅ | ❌ | ✅ | | [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 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 | ❌| ✅| ✅ | ✅ | | [URL vhost-style](https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#virtual-hosted-style-access) URL (eg. `bucket.host.tld/key`) | ✅ Implemented | ❌| ✅| ✅ | ✅ |
Generated
+14 -14
View File
@@ -2,11 +2,11 @@
"nodes": { "nodes": {
"crane": { "crane": {
"locked": { "locked": {
"lastModified": 1737689766, "lastModified": 1768873933,
"narHash": "sha256-ivVXYaYlShxYoKfSo5+y5930qMKKJ8CLcAoIBPQfJ6s=", "narHash": "sha256-CfyzdaeLNGkyAHp3kT5vjvXhA1pVVK7nyDziYxCPsNk=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "6fe74265bbb6d016d663b1091f015e2976c4a527", "rev": "0bda7e7d005ccb5522a76d11ccfbf562b71953ca",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -17,11 +17,11 @@
}, },
"flake-compat": { "flake-compat": {
"locked": { "locked": {
"lastModified": 1717312683, "lastModified": 1761640442,
"narHash": "sha256-FrlieJH50AuvagamEvWMIE6D2OAnERuDboFDYAED/dE=", "narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=",
"owner": "nix-community", "owner": "nix-community",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "38fd3954cf65ce6faf3d0d45cd26059e059f07ea", "rev": "4a56054d8ffc173222d09dad23adf4ba946c8884",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -50,17 +50,17 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1747825515, "lastModified": 1763977559,
"narHash": "sha256-BWpMQymVI73QoKZdcVCxUCCK3GNvr/xa2Dc4DM1o2BE=", "narHash": "sha256-g4MKqsIRy5yJwEsI+fYODqLUnAqIY4kZai0nldAP6EM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "cd2812de55cf87df88a9e09bf3be1ce63d50c1a6", "rev": "cfe2c7d5b5d3032862254e68c37a6576b633d632",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "cd2812de55cf87df88a9e09bf3be1ce63d50c1a6", "rev": "cfe2c7d5b5d3032862254e68c37a6576b633d632",
"type": "github" "type": "github"
} }
}, },
@@ -80,17 +80,17 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1738549608, "lastModified": 1763952169,
"narHash": "sha256-GdyT9QEUSx5k/n8kILuNy83vxxdyUfJ8jL5mMpQZWfw=", "narHash": "sha256-+PeDBD8P+NKauH+w7eO/QWCIp8Cx4mCfWnh9sJmy9CM=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "35c6f8c4352f995ecd53896200769f80a3e8f22d", "rev": "ab726555a9a72e6dc80649809147823a813fa95b",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "35c6f8c4352f995ecd53896200769f80a3e8f22d", "rev": "ab726555a9a72e6dc80649809147823a813fa95b",
"type": "github" "type": "github"
} }
}, },
+12 -4
View File
@@ -2,13 +2,13 @@
description = description =
"Garage, an S3-compatible distributed object store for self-hosted deployments"; "Garage, an S3-compatible distributed object store for self-hosted deployments";
# Nixpkgs 25.05 as of 2025-05-22 # Nixpkgs 25.05 as of 2025-11-24
inputs.nixpkgs.url = inputs.nixpkgs.url =
"github:NixOS/nixpkgs/cd2812de55cf87df88a9e09bf3be1ce63d50c1a6"; "github:NixOS/nixpkgs/cfe2c7d5b5d3032862254e68c37a6576b633d632";
# Rust overlay as of 2025-02-03 # Rust overlay as of 2025-11-24
inputs.rust-overlay.url = inputs.rust-overlay.url =
"github:oxalica/rust-overlay/35c6f8c4352f995ecd53896200769f80a3e8f22d"; "github:oxalica/rust-overlay/ab726555a9a72e6dc80649809147823a813fa95b";
inputs.rust-overlay.inputs.nixpkgs.follows = "nixpkgs"; inputs.rust-overlay.inputs.nixpkgs.follows = "nixpkgs";
inputs.crane.url = "github:ipetkov/crane"; inputs.crane.url = "github:ipetkov/crane";
@@ -30,6 +30,10 @@
inherit system nixpkgs crane rust-overlay extraTestEnv; inherit system nixpkgs crane rust-overlay extraTestEnv;
release = false; release = false;
}).garage-test; }).garage-test;
lints = (compile {
inherit system nixpkgs crane rust-overlay;
release = false;
});
in in
{ {
packages = { packages = {
@@ -56,6 +60,10 @@
tests-fjall = testWith { tests-fjall = testWith {
GARAGE_TEST_INTEGRATION_DB_ENGINE = "fjall"; 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 ---- # ---- developpment shell, for making native builds only ----
+12 -1
View File
@@ -48,7 +48,7 @@ let
inherit (pkgs) lib stdenv; inherit (pkgs) lib stdenv;
toolchainFn = (p: p.rust-bin.stable."1.82.0".default.override { toolchainFn = (p: p.rust-bin.stable."1.91.0".default.override {
targets = lib.optionals (target != null) [ rustTarget ]; targets = lib.optionals (target != null) [ rustTarget ];
extensions = [ extensions = [
"rust-src" "rust-src"
@@ -190,4 +190,15 @@ in rec {
pkgs.cacert pkgs.cacert
]; ];
} // extraTestEnv); } // 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";
});
} }
+1
View File
@@ -1,6 +1,7 @@
export AWS_ACCESS_KEY_ID=`cat /tmp/garage.s3 |cut -d' ' -f1` 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_SECRET_ACCESS_KEY=`cat /tmp/garage.s3 |cut -d' ' -f2`
export AWS_DEFAULT_REGION='garage' 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. # 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 $@ ; } function aws { command aws --endpoint-url http://127.0.0.1:3911 $@ ; }
+2
View File
@@ -36,6 +36,8 @@ in
jq jq
]; ];
shellHook = '' shellHook = ''
export AWS_REQUEST_CHECKSUM_CALCULATION='when_required'
function to_s3 { function to_s3 {
AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED \ AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED \
aws \ aws \
+1 -1
View File
@@ -26,7 +26,7 @@ pub enum Error {
NoSuchAdminToken(String), NoSuchAdminToken(String),
/// The API access key does not exist /// The API access key does not exist
#[error("Access key not found: {00}")] #[error("Access key not found: {0}")]
NoSuchAccessKey(String), NoSuchAccessKey(String),
/// The requested block does not exist /// The requested block does not exist
+6 -5
View File
@@ -105,7 +105,7 @@ fn check_standard_signature(
// Verify that all necessary request headers are included in signed_headers // Verify that all necessary request headers are included in signed_headers
// The following must be included for all signatures: // The following must be included for all signatures:
// - the Host header (mandatory) // - the Host header (mandatory)
// - all x-amz-* headers used in the request // - all x-amz-* headers used in the request (except x-amz-content-sha256)
// AWS also indicates that the Content-Type header should be signed if // 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. // 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)?; let signed_headers = split_signed_headers(&authorization)?;
@@ -152,7 +152,7 @@ fn check_presigned_signature(
// Verify that all necessary request headers are included in signed_headers // Verify that all necessary request headers are included in signed_headers
// For AWSv4 pre-signed URLs, the following must be included: // For AWSv4 pre-signed URLs, the following must be included:
// - the Host header (mandatory) // - the Host header (mandatory)
// - all x-amz-* headers used in the request // - all x-amz-* headers used in the request (except x-amz-content-sha256)
let signed_headers = split_signed_headers(&authorization)?; let signed_headers = split_signed_headers(&authorization)?;
verify_signed_headers(request.headers(), &signed_headers)?; verify_signed_headers(request.headers(), &signed_headers)?;
@@ -269,7 +269,9 @@ fn verify_signed_headers(headers: &HeaderMap, signed_headers: &[HeaderName]) ->
return Err(Error::bad_request("Header `Host` should be signed")); return Err(Error::bad_request("Header `Host` should be signed"));
} }
for (name, _) in headers.iter() { for (name, _) in headers.iter() {
if name.as_str().starts_with("x-amz-") { // 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 !signed_headers.contains(name) { if !signed_headers.contains(name) {
return Err(Error::bad_request(format!( return Err(Error::bad_request(format!(
"Header `{}` should be signed", "Header `{}` should be signed",
@@ -475,8 +477,7 @@ impl Authorization {
let date = headers let date = headers
.get(X_AMZ_DATE) .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()?; .to_str()?;
let date = parse_date(date)?; let date = parse_date(date)?;
+3 -1
View File
@@ -853,7 +853,9 @@ impl PreconditionHeaders {
} }
fn check(&self, v: &ObjectVersion, etag: &str) -> Result<Option<StatusCode>, Error> { fn check(&self, v: &ObjectVersion, etag: &str) -> Result<Option<StatusCode>, Error> {
let v_date = UNIX_EPOCH + Duration::from_millis(v.timestamp); // 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);
// Implemented from https://datatracker.ietf.org/doc/html/rfc7232#section-6 // Implemented from https://datatracker.ietf.org/doc/html/rfc7232#section-6
+1 -3
View File
@@ -39,8 +39,6 @@ use crate::encryption::{EncryptionParams, OekDerivationInfo};
use crate::error::*; use crate::error::*;
use crate::website::X_AMZ_WEBSITE_REDIRECT_LOCATION; use crate::website::X_AMZ_WEBSITE_REDIRECT_LOCATION;
const PUT_BLOCKS_MAX_PARALLEL: usize = 3;
pub(crate) struct SaveStreamResult { pub(crate) struct SaveStreamResult {
pub(crate) version_uuid: Uuid, pub(crate) version_uuid: Uuid,
pub(crate) version_timestamp: u64, pub(crate) version_timestamp: u64,
@@ -507,7 +505,7 @@ pub(crate) async fn read_and_put_blocks<S: Stream<Item = Result<Bytes, Error>> +
}; };
let recv_next = async { let recv_next = async {
// If more than a maximum number of writes are in progress, don't add more for now // If more than a maximum number of writes are in progress, don't add more for now
if currently_running >= PUT_BLOCKS_MAX_PARALLEL { if currently_running >= ctx.garage.config.block_max_concurrent_writes_per_request {
futures::future::pending().await futures::future::pending().await
} else { } else {
block_rx3.recv().await block_rx3.recv().await
+24
View File
@@ -198,6 +198,7 @@ async fn test_precondition() {
); );
} }
let older_date = DateTime::from_secs_f64(last_modified.as_secs_f64() - 10.0); 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 newer_date = DateTime::from_secs_f64(last_modified.as_secs_f64() + 10.0);
{ {
let err = ctx let err = ctx
@@ -212,6 +213,18 @@ async fn test_precondition() {
matches!(err, Err(SdkError::ServiceError(se)) if se.raw().status().as_u16() == 304) 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 let o = ctx
.client .client
.get_object() .get_object()
@@ -236,6 +249,17 @@ async fn test_precondition() {
matches!(err, Err(SdkError::ServiceError(se)) if se.raw().status().as_u16() == 412) 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 let o = ctx
.client .client
.get_object() .get_object()
+8
View File
@@ -45,6 +45,11 @@ pub struct Config {
)] )]
pub block_size: usize, 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. /// Number of replicas. Can be any positive integer, but uneven numbers are more favorable.
/// - 1 for single-node clusters, or to disable replication /// - 1 for single-node clusters, or to disable replication
/// - 3 is the recommended and supported setting. /// - 3 is the recommended and supported setting.
@@ -272,6 +277,9 @@ pub struct KubernetesDiscoveryConfig {
pub skip_crd: bool, pub skip_crd: bool,
} }
pub fn default_block_max_concurrent_writes_per_request() -> usize {
3
}
/// Read and parse configuration /// Read and parse configuration
pub fn read_config(config_file: PathBuf) -> Result<Config, Error> { pub fn read_config(config_file: PathBuf) -> Result<Config, Error> {
let config = std::fs::read_to_string(config_file)?; let config = std::fs::read_to_string(config_file)?;