Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
636 changes: 613 additions & 23 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ resolver = "2"
members = [
"crates/core",
"crates/cache",
"crates/backup-store",
"crates/engine",
"crates/storage",
"crates/config",
Expand All @@ -27,6 +28,7 @@ license = "Apache-2.0"
# Internal crates
extenddb-core = { path = "crates/core" }
extenddb-cache = { path = "crates/cache" }
extenddb-backup-store = { path = "crates/backup-store" }
extenddb-engine = { path = "crates/engine" }
extenddb-storage = { path = "crates/storage" }
extenddb-config = { path = "crates/config" }
Expand Down Expand Up @@ -72,6 +74,16 @@ mongodb = "3"
bson = "2.13"
dashmap = "6"

# Backup store
# Exact pins: the aws-sdk crates release in lockstep, and these are the newest
# versions whose declared minimum supported rustc matches the workspace
# rust-version (1.88); later releases require a newer compiler. Their
# transitive aws-smithy-* crates are held in Cargo.lock to matching versions
# for the same reason. Feature set mirrors tests/rust/Cargo.toml.
aws-config = { version = "=1.8.13", default-features = false, features = ["behavior-version-latest", "rt-tokio", "default-https-client", "credentials-process"] }
aws-sdk-s3 = { version = "=1.122.0", default-features = false, features = ["behavior-version-latest", "rt-tokio", "default-https-client"] }
bytes = "1"

# Crypto & checksums
crc32fast = "1"
uuid = { version = "1", features = ["v4"] }
Expand Down
2 changes: 1 addition & 1 deletion SOFTWARE-LICENSE-NOTICES-DEV.html
Original file line number Diff line number Diff line change
Expand Up @@ -1765,7 +1765,7 @@ <h4>Used by:</h4>
<h3 id="Apache-2.0">Apache License 2.0</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
<li><a href=" https://github.com/mrhooray/crc-rs.git ">crc 3.4.0</a></li>
<li><a href=" https://github.com/mrhooray/crc-rs.git ">crc 3.3.0</a></li>
</ul>
<pre class="license-text"> Apache License
Version 2.0 January 2004
Expand Down
2 changes: 1 addition & 1 deletion SOFTWARE-LICENSE-NOTICES-MONGODB.html
Original file line number Diff line number Diff line change
Expand Up @@ -2191,7 +2191,7 @@ <h4>Used by:</h4>
<h3 id="Apache-2.0">Apache License 2.0</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
<li><a href=" https://github.com/mrhooray/crc-rs.git ">crc 3.4.0</a></li>
<li><a href=" https://github.com/mrhooray/crc-rs.git ">crc 3.3.0</a></li>
</ul>
<pre class="license-text"> Apache License
Version 2.0 January 2004
Expand Down
2 changes: 1 addition & 1 deletion SOFTWARE-LICENSE-NOTICES.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions crates/backup-store/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2026 ExtendDB contributors
# SPDX-License-Identifier: Apache-2.0
[package]
name = "extenddb-backup-store"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Backup object store abstraction with filesystem and S3 implementations."

[dependencies]
aws-config = { workspace = true }
aws-sdk-s3 = { workspace = true }
bytes = { workspace = true }
futures = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true, features = ["io"] }
tracing = { workspace = true }
uuid = { workspace = true }

[features]
# Failure-injection seam for the crate's own test suite; not part of the
# supported API surface and never enabled by production consumers.
test-util = []

[dev-dependencies]
extenddb-backup-store = { path = ".", features = ["test-util"] }
tempfile = "3"
toml = { workspace = true }
55 changes: 55 additions & 0 deletions crates/backup-store/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# extenddb-backup-store

Backup object store abstraction for ExtendDB: one `BackupStore` trait with a
filesystem implementation and an S3 implementation built on the official
`aws-sdk-s3` crate. The engine writes portable backups through this trait; see
`docs/design/15-backup-restore.md` for the full design.

## Behavior

- Keys are `/`-separated component paths. Both stores reject empty
components, `.` and `..`, backslashes, control characters, and keys over
1024 bytes, so the two stores accept the same key space.
- The filesystem store canonicalizes its root at construction and resolves
every key one component at a time without following symlinks: a symlink
anywhere in a key's path, pointing inside or outside the root, is refused.
This defeats symlinks present when an operation starts; it does not defend
against another local process mutating the tree during an operation, so
the backup root must not be writable by less-trusted users (write access
inside the root already grants full read, write, and delete over every
backup). Writes go through a temporary sibling file renamed into place so
a crash never leaves a truncated object under its final name.
- The S3 store takes credentials from the standard AWS SDK chain, uses
multipart upload for bodies larger than the configured part size (default
8 MiB, minimum 5 MiB), aborts the multipart upload when a put fails,
paginates listings, and deletes prefixes in DeleteObjects batches of 1000.

## Tests

`cargo test -p extenddb-backup-store` runs the key validation and
configuration unit tests, the filesystem conformance suite against a
temporary directory, and the S3 conformance suite.

The S3 suite needs a local S3-compatible endpoint and is skipped with a
printed reason when `EXTENDDB_TEST_S3_ENDPOINT` is unset. To run it against a
local MinIO:

```sh
docker run --rm -d -p 9000:9000 --name extenddb-backup-store-minio \
-e MINIO_ROOT_USER=minioadmin -e MINIO_ROOT_PASSWORD=minioadmin \
minio/minio server /data

EXTENDDB_TEST_S3_ENDPOINT=http://127.0.0.1:9000 \
AWS_ACCESS_KEY_ID=minioadmin \
AWS_SECRET_ACCESS_KEY=minioadmin \
cargo test -p extenddb-backup-store
```

A bare `minio server /data-dir` binary works the same way; only the endpoint
and the two credential variables matter.

`EXTENDDB_TEST_S3_BUCKET` (default `extenddb-backup-store-tests`) and
`EXTENDDB_TEST_S3_REGION` (default `us-east-1`) can override the bucket and
region. The suite creates the bucket when it does not exist and isolates each
test under a unique key prefix. CI wiring for the MinIO service is tracked as
its own task in the design doc.
Loading
Loading