Compare commits
3 commits
main
...
feat/preem
Author | SHA1 | Date | |
---|---|---|---|
1ca12a9cb6 | |||
5b6521b868 | |||
e628072d37 |
40 changed files with 434 additions and 340 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -1304,7 +1304,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"async-trait",
|
||||
|
@ -1360,7 +1360,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_api"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
|
@ -1415,7 +1415,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_block"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-compression",
|
||||
|
@ -1442,7 +1442,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_db"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"err-derive",
|
||||
"heed",
|
||||
|
@ -1456,7 +1456,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_model"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
|
@ -1486,7 +1486,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_net"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
|
@ -1512,7 +1512,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_rpc"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
|
@ -1548,7 +1548,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_table"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
|
@ -1570,7 +1570,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_util"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
|
@ -1604,7 +1604,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "garage_web"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"err-derive",
|
||||
"futures",
|
||||
|
@ -4082,9 +4082,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"num-conv",
|
||||
|
@ -4102,9 +4102,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
|
|
158
Cargo.nix
158
Cargo.nix
|
@ -34,7 +34,7 @@ args@{
|
|||
ignoreLockHash,
|
||||
}:
|
||||
let
|
||||
nixifiedLockHash = "466643eea782cd68c6f205858bb9e053aecdb18e2e58427b0527022aad596130";
|
||||
nixifiedLockHash = "fc41fb639a69d62c8c0fb3f9c227162162ebc8142c6fa5cd0599dc381dcd9ebb";
|
||||
workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc;
|
||||
currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock);
|
||||
lockHashIgnored = if ignoreLockHash
|
||||
|
@ -58,17 +58,17 @@ in
|
|||
{
|
||||
cargo2nixVersion = "0.11.0";
|
||||
workspace = {
|
||||
garage_db = rustPackages.unknown.garage_db."1.0.1";
|
||||
garage_util = rustPackages.unknown.garage_util."1.0.1";
|
||||
garage_net = rustPackages.unknown.garage_net."1.0.1";
|
||||
garage_rpc = rustPackages.unknown.garage_rpc."1.0.1";
|
||||
garage_db = rustPackages.unknown.garage_db."1.0.0";
|
||||
garage_util = rustPackages.unknown.garage_util."1.0.0";
|
||||
garage_net = rustPackages.unknown.garage_net."1.0.0";
|
||||
garage_rpc = rustPackages.unknown.garage_rpc."1.0.0";
|
||||
format_table = rustPackages.unknown.format_table."0.1.1";
|
||||
garage_table = rustPackages.unknown.garage_table."1.0.1";
|
||||
garage_block = rustPackages.unknown.garage_block."1.0.1";
|
||||
garage_model = rustPackages.unknown.garage_model."1.0.1";
|
||||
garage_api = rustPackages.unknown.garage_api."1.0.1";
|
||||
garage_web = rustPackages.unknown.garage_web."1.0.1";
|
||||
garage = rustPackages.unknown.garage."1.0.1";
|
||||
garage_table = rustPackages.unknown.garage_table."1.0.0";
|
||||
garage_block = rustPackages.unknown.garage_block."1.0.0";
|
||||
garage_model = rustPackages.unknown.garage_model."1.0.0";
|
||||
garage_api = rustPackages.unknown.garage_api."1.0.0";
|
||||
garage_web = rustPackages.unknown.garage_web."1.0.0";
|
||||
garage = rustPackages.unknown.garage."1.0.0";
|
||||
k2v-client = rustPackages.unknown.k2v-client."0.0.4";
|
||||
};
|
||||
"registry+https://github.com/rust-lang/crates.io-index".addr2line."0.21.0" = overridableMkRustCrate (profileName: rec {
|
||||
|
@ -424,7 +424,7 @@ in
|
|||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."0.2.11" { inherit profileName; }).out;
|
||||
hyper = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."0.14.28" { inherit profileName; }).out;
|
||||
ring = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".ring."0.17.7" { inherit profileName; }).out;
|
||||
time = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" { inherit profileName; }).out;
|
||||
time = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.34" { inherit profileName; }).out;
|
||||
tokio = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.36.0" { inherit profileName; }).out;
|
||||
tracing = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".tracing."0.1.40" { inherit profileName; }).out;
|
||||
zeroize = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".zeroize."1.7.0" { inherit profileName; }).out;
|
||||
|
@ -643,7 +643,7 @@ in
|
|||
ring = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".ring."0.17.7" { inherit profileName; }).out;
|
||||
sha2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".sha2."0.10.8" { inherit profileName; }).out;
|
||||
subtle = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".subtle."2.5.0" { inherit profileName; }).out;
|
||||
time = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" { inherit profileName; }).out;
|
||||
time = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.34" { inherit profileName; }).out;
|
||||
tracing = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".tracing."0.1.40" { inherit profileName; }).out;
|
||||
zeroize = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".zeroize."1.7.0" { inherit profileName; }).out;
|
||||
};
|
||||
|
@ -823,7 +823,7 @@ in
|
|||
pin_utils = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".pin-utils."0.1.0" { inherit profileName; }).out;
|
||||
ryu = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".ryu."1.0.16" { inherit profileName; }).out;
|
||||
${ if false then "serde" else null } = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.196" { inherit profileName; }).out;
|
||||
time = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" { inherit profileName; }).out;
|
||||
time = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.34" { inherit profileName; }).out;
|
||||
tokio = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.36.0" { inherit profileName; }).out;
|
||||
tokio_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.10" { inherit profileName; }).out;
|
||||
};
|
||||
|
@ -1910,9 +1910,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/garage");
|
||||
features = builtins.concatLists [
|
||||
|
@ -1940,15 +1940,15 @@ in
|
|||
format_table = (rustPackages."unknown".format_table."0.1.1" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
|
||||
garage_api = (rustPackages."unknown".garage_api."1.0.1" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."1.0.1" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.1" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."1.0.1" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.1" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.1" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.1" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.1" { inherit profileName; }).out;
|
||||
garage_web = (rustPackages."unknown".garage_web."1.0.1" { inherit profileName; }).out;
|
||||
garage_api = (rustPackages."unknown".garage_api."1.0.0" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."1.0.0" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.0" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."1.0.0" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.0" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.0" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.0" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.0" { inherit profileName; }).out;
|
||||
garage_web = (rustPackages."unknown".garage_web."1.0.0" { inherit profileName; }).out;
|
||||
git_version = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".git-version."0.3.9" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
sodiumoxide = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".kuska-sodiumoxide."0.2.5-0" { inherit profileName; }).out;
|
||||
|
@ -1988,9 +1988,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_api."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_api."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_api";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/api");
|
||||
features = builtins.concatLists [
|
||||
|
@ -2014,12 +2014,12 @@ in
|
|||
form_urlencoded = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".form_urlencoded."1.2.1" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."1.0.1" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."1.0.1" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.1" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.1" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.1" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.1" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."1.0.0" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."1.0.0" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.0" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.0" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.0" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.0" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
hmac = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hmac."0.12.1" { inherit profileName; }).out;
|
||||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out;
|
||||
|
@ -2052,9 +2052,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_block."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_block."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_block";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/block");
|
||||
features = builtins.concatLists [
|
||||
|
@ -2068,11 +2068,11 @@ in
|
|||
bytesize = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytesize."1.3.0" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.1" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.1" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.1" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.1" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.1" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.0" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.0" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.0" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.0" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.0" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
|
||||
rand = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand."0.8.5" { inherit profileName; }).out;
|
||||
|
@ -2085,9 +2085,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_db."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_db."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_db";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/db");
|
||||
features = builtins.concatLists [
|
||||
|
@ -2114,9 +2114,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_model."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_model."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_model";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/model");
|
||||
features = builtins.concatLists [
|
||||
|
@ -2134,12 +2134,12 @@ in
|
|||
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."1.0.1" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.1" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.1" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.1" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.1" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.1" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."1.0.0" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.0" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.0" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.0" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.0" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.0" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out;
|
||||
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
|
||||
|
@ -2153,9 +2153,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_net."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_net."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_net";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/net");
|
||||
features = builtins.concatLists [
|
||||
|
@ -2190,9 +2190,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_rpc."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_rpc."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_rpc";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/rpc");
|
||||
features = builtins.concatLists [
|
||||
|
@ -2214,9 +2214,9 @@ in
|
|||
format_table = (rustPackages."unknown".format_table."0.1.1" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.1" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.1" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.1" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.0" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.0" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.0" { inherit profileName; }).out;
|
||||
gethostname = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".gethostname."0.4.3" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
ipnet = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".ipnet."2.9.0" { inherit profileName; }).out;
|
||||
|
@ -2239,9 +2239,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_table."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_table."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_table";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/table");
|
||||
dependencies = {
|
||||
|
@ -2250,9 +2250,9 @@ in
|
|||
bytes = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytes."1.5.0" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.1" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.1" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.1" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.0" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."1.0.0" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.0" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
hexdump = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hexdump."0.1.1" { inherit profileName; }).out;
|
||||
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
|
||||
|
@ -2264,9 +2264,9 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_util."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_util."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_util";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/util");
|
||||
features = builtins.concatLists [
|
||||
|
@ -2282,8 +2282,8 @@ in
|
|||
digest = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".digest."0.10.7" { inherit profileName; }).out;
|
||||
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.1" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.1" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."1.0.0" { inherit profileName; }).out;
|
||||
garage_net = (rustPackages."unknown".garage_net."1.0.0" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
hexdump = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hexdump."0.1.1" { inherit profileName; }).out;
|
||||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out;
|
||||
|
@ -2308,18 +2308,18 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"unknown".garage_web."1.0.1" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_web."1.0.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_web";
|
||||
version = "1.0.1";
|
||||
version = "1.0.0";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/web");
|
||||
dependencies = {
|
||||
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
|
||||
garage_api = (rustPackages."unknown".garage_api."1.0.1" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."1.0.1" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.1" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.1" { inherit profileName; }).out;
|
||||
garage_api = (rustPackages."unknown".garage_api."1.0.0" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."1.0.0" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."1.0.0" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."1.0.0" { inherit profileName; }).out;
|
||||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out;
|
||||
http_body_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http-body-util."0.1.0" { inherit profileName; }).out;
|
||||
hyper = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."1.1.0" { inherit profileName; }).out;
|
||||
|
@ -5781,11 +5781,11 @@ in
|
|||
};
|
||||
});
|
||||
|
||||
"registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" = overridableMkRustCrate (profileName: rec {
|
||||
"registry+https://github.com/rust-lang/crates.io-index".time."0.3.34" = overridableMkRustCrate (profileName: rec {
|
||||
name = "time";
|
||||
version = "0.3.36";
|
||||
version = "0.3.34";
|
||||
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
||||
src = fetchCratesIo { inherit name version; sha256 = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"; };
|
||||
src = fetchCratesIo { inherit name version; sha256 = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"; };
|
||||
features = builtins.concatLists [
|
||||
[ "alloc" ]
|
||||
[ "default" ]
|
||||
|
@ -5798,7 +5798,7 @@ in
|
|||
powerfmt = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".powerfmt."0.2.0" { inherit profileName; }).out;
|
||||
serde = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.196" { inherit profileName; }).out;
|
||||
time_core = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".time-core."0.1.2" { inherit profileName; }).out;
|
||||
time_macros = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".time-macros."0.2.18" { profileName = "__noProfile"; }).out;
|
||||
time_macros = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".time-macros."0.2.17" { profileName = "__noProfile"; }).out;
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -5809,11 +5809,11 @@ in
|
|||
src = fetchCratesIo { inherit name version; sha256 = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"; };
|
||||
});
|
||||
|
||||
"registry+https://github.com/rust-lang/crates.io-index".time-macros."0.2.18" = overridableMkRustCrate (profileName: rec {
|
||||
"registry+https://github.com/rust-lang/crates.io-index".time-macros."0.2.17" = overridableMkRustCrate (profileName: rec {
|
||||
name = "time-macros";
|
||||
version = "0.2.18";
|
||||
version = "0.2.17";
|
||||
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
||||
src = fetchCratesIo { inherit name version; sha256 = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"; };
|
||||
src = fetchCratesIo { inherit name version; sha256 = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"; };
|
||||
features = builtins.concatLists [
|
||||
[ "parsing" ]
|
||||
];
|
||||
|
|
18
Cargo.toml
18
Cargo.toml
|
@ -21,15 +21,15 @@ default-members = ["src/garage"]
|
|||
|
||||
# Internal Garage crates
|
||||
format_table = { version = "0.1.1", path = "src/format-table" }
|
||||
garage_api = { version = "1.0.1", path = "src/api" }
|
||||
garage_block = { version = "1.0.1", path = "src/block" }
|
||||
garage_db = { version = "1.0.1", path = "src/db", default-features = false }
|
||||
garage_model = { version = "1.0.1", path = "src/model", default-features = false }
|
||||
garage_net = { version = "1.0.1", path = "src/net" }
|
||||
garage_rpc = { version = "1.0.1", path = "src/rpc" }
|
||||
garage_table = { version = "1.0.1", path = "src/table" }
|
||||
garage_util = { version = "1.0.1", path = "src/util" }
|
||||
garage_web = { version = "1.0.1", path = "src/web" }
|
||||
garage_api = { version = "1.0.0", path = "src/api" }
|
||||
garage_block = { version = "1.0.0", path = "src/block" }
|
||||
garage_db = { version = "1.0.0", path = "src/db", default-features = false }
|
||||
garage_model = { version = "1.0.0", path = "src/model", default-features = false }
|
||||
garage_net = { version = "1.0.0", path = "src/net" }
|
||||
garage_rpc = { version = "1.0.0", path = "src/rpc" }
|
||||
garage_table = { version = "1.0.0", path = "src/table" }
|
||||
garage_util = { version = "1.0.0", path = "src/util" }
|
||||
garage_web = { version = "1.0.0", path = "src/web" }
|
||||
k2v-client = { version = "0.0.4", path = "src/k2v-client" }
|
||||
|
||||
# External crates from crates.io
|
||||
|
|
|
@ -23,7 +23,7 @@ client = minio.Minio(
|
|||
"GKyourapikey",
|
||||
"abcd[...]1234",
|
||||
# Force the region, this is specific to garage
|
||||
region="garage",
|
||||
region="region",
|
||||
)
|
||||
```
|
||||
|
||||
|
|
|
@ -335,7 +335,6 @@ From the [official Mastodon documentation](https://docs.joinmastodon.org/admin/t
|
|||
|
||||
```bash
|
||||
$ RAILS_ENV=production bin/tootctl media remove --days 3
|
||||
$ RAILS_ENV=production bin/tootctl media remove --days 15 --prune-profiles
|
||||
$ RAILS_ENV=production bin/tootctl media remove-orphans
|
||||
$ RAILS_ENV=production bin/tootctl preview_cards remove --days 15
|
||||
```
|
||||
|
@ -354,6 +353,8 @@ Imports: 1.7 KB
|
|||
Settings: 0 Bytes
|
||||
```
|
||||
|
||||
Unfortunately, [old avatars and headers cannot currently be cleaned up](https://github.com/mastodon/mastodon/issues/9567).
|
||||
|
||||
### Migrating your data
|
||||
|
||||
Data migration should be done with an efficient S3 client.
|
||||
|
|
|
@ -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.0.1`) and not the `latest` tag.
|
||||
For this example, we will use the latest published version at the time of the writing which is `v1.0.1` but it's up to you
|
||||
We encourage you to use a fixed tag (eg. `v1.0.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.0.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.0.1
|
||||
sudo docker pull dxflrs/garage:v1.0.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.0.1
|
||||
dxflrs/garage:v1.0.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.0.1
|
||||
image: dxflrs/garage:v1.0.0
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
|
|
|
@ -50,20 +50,3 @@ locations. They use Garage themselves for the following tasks:
|
|||
|
||||
The Deuxfleurs Garage cluster is a multi-site cluster currently composed of
|
||||
9 nodes in 3 physical locations.
|
||||
|
||||
### Triplebit
|
||||
|
||||
[Triplebit](https://www.triplebit.org) is a non-profit hosting provider and
|
||||
ISP focused on improving access to privacy-related services. They use
|
||||
Garage themselves for the following tasks:
|
||||
|
||||
- Hosting of their homepage, [privacyguides.org](https://www.privacyguides.org/), and various other static sites
|
||||
|
||||
- As a Mastodon object storage backend for [mstdn.party](https://mstdn.party/) and [mstdn.plus](https://mstdn.plus/)
|
||||
|
||||
- As a PeerTube storage backend for [neat.tube](https://neat.tube/)
|
||||
|
||||
- As a [Matrix media backend](https://github.com/matrix-org/synapse-s3-storage-provider)
|
||||
|
||||
Triplebit's Garage cluster is a multi-site cluster currently composed of
|
||||
10 nodes in 3 physical locations.
|
||||
|
|
|
@ -5,7 +5,7 @@ weight = 40
|
|||
|
||||
Garage is meant to work on old, second-hand hardware.
|
||||
In particular, this makes it likely that some of your drives will fail, and some manual intervention will be needed.
|
||||
Fear not! Garage is fully equipped to handle drive failures, in most common cases.
|
||||
Fear not! For Garage is fully equipped to handle drive failures, in most common cases.
|
||||
|
||||
## A note on availability of Garage
|
||||
|
||||
|
@ -61,7 +61,7 @@ garage repair -a --yes blocks
|
|||
|
||||
This will re-synchronize blocks of data that are missing to the new HDD, reading them from copies located on other nodes.
|
||||
|
||||
You can check on the advancement of this process by doing the following command:
|
||||
You can check on the advancement of this process by doing the following command:
|
||||
|
||||
```bash
|
||||
garage stats -a
|
||||
|
|
|
@ -42,13 +42,6 @@ If a binary of the last version is not available for your architecture,
|
|||
or if you want a build customized for your system,
|
||||
you can [build Garage from source](@/documentation/cookbook/from-source.md).
|
||||
|
||||
If none of these option work for you, you can also run Garage in a Docker
|
||||
container. When using Docker, the commands used in this guide will not work
|
||||
anymore. We recommend reading the tutorial on [configuring a
|
||||
multi-node cluster](@/documentation/cookbook/real-world.md) to learn about
|
||||
using Garage as a Docker container. For simplicity, a minimal command to launch
|
||||
Garage using Docker is provided in this quick start guide as well.
|
||||
|
||||
|
||||
## Configuring and starting Garage
|
||||
|
||||
|
@ -92,9 +85,6 @@ metrics_token = "$(openssl rand -base64 32)"
|
|||
EOF
|
||||
```
|
||||
|
||||
See the [Configuration file format](https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/)
|
||||
for complete options and values.
|
||||
|
||||
Now that your configuration file has been created, you may save it to the directory of your choice.
|
||||
By default, Garage looks for **`/etc/garage.toml`.**
|
||||
You can also store it somewhere else, but you will have to specify `-c path/to/garage.toml`
|
||||
|
@ -121,26 +111,6 @@ garage -c path/to/garage.toml server
|
|||
|
||||
If you have placed the `garage.toml` file in `/etc` (its default location), you can simply run `garage server`.
|
||||
|
||||
Alternatively, if you cannot or do not wish to run the Garage binary directly,
|
||||
you may use Docker to run Garage in a container using the following command:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-d \
|
||||
--name garaged \
|
||||
-p 3900:3900 -p 3901:3901 -p 3902:3902 -p 3903:3903 \
|
||||
-v /etc/garage.toml:/path/to/garage.toml \
|
||||
-v /var/lib/garage/meta:/path/to/garage/meta \
|
||||
-v /var/lib/garage/data:/path/to/garage/data \
|
||||
dxflrs/garage:v0.9.4
|
||||
```
|
||||
|
||||
Under Linux, you can substitute `--network host` for `-p 3900:3900 -p 3901:3901 -p 3902:3902 -p 3903:3903`
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
Ensure your configuration file, `metadata_dir` and `data_dir` are readable by the user running the `garage` server or Docker.
|
||||
|
||||
You can tune Garage's verbosity by setting the `RUST_LOG=` environment variable. \
|
||||
Available log levels are (from less verbose to more verbose): `error`, `warn`, `info` *(default)*, `debug` and `trace`.
|
||||
|
||||
|
@ -161,9 +131,6 @@ It uses values from the TOML configuration file to find the Garage daemon runnin
|
|||
local node, therefore if your configuration file is not at `/etc/garage.toml` you will
|
||||
again have to specify `-c path/to/garage.toml` at each invocation.
|
||||
|
||||
If you are running Garage in a Docker container, you can set `alias garage="docker exec -ti <container name> /garage"`
|
||||
to use the Garage binary inside your container.
|
||||
|
||||
If the `garage` CLI is able to correctly detect the parameters of your local Garage node,
|
||||
the following command should be enough to show the status of your cluster:
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ Example response body:
|
|||
```json
|
||||
{
|
||||
"node": "b10c110e4e854e5aa3f4637681befac755154b20059ec163254ddbfae86b09df",
|
||||
"garageVersion": "v1.0.1",
|
||||
"garageVersion": "v1.0.0",
|
||||
"garageFeatures": [
|
||||
"k2v",
|
||||
"lmdb",
|
||||
|
|
84
flake.lock
84
flake.lock
|
@ -28,11 +28,11 @@
|
|||
},
|
||||
"flake-compat": {
|
||||
"locked": {
|
||||
"lastModified": 1717312683,
|
||||
"narHash": "sha256-FrlieJH50AuvagamEvWMIE6D2OAnERuDboFDYAED/dE=",
|
||||
"lastModified": 1688025799,
|
||||
"narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=",
|
||||
"owner": "nix-community",
|
||||
"repo": "flake-compat",
|
||||
"rev": "38fd3954cf65ce6faf3d0d45cd26059e059f07ea",
|
||||
"rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -42,12 +42,33 @@
|
|||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1659877975,
|
||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||
"lastModified": 1681202837,
|
||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1681202837,
|
||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -58,11 +79,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1724395761,
|
||||
"narHash": "sha256-zRkDV/nbrnp3Y8oCADf5ETl1sDrdmAW6/bBVJ8EbIdQ=",
|
||||
"lastModified": 1682109806,
|
||||
"narHash": "sha256-d9g7RKNShMLboTWwukM+RObDWWpHKaqTYXB48clBWXI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ae815cee91b417be55d43781eb4b73ae1ecc396c",
|
||||
"rev": "2362848adf8def2866fabbffc50462e929d7fffb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -74,17 +95,17 @@
|
|||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1724681257,
|
||||
"narHash": "sha256-EJRuc5Qp7yfXko5ZNeEMYAs4DzAvkCyALuJ/tGllhN4=",
|
||||
"lastModified": 1707091808,
|
||||
"narHash": "sha256-LahKBAfGbY836gtpVNnWwBTIzN7yf/uYM/S0g393r0Y=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0239aeb2f82ea27ccd6b61582b8f7fb8750eeada",
|
||||
"rev": "9f2ee8c91ac42da3ae6c6a1d21555f283458247e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0239aeb2f82ea27ccd6b61582b8f7fb8750eeada",
|
||||
"rev": "9f2ee8c91ac42da3ae6c6a1d21555f283458247e",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
@ -101,14 +122,15 @@
|
|||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1724638882,
|
||||
"narHash": "sha256-ap2jIQi/FuUHR6HCht6ASWhoz8EiB99XmI8Esot38VE=",
|
||||
"lastModified": 1707271822,
|
||||
"narHash": "sha256-/DZsoPH5GBzOpVEGz5PgJ7vh8Q6TcrJq5u8FcBjqAfI=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "19b70f147b9c67a759e35824b241f1ed92e46694",
|
||||
"rev": "7a94fe7690d2bdfe1aab475382a505e14dc114a6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -116,6 +138,36 @@
|
|||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
description =
|
||||
"Garage, an S3-compatible distributed object store for self-hosted deployments";
|
||||
|
||||
# Nixpkgs 24.05 as of 2024-08-26 has rustc v1.77
|
||||
# Nixpkgs 23.11 as of 2024-02-07, has rustc v1.73
|
||||
inputs.nixpkgs.url =
|
||||
"github:NixOS/nixpkgs/0239aeb2f82ea27ccd6b61582b8f7fb8750eeada";
|
||||
"github:NixOS/nixpkgs/9f2ee8c91ac42da3ae6c6a1d21555f283458247e";
|
||||
|
||||
inputs.flake-compat.url = "github:nix-community/flake-compat";
|
||||
|
||||
|
@ -17,9 +17,9 @@
|
|||
# - rustc v1.66
|
||||
# url = "github:cargo2nix/cargo2nix/8fb57a670f7993bfc24099c33eb9c5abb51f29a2";
|
||||
|
||||
# Rust overlay as of 2024-08-26
|
||||
# Rust overlay as of 2024-02-07
|
||||
inputs.rust-overlay.url =
|
||||
"github:oxalica/rust-overlay/19b70f147b9c67a759e35824b241f1ed92e46694";
|
||||
"github:oxalica/rust-overlay/7a94fe7690d2bdfe1aab475382a505e14dc114a6";
|
||||
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.flake-compat.follows = "flake-compat";
|
||||
|
|
|
@ -20,7 +20,7 @@ let
|
|||
};
|
||||
|
||||
toolchainOptions = {
|
||||
rustVersion = "1.77.0";
|
||||
rustVersion = "1.73.0";
|
||||
extraRustComponents = [ "clippy" ];
|
||||
};
|
||||
|
||||
|
|
|
@ -15,10 +15,10 @@ type: application
|
|||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.5.1
|
||||
version: 0.5.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "v1.0.1"
|
||||
appVersion: "v1.0.0"
|
||||
|
|
|
@ -76,9 +76,6 @@ spec:
|
|||
- name: etc
|
||||
mountPath: /etc/garage.toml
|
||||
subPath: garage.toml
|
||||
{{- with .Values.extraVolumeMounts }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
# TODO
|
||||
# livenessProbe:
|
||||
# httpGet:
|
||||
|
@ -113,9 +110,6 @@ spec:
|
|||
- name: data
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- with .Values.extraVolumes }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
|
|
|
@ -218,10 +218,6 @@ affinity: {}
|
|||
|
||||
environment: {}
|
||||
|
||||
extraVolumes: {}
|
||||
|
||||
extraVolumeMounts: {}
|
||||
|
||||
monitoring:
|
||||
metrics:
|
||||
# If true, a service for monitoring is created with a prometheus.io/scrape annotation
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_api"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -2,7 +2,6 @@ use std::convert::Infallible;
|
|||
use std::fs::{self, Permissions};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
|
@ -20,7 +19,6 @@ use hyper_util::rt::TokioIo;
|
|||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::net::{TcpListener, TcpStream, UnixListener, UnixStream};
|
||||
use tokio::sync::watch;
|
||||
use tokio::time::{sleep_until, Instant};
|
||||
|
||||
use opentelemetry::{
|
||||
global,
|
||||
|
@ -293,7 +291,7 @@ where
|
|||
let connection_collector = tokio::spawn({
|
||||
let server_name = server_name.clone();
|
||||
async move {
|
||||
let mut connections = FuturesUnordered::<tokio::task::JoinHandle<()>>::new();
|
||||
let mut connections = FuturesUnordered::new();
|
||||
loop {
|
||||
let collect_next = async {
|
||||
if connections.is_empty() {
|
||||
|
@ -314,34 +312,23 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
let deadline = Instant::now() + Duration::from_secs(10);
|
||||
while !connections.is_empty() {
|
||||
if !connections.is_empty() {
|
||||
info!(
|
||||
"{} server: {} connections still open, deadline in {:.2}s",
|
||||
"{} server: {} connections still open",
|
||||
server_name,
|
||||
connections.len(),
|
||||
(deadline - Instant::now()).as_secs_f32(),
|
||||
connections.len()
|
||||
);
|
||||
tokio::select! {
|
||||
conn_res = connections.next() => {
|
||||
trace!(
|
||||
"{} server: HTTP connection finished: {:?}",
|
||||
server_name,
|
||||
conn_res.unwrap(),
|
||||
);
|
||||
}
|
||||
_ = sleep_until(deadline) => {
|
||||
warn!("{} server: exit deadline reached with {} connections still open, killing them now",
|
||||
server_name,
|
||||
connections.len());
|
||||
for conn in connections.iter() {
|
||||
conn.abort();
|
||||
}
|
||||
for conn in connections {
|
||||
assert!(conn.await.unwrap_err().is_cancelled());
|
||||
}
|
||||
break;
|
||||
}
|
||||
while let Some(conn_res) = connections.next().await {
|
||||
trace!(
|
||||
"{} server: HTTP connection finished: {:?}",
|
||||
server_name,
|
||||
conn_res
|
||||
);
|
||||
info!(
|
||||
"{} server: {} connections still open",
|
||||
server_name,
|
||||
connections.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,11 +71,21 @@ pub async fn handle_post_object(
|
|||
}
|
||||
|
||||
if let Ok(content) = HeaderValue::from_str(&field.text().await?) {
|
||||
if params.insert(&name, content).is_some() {
|
||||
return Err(Error::bad_request(format!(
|
||||
"Field '{}' provided more than once",
|
||||
name
|
||||
)));
|
||||
match name.as_str() {
|
||||
"tag" => (/* tag need to be reencoded, but we don't support them yet anyway */),
|
||||
"acl" => {
|
||||
if params.insert("x-amz-acl", content).is_some() {
|
||||
return Err(Error::bad_request("Field 'acl' provided more than once"));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if params.insert(&name, content).is_some() {
|
||||
return Err(Error::bad_request(format!(
|
||||
"Field '{}' provided more than once",
|
||||
name
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -212,8 +222,6 @@ pub async fn handle_post_object(
|
|||
)));
|
||||
}
|
||||
|
||||
// if we ever start supporting ACLs, we likely want to map "acl" to x-amz-acl" somewhere
|
||||
// arround here to make sure the rest of the machinery takes our acl into account.
|
||||
let headers = get_headers(¶ms)?;
|
||||
|
||||
let expected_checksums = ExpectedChecksums {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_block"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_db"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -211,12 +211,16 @@ impl Tree {
|
|||
|
||||
/// Returns the old value if there was one
|
||||
#[inline]
|
||||
pub fn insert<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<()> {
|
||||
pub fn insert<T: AsRef<[u8]>, U: AsRef<[u8]>>(
|
||||
&self,
|
||||
key: T,
|
||||
value: U,
|
||||
) -> Result<Option<Value>> {
|
||||
self.0.insert(self.1, key.as_ref(), value.as_ref())
|
||||
}
|
||||
/// Returns the old value if there was one
|
||||
#[inline]
|
||||
pub fn remove<T: AsRef<[u8]>>(&self, key: T) -> Result<()> {
|
||||
pub fn remove<T: AsRef<[u8]>>(&self, key: T) -> Result<Option<Value>> {
|
||||
self.0.remove(self.1, key.as_ref())
|
||||
}
|
||||
/// Clears all values from the tree
|
||||
|
@ -274,12 +278,12 @@ impl<'a> Transaction<'a> {
|
|||
tree: &Tree,
|
||||
key: T,
|
||||
value: U,
|
||||
) -> TxOpResult<()> {
|
||||
) -> TxOpResult<Option<Value>> {
|
||||
self.tx.insert(tree.1, key.as_ref(), value.as_ref())
|
||||
}
|
||||
/// Returns the old value if there was one
|
||||
#[inline]
|
||||
pub fn remove<T: AsRef<[u8]>>(&mut self, tree: &Tree, key: T) -> TxOpResult<()> {
|
||||
pub fn remove<T: AsRef<[u8]>>(&mut self, tree: &Tree, key: T) -> TxOpResult<Option<Value>> {
|
||||
self.tx.remove(tree.1, key.as_ref())
|
||||
}
|
||||
/// Clears all values in a tree
|
||||
|
@ -335,8 +339,8 @@ pub(crate) trait IDb: Send + Sync {
|
|||
fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>;
|
||||
fn len(&self, tree: usize) -> Result<usize>;
|
||||
|
||||
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>;
|
||||
fn remove(&self, tree: usize, key: &[u8]) -> Result<()>;
|
||||
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>>;
|
||||
fn remove(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>;
|
||||
fn clear(&self, tree: usize) -> Result<()>;
|
||||
|
||||
fn iter(&self, tree: usize) -> Result<ValueIter<'_>>;
|
||||
|
@ -362,8 +366,8 @@ pub(crate) trait ITx {
|
|||
fn get(&self, tree: usize, key: &[u8]) -> TxOpResult<Option<Value>>;
|
||||
fn len(&self, tree: usize) -> TxOpResult<usize>;
|
||||
|
||||
fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> TxOpResult<()>;
|
||||
fn remove(&mut self, tree: usize, key: &[u8]) -> TxOpResult<()>;
|
||||
fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> TxOpResult<Option<Value>>;
|
||||
fn remove(&mut self, tree: usize, key: &[u8]) -> TxOpResult<Option<Value>>;
|
||||
fn clear(&mut self, tree: usize) -> TxOpResult<()>;
|
||||
|
||||
fn iter(&self, tree: usize) -> TxOpResult<TxValueIter<'_>>;
|
||||
|
|
|
@ -132,20 +132,22 @@ impl IDb for LmdbDb {
|
|||
Ok(tree.len(&tx)?.try_into().unwrap())
|
||||
}
|
||||
|
||||
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
|
||||
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>> {
|
||||
let tree = self.get_tree(tree)?;
|
||||
let mut tx = self.db.write_txn()?;
|
||||
let old_val = tree.get(&tx, key)?.map(Vec::from);
|
||||
tree.put(&mut tx, key, value)?;
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
Ok(old_val)
|
||||
}
|
||||
|
||||
fn remove(&self, tree: usize, key: &[u8]) -> Result<()> {
|
||||
fn remove(&self, tree: usize, key: &[u8]) -> Result<Option<Value>> {
|
||||
let tree = self.get_tree(tree)?;
|
||||
let mut tx = self.db.write_txn()?;
|
||||
let old_val = tree.get(&tx, key)?.map(Vec::from);
|
||||
tree.delete(&mut tx, key)?;
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
Ok(old_val)
|
||||
}
|
||||
|
||||
fn clear(&self, tree: usize) -> Result<()> {
|
||||
|
@ -252,15 +254,17 @@ impl<'a> ITx for LmdbTx<'a> {
|
|||
Ok(tree.len(&self.tx)? as usize)
|
||||
}
|
||||
|
||||
fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> TxOpResult<()> {
|
||||
fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> TxOpResult<Option<Value>> {
|
||||
let tree = *self.get_tree(tree)?;
|
||||
let old_val = tree.get(&self.tx, key)?.map(Vec::from);
|
||||
tree.put(&mut self.tx, key, value)?;
|
||||
Ok(())
|
||||
Ok(old_val)
|
||||
}
|
||||
fn remove(&mut self, tree: usize, key: &[u8]) -> TxOpResult<()> {
|
||||
fn remove(&mut self, tree: usize, key: &[u8]) -> TxOpResult<Option<Value>> {
|
||||
let tree = *self.get_tree(tree)?;
|
||||
let old_val = tree.get(&self.tx, key)?.map(Vec::from);
|
||||
tree.delete(&mut self.tx, key)?;
|
||||
Ok(())
|
||||
Ok(old_val)
|
||||
}
|
||||
fn clear(&mut self, tree: usize) -> TxOpResult<()> {
|
||||
let tree = *self.get_tree(tree)?;
|
||||
|
|
|
@ -169,7 +169,7 @@ impl IDb for SqliteDb {
|
|||
}
|
||||
}
|
||||
|
||||
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
|
||||
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>> {
|
||||
let tree = self.get_tree(tree)?;
|
||||
let db = self.db.get()?;
|
||||
let lock = self.write_lock.lock();
|
||||
|
@ -184,18 +184,23 @@ impl IDb for SqliteDb {
|
|||
assert_eq!(n, 1);
|
||||
|
||||
drop(lock);
|
||||
Ok(())
|
||||
Ok(old_val)
|
||||
}
|
||||
|
||||
fn remove(&self, tree: usize, key: &[u8]) -> Result<()> {
|
||||
fn remove(&self, tree: usize, key: &[u8]) -> Result<Option<Value>> {
|
||||
let tree = self.get_tree(tree)?;
|
||||
let db = self.db.get()?;
|
||||
let lock = self.write_lock.lock();
|
||||
|
||||
db.execute(&format!("DELETE FROM {} WHERE k = ?1", tree), params![key])?;
|
||||
let old_val = self.internal_get(&db, &tree, key)?;
|
||||
|
||||
if old_val.is_some() {
|
||||
let n = db.execute(&format!("DELETE FROM {} WHERE k = ?1", tree), params![key])?;
|
||||
assert_eq!(n, 1);
|
||||
}
|
||||
|
||||
drop(lock);
|
||||
Ok(())
|
||||
Ok(old_val)
|
||||
}
|
||||
|
||||
fn clear(&self, tree: usize) -> Result<()> {
|
||||
|
@ -336,17 +341,31 @@ impl<'a> ITx for SqliteTx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> TxOpResult<()> {
|
||||
fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> TxOpResult<Option<Value>> {
|
||||
let tree = self.get_tree(tree)?;
|
||||
let sql = format!("INSERT OR REPLACE INTO {} (k, v) VALUES (?1, ?2)", tree);
|
||||
self.tx.execute(&sql, params![key, value])?;
|
||||
Ok(())
|
||||
let old_val = self.internal_get(tree, key)?;
|
||||
|
||||
let sql = match &old_val {
|
||||
Some(_) => format!("UPDATE {} SET v = ?2 WHERE k = ?1", tree),
|
||||
None => format!("INSERT INTO {} (k, v) VALUES (?1, ?2)", tree),
|
||||
};
|
||||
let n = self.tx.execute(&sql, params![key, value])?;
|
||||
assert_eq!(n, 1);
|
||||
|
||||
Ok(old_val)
|
||||
}
|
||||
fn remove(&mut self, tree: usize, key: &[u8]) -> TxOpResult<()> {
|
||||
fn remove(&mut self, tree: usize, key: &[u8]) -> TxOpResult<Option<Value>> {
|
||||
let tree = self.get_tree(tree)?;
|
||||
self.tx
|
||||
.execute(&format!("DELETE FROM {} WHERE k = ?1", tree), params![key])?;
|
||||
Ok(())
|
||||
let old_val = self.internal_get(tree, key)?;
|
||||
|
||||
if old_val.is_some() {
|
||||
let n = self
|
||||
.tx
|
||||
.execute(&format!("DELETE FROM {} WHERE k = ?1", tree), params![key])?;
|
||||
assert_eq!(n, 1);
|
||||
}
|
||||
|
||||
Ok(old_val)
|
||||
}
|
||||
fn clear(&mut self, tree: usize) -> TxOpResult<()> {
|
||||
let tree = self.get_tree(tree)?;
|
||||
|
|
|
@ -12,7 +12,7 @@ fn test_suite(db: Db) {
|
|||
|
||||
// ---- test simple insert/delete ----
|
||||
|
||||
assert!(tree.insert(ka, va).is_ok());
|
||||
assert!(tree.insert(ka, va).unwrap().is_none());
|
||||
assert_eq!(tree.get(ka).unwrap().unwrap(), va);
|
||||
assert_eq!(tree.len().unwrap(), 1);
|
||||
|
||||
|
@ -21,7 +21,7 @@ fn test_suite(db: Db) {
|
|||
let res = db.transaction::<_, (), _>(|tx| {
|
||||
assert_eq!(tx.get(&tree, ka).unwrap().unwrap(), va);
|
||||
|
||||
assert_eq!(tx.insert(&tree, ka, vb).unwrap(), ());
|
||||
assert_eq!(tx.insert(&tree, ka, vb).unwrap().unwrap(), va);
|
||||
|
||||
assert_eq!(tx.get(&tree, ka).unwrap().unwrap(), vb);
|
||||
|
||||
|
@ -33,7 +33,7 @@ fn test_suite(db: Db) {
|
|||
let res = db.transaction::<(), _, _>(|tx| {
|
||||
assert_eq!(tx.get(&tree, ka).unwrap().unwrap(), vb);
|
||||
|
||||
assert_eq!(tx.insert(&tree, ka, vc).unwrap(), ());
|
||||
assert_eq!(tx.insert(&tree, ka, vc).unwrap().unwrap(), vb);
|
||||
|
||||
assert_eq!(tx.get(&tree, ka).unwrap().unwrap(), vc);
|
||||
|
||||
|
@ -50,7 +50,7 @@ fn test_suite(db: Db) {
|
|||
assert!(iter.next().is_none());
|
||||
drop(iter);
|
||||
|
||||
assert!(tree.insert(kb, vc).is_ok());
|
||||
assert!(tree.insert(kb, vc).unwrap().is_none());
|
||||
assert_eq!(tree.get(kb).unwrap().unwrap(), vc);
|
||||
|
||||
let mut iter = tree.iter().unwrap();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -24,7 +24,6 @@ pub struct ConvertDbOpt {
|
|||
output_engine: Engine,
|
||||
|
||||
#[structopt(flatten)]
|
||||
#[allow(dead_code)]
|
||||
db_open: OpenDbOpt,
|
||||
}
|
||||
|
||||
|
@ -53,7 +52,6 @@ pub(crate) fn do_conversion(args: ConvertDbOpt) -> Result<()> {
|
|||
}
|
||||
|
||||
let opt = OpenOpt {
|
||||
#[cfg(feature = "lmdb")]
|
||||
lmdb_map_size: args.db_open.lmdb.map_size.map(|x| x.as_u64() as usize),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
@ -358,7 +358,7 @@ pub async fn cmd_layout_history(
|
|||
|
||||
if layout.versions.len() > 1 {
|
||||
println!("==== UPDATE TRACKERS ====");
|
||||
println!("Several layout versions are currently live in the cluster, and data is being migrated.");
|
||||
println!("Several layout versions are currently live in the version, and data is being migrated.");
|
||||
println!(
|
||||
"This is the internal data that Garage stores to know which nodes have what data."
|
||||
);
|
||||
|
@ -377,27 +377,15 @@ pub async fn cmd_layout_history(
|
|||
table[1..].sort();
|
||||
format_table(table);
|
||||
|
||||
let min_ack = layout
|
||||
.update_trackers
|
||||
.ack_map
|
||||
.min_among(&all_nodes, layout.min_stored());
|
||||
|
||||
println!();
|
||||
println!(
|
||||
"If some nodes are not catching up to the latest layout version in the update trackers,"
|
||||
);
|
||||
println!("it might be because they are offline or unable to complete a sync successfully.");
|
||||
if min_ack < layout.current().version {
|
||||
println!(
|
||||
"You may force progress using `garage layout skip-dead-nodes --version {}`",
|
||||
layout.current().version
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"You may force progress using `garage layout skip-dead-nodes --version {} --allow-missing-data`.",
|
||||
layout.current().version
|
||||
);
|
||||
}
|
||||
println!(
|
||||
"You may force progress using `garage layout skip-dead-nodes --version {}`",
|
||||
layout.current().version
|
||||
);
|
||||
} else {
|
||||
println!("Your cluster is currently in a stable state with a single live layout version.");
|
||||
println!("No metadata migration is in progress. Note that the migration of data blocks is not tracked,");
|
||||
|
@ -438,15 +426,15 @@ pub async fn cmd_layout_skip_dead_nodes(
|
|||
let all_nodes = layout.get_all_nodes();
|
||||
let mut did_something = false;
|
||||
for node in all_nodes.iter() {
|
||||
// Update ACK tracker for dead nodes or for all nodes if --allow-missing-data
|
||||
if opt.allow_missing_data || !status.iter().any(|x| x.id == *node && x.is_up) {
|
||||
if layout.update_trackers.ack_map.set_max(*node, opt.version) {
|
||||
println!("Increased the ACK tracker for node {:?}", node);
|
||||
did_something = true;
|
||||
}
|
||||
if status.iter().any(|x| x.id == *node && x.is_up) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if layout.update_trackers.ack_map.set_max(*node, opt.version) {
|
||||
println!("Increased the ACK tracker for node {:?}", node);
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
// If --allow-missing-data, update SYNC tracker for all nodes.
|
||||
if opt.allow_missing_data {
|
||||
if layout.update_trackers.sync_map.set_max(*node, opt.version) {
|
||||
println!("Increased the SYNC tracker for node {:?}", node);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_model"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -141,7 +141,7 @@ impl Garage {
|
|||
)?)
|
||||
.ok()
|
||||
.and_then(|x| NetworkKey::from_slice(&x))
|
||||
.ok_or_message("Invalid RPC secret key: expected 32 bytes of random hex, please check the documentation for requirements")?;
|
||||
.ok_or_message("Invalid RPC secret key: expected 32 bits of entropy, please check the documentation for requirements")?;
|
||||
|
||||
let (replication_factor, consistency_mode) = parse_replication_mode(&config)?;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_net"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_rpc"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -227,29 +227,24 @@ impl LayoutHistory {
|
|||
// ================== updates to layout, public interface ===================
|
||||
|
||||
pub fn merge(&mut self, other: &LayoutHistory) -> bool {
|
||||
// If our current layout version is completely out-of-date,
|
||||
// forget everything we know and replace it by incoming layout data.
|
||||
if self.current().version < other.min_stored() {
|
||||
*self = other.clone();
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut changed = false;
|
||||
|
||||
// Add any new versions to history
|
||||
for v2 in other.versions.iter() {
|
||||
if v2.version == self.current().version + 1 {
|
||||
// This is the next version, add it to our version list
|
||||
self.versions.push(v2.clone());
|
||||
changed = true;
|
||||
} else if let Some(v1) = self.versions.iter().find(|v| v.version == v2.version) {
|
||||
if let Some(v1) = self.versions.iter().find(|v| v.version == v2.version) {
|
||||
// Version is already present, check consistency
|
||||
if v1 != v2 {
|
||||
error!("Inconsistent layout histories: different layout compositions for version {}. Your cluster will be broken as long as this layout version is not replaced.", v2.version);
|
||||
}
|
||||
} else if self.versions.iter().all(|v| v.version != v2.version - 1) {
|
||||
error!(
|
||||
"Cannot receive new layout version {}, version {} is missing",
|
||||
v2.version,
|
||||
v2.version - 1
|
||||
);
|
||||
} else {
|
||||
// This is an older version
|
||||
assert!(v2.version < self.min_stored());
|
||||
self.versions.push(v2.clone());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -455,7 +455,7 @@ impl UpdateTracker {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn min_among(&self, storage_nodes: &[Uuid], min_version: u64) -> u64 {
|
||||
pub(crate) fn min_among(&self, storage_nodes: &[Uuid], min_version: u64) -> u64 {
|
||||
storage_nodes
|
||||
.iter()
|
||||
.map(|x| self.get(x, min_version))
|
||||
|
|
|
@ -4,6 +4,8 @@ use opentelemetry::{global, metrics::*};
|
|||
pub struct RpcMetrics {
|
||||
pub(crate) rpc_counter: Counter<u64>,
|
||||
pub(crate) rpc_timeout_counter: Counter<u64>,
|
||||
pub(crate) rpc_watchdogs_started_counter: Counter<u64>,
|
||||
pub(crate) rpc_watchdogs_preemption_counter: Counter<u64>,
|
||||
pub(crate) rpc_netapp_error_counter: Counter<u64>,
|
||||
pub(crate) rpc_garage_error_counter: Counter<u64>,
|
||||
|
||||
|
@ -21,6 +23,14 @@ impl RpcMetrics {
|
|||
.u64_counter("rpc.timeout_counter")
|
||||
.with_description("Number of RPC timeouts")
|
||||
.init(),
|
||||
rpc_watchdogs_started_counter: meter
|
||||
.u64_counter("rpc.watchdogs_started_counter")
|
||||
.with_description("Number of RPC requests started with a watchdog")
|
||||
.init(),
|
||||
rpc_watchdogs_preemption_counter: meter
|
||||
.u64_counter("rpc.watchdogs_preemption_counter")
|
||||
.with_description("Number of RPC watchdogs which timed out and caused an extra RPC to be scheduled")
|
||||
.init(),
|
||||
rpc_netapp_error_counter: meter
|
||||
.u64_counter("rpc.netapp_error_counter")
|
||||
.with_description("Number of communication errors (errors in the Netapp library)")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Contain structs related to making RPCs
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -38,6 +38,8 @@ pub struct RequestStrategy<T> {
|
|||
rs_quorum: Option<usize>,
|
||||
/// Send all requests at once
|
||||
rs_send_all_at_once: Option<bool>,
|
||||
/// Start with enough RPCs to reach quorum, but send extras when some take too long
|
||||
rs_preemptive_send: Option<bool>,
|
||||
/// Request priority
|
||||
rs_priority: RequestPriority,
|
||||
/// Custom timeout for this request
|
||||
|
@ -58,6 +60,7 @@ impl Clone for RequestStrategy<()> {
|
|||
RequestStrategy {
|
||||
rs_quorum: self.rs_quorum,
|
||||
rs_send_all_at_once: self.rs_send_all_at_once,
|
||||
rs_preemptive_send: self.rs_preemptive_send,
|
||||
rs_priority: self.rs_priority,
|
||||
rs_timeout: self.rs_timeout,
|
||||
rs_drop_on_complete: (),
|
||||
|
@ -71,6 +74,7 @@ impl RequestStrategy<()> {
|
|||
RequestStrategy {
|
||||
rs_quorum: None,
|
||||
rs_send_all_at_once: None,
|
||||
rs_preemptive_send: None,
|
||||
rs_priority: prio,
|
||||
rs_timeout: Timeout::Default,
|
||||
rs_drop_on_complete: (),
|
||||
|
@ -81,6 +85,7 @@ impl RequestStrategy<()> {
|
|||
RequestStrategy {
|
||||
rs_quorum: self.rs_quorum,
|
||||
rs_send_all_at_once: self.rs_send_all_at_once,
|
||||
rs_preemptive_send: self.rs_preemptive_send,
|
||||
rs_priority: self.rs_priority,
|
||||
rs_timeout: self.rs_timeout,
|
||||
rs_drop_on_complete: drop_on_complete,
|
||||
|
@ -94,11 +99,16 @@ impl<T> RequestStrategy<T> {
|
|||
self.rs_quorum = Some(quorum);
|
||||
self
|
||||
}
|
||||
/// Set quorum to be reached for request
|
||||
/// Set flag to send all requests at once
|
||||
pub fn send_all_at_once(mut self, value: bool) -> Self {
|
||||
self.rs_send_all_at_once = Some(value);
|
||||
self
|
||||
}
|
||||
/// Set flag to preemptively send extra requests after some wait
|
||||
pub fn with_preemptive_send(mut self, value: bool) -> Self {
|
||||
self.rs_preemptive_send = Some(value);
|
||||
self
|
||||
}
|
||||
/// Deactivate timeout for this request
|
||||
pub fn without_timeout(mut self) -> Self {
|
||||
self.rs_timeout = Timeout::None;
|
||||
|
@ -115,6 +125,7 @@ impl<T> RequestStrategy<T> {
|
|||
RequestStrategy {
|
||||
rs_quorum: self.rs_quorum,
|
||||
rs_send_all_at_once: self.rs_send_all_at_once,
|
||||
rs_preemptive_send: self.rs_preemptive_send,
|
||||
rs_priority: self.rs_priority,
|
||||
rs_timeout: self.rs_timeout,
|
||||
rs_drop_on_complete: (),
|
||||
|
@ -335,29 +346,53 @@ impl RpcHelper {
|
|||
S: Send + 'static,
|
||||
{
|
||||
// Once quorum is reached, other requests don't matter.
|
||||
// What we do here is only send the required number of requests
|
||||
// The default here is to only send the required number of requests
|
||||
// to reach a quorum, priorizing nodes with the lowest latency.
|
||||
// When there are errors, we start new requests to compensate.
|
||||
|
||||
// TODO: this could be made more aggressive, e.g. if after 2x the
|
||||
// average ping of a given request, the response is not yet received,
|
||||
// preemptively send an additional request to any remaining nodes.
|
||||
|
||||
// Reorder requests to priorize closeness / low latency
|
||||
let request_order =
|
||||
self.request_order(&self.0.layout.read().unwrap().current(), to.iter().copied());
|
||||
let layout_nodes_count = request_order.len();
|
||||
|
||||
// The send_all_at_once flag overrides the behaviour described
|
||||
// above and sends an RPC to every node from the get-go. This
|
||||
// is more demanding on the network but also offers the best
|
||||
// chance to reach quorum quickly.
|
||||
let send_all_at_once = strategy.rs_send_all_at_once.unwrap_or(false);
|
||||
|
||||
// The preemptive_send flag is an attempt at compromise: we
|
||||
// start by sending just enough to reach quorum, but associate
|
||||
// each RPC to a watchog which triggers after 2x the average
|
||||
// ping for that peer. When a watchdog triggers, an extra RPC
|
||||
// is sent as a preemptive "replacement" in case the slow node
|
||||
// is having serious trouble and can't reply. This is
|
||||
// overriden by send_all_at_once.
|
||||
let preemptive_send = strategy.rs_preemptive_send.unwrap_or(false);
|
||||
let mut preemptive_watchdogs = FuturesUnordered::new();
|
||||
let mut completed_node_ids = HashSet::<Uuid>::new();
|
||||
let metric_tags = [
|
||||
KeyValue::new("rpc_endpoint", endpoint.path().to_string()),
|
||||
KeyValue::new("from", format!("{:?}", self.0.our_node_id)),
|
||||
KeyValue::new("to", format!("{:?}", to)),
|
||||
];
|
||||
|
||||
// Build future for each request
|
||||
// They are not started now: they are added below in a FuturesUnordered
|
||||
// object that will take care of polling them (see below)
|
||||
let msg = msg.into_req().map_err(garage_net::error::Error::from)?;
|
||||
let mut requests = request_order.into_iter().map(|to| {
|
||||
let mut requests = request_order.into_iter().map(|(avg_ping, to)| {
|
||||
let self2 = self.clone();
|
||||
let msg = msg.clone();
|
||||
let endpoint2 = endpoint.clone();
|
||||
let strategy = strategy.clone();
|
||||
async move { self2.call(&endpoint2, to, msg, strategy).await }
|
||||
(
|
||||
async move { (to, self2.call(&endpoint2, to, msg, strategy).await) },
|
||||
preemptive_send.then_some(async move {
|
||||
tokio::time::sleep(2 * avg_ping).await;
|
||||
to
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
||||
// Vectors in which success results and errors will be collected
|
||||
|
@ -368,13 +403,26 @@ impl RpcHelper {
|
|||
// (for the moment none, they will be added in the loop below)
|
||||
let mut resp_stream = FuturesUnordered::new();
|
||||
|
||||
// The number of in-flight requests we want at the moment
|
||||
let mut target_outbound_count = if send_all_at_once {
|
||||
layout_nodes_count
|
||||
} else {
|
||||
quorum
|
||||
};
|
||||
|
||||
// Do some requests and collect results
|
||||
while successes.len() < quorum {
|
||||
// If the current set of requests that are running is not enough to possibly
|
||||
// reach quorum, start some new requests.
|
||||
while send_all_at_once || successes.len() + resp_stream.len() < quorum {
|
||||
if let Some(fut) = requests.next() {
|
||||
resp_stream.push(fut)
|
||||
// If our current outbound request count is not enough, start new ones
|
||||
while successes.len() + resp_stream.len() < target_outbound_count {
|
||||
if let Some((fut, watchdog)) = requests.next() {
|
||||
if let Some(sleep) = watchdog {
|
||||
preemptive_watchdogs.push(sleep);
|
||||
self.0
|
||||
.metrics
|
||||
.rpc_watchdogs_started_counter
|
||||
.add(1, &metric_tags);
|
||||
}
|
||||
resp_stream.push(fut);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -385,14 +433,45 @@ impl RpcHelper {
|
|||
break;
|
||||
}
|
||||
|
||||
// Wait for one request to terminate
|
||||
match resp_stream.next().await.unwrap() {
|
||||
Ok(msg) => {
|
||||
successes.push(msg);
|
||||
let response_or_watchdog = async {
|
||||
if preemptive_watchdogs.is_empty() {
|
||||
// We don't have any watchdogs to listen to, just wait for a request
|
||||
// This avoids waiting on a empty FuturesUnordered, which creates a busy wait
|
||||
resp_stream
|
||||
.next()
|
||||
.await
|
||||
.map(|(res, to)| WatchedRPCResult::Completed(res, to))
|
||||
} else {
|
||||
select! {
|
||||
opt = resp_stream.next() => opt.map(|(res, to)| WatchedRPCResult::Completed(res, to)),
|
||||
watchdog = preemptive_watchdogs.next() => watchdog.map(WatchedRPCResult::TimedOut),
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
errors.push(e);
|
||||
};
|
||||
|
||||
// Wait for the next completed request, or for a watchdog to trigger
|
||||
match response_or_watchdog.await {
|
||||
Some(WatchedRPCResult::Completed(to, res)) => {
|
||||
completed_node_ids.insert(to);
|
||||
match res {
|
||||
Ok(msg) => successes.push(msg),
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
}
|
||||
Some(WatchedRPCResult::TimedOut(to)) => {
|
||||
// A watchdog has triggered, meaning one of the active requests is taking too long
|
||||
// Note that we don't cancel watchdogs after requests complete, so we need to ignore those
|
||||
if target_outbound_count < layout_nodes_count
|
||||
&& !completed_node_ids.contains(&to)
|
||||
{
|
||||
target_outbound_count += 1;
|
||||
self.0
|
||||
.metrics
|
||||
.rpc_watchdogs_preemption_counter
|
||||
.add(1, &metric_tags);
|
||||
}
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -554,7 +633,7 @@ impl RpcHelper {
|
|||
continue;
|
||||
}
|
||||
let nodes = ver.nodes_of(position, ver.replication_factor);
|
||||
for node in rpc_helper.request_order(layout.current(), nodes) {
|
||||
for (_, node) in rpc_helper.request_order(layout.current(), nodes) {
|
||||
if !ret.contains(&node) {
|
||||
ret.push(node);
|
||||
}
|
||||
|
@ -567,7 +646,7 @@ impl RpcHelper {
|
|||
&self,
|
||||
layout: &LayoutVersion,
|
||||
nodes: impl Iterator<Item = Uuid>,
|
||||
) -> Vec<Uuid> {
|
||||
) -> Vec<(Duration, Uuid)> {
|
||||
// Retrieve some status variables that we will use to sort requests
|
||||
let peer_list = self.0.peering.get_peer_list();
|
||||
let our_zone = layout.get_node_zone(&self.0.our_node_id).unwrap_or("");
|
||||
|
@ -600,7 +679,7 @@ impl RpcHelper {
|
|||
|
||||
nodes
|
||||
.into_iter()
|
||||
.map(|(_, _, _, to)| to)
|
||||
.map(|(_, _, ping, to)| (ping, to))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
@ -709,3 +788,10 @@ where
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ------- utility for tracking RPC results and watchdog triggers --------
|
||||
|
||||
enum WatchedRPCResult<S> {
|
||||
Completed(Uuid, Result<S, Error>),
|
||||
TimedOut(Uuid),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_table"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -317,7 +317,8 @@ impl<F: TableSchema, R: TableReplication> Table<F, R> {
|
|||
&who,
|
||||
rpc,
|
||||
RequestStrategy::with_priority(PRIO_NORMAL)
|
||||
.with_quorum(self.data.replication.read_quorum()),
|
||||
.with_quorum(self.data.replication.read_quorum())
|
||||
.with_preemptive_send(true),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -412,7 +413,8 @@ impl<F: TableSchema, R: TableReplication> Table<F, R> {
|
|||
&who,
|
||||
rpc,
|
||||
RequestStrategy::with_priority(PRIO_NORMAL)
|
||||
.with_quorum(self.data.replication.read_quorum()),
|
||||
.with_quorum(self.data.replication.read_quorum())
|
||||
.with_preemptive_send(true),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_util"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "garage_web"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
|
Loading…
Reference in a new issue