icechunk 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {icechunk-0.2.0 → icechunk-0.2.2}/PKG-INFO +1 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/Cargo.toml +1 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/examples/multithreaded_store.rs +1 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/repository.rs +28 -7
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/storage/mod.rs +47 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/storage/object_store.rs +55 -59
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/storage/s3.rs +64 -31
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/tests/test_storage.rs +53 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/tests/test_virtual_refs.rs +1 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/Cargo.toml +2 -2
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/demo-dummy-data.ipynb +2 -2
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/version-control.ipynb +2 -2
- {icechunk-0.2.0 → icechunk-0.2.2/icechunk-python}/python/icechunk/__init__.py +2 -0
- {icechunk-0.2.0 → icechunk-0.2.2/icechunk-python}/python/icechunk/_icechunk_python.pyi +40 -6
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/repository.py +25 -15
- {icechunk-0.2.0 → icechunk-0.2.2/icechunk-python}/python/icechunk/storage.py +5 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/store.py +1 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/src/config.rs +36 -15
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/src/repository.rs +27 -15
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_can_read_old.py +2 -2
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_config.py +1 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_credentials.py +1 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_stateful_repo_ops.py +1 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_timetravel.py +42 -7
- {icechunk-0.2.0/icechunk-python → icechunk-0.2.2}/python/icechunk/__init__.py +2 -0
- {icechunk-0.2.0/icechunk-python → icechunk-0.2.2}/python/icechunk/_icechunk_python.pyi +40 -6
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/repository.py +25 -15
- {icechunk-0.2.0/icechunk-python → icechunk-0.2.2}/python/icechunk/storage.py +5 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/store.py +1 -1
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/examples/low_level_dataset.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/examples/multithreaded_get_chunk_refs.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/flatbuffers/all.fbs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/flatbuffers/manifest.fbs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/flatbuffers/object_ids.fbs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/flatbuffers/snapshot.fbs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/flatbuffers/transaction_log.fbs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/proptest-regressions/dataset.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/proptest-regressions/format/manifest.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/proptest-regressions/manifest.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/proptest-regressions/session.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/proptest-regressions/storage/mod.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/proptest-regressions/storage/s3.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/proptest-regressions/storage/virtual_ref.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/asset_manager.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/change_set.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/config.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/conflicts/basic_solver.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/conflicts/detector.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/conflicts/mod.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/error.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/format/attributes.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/format/flatbuffers/all_generated.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/format/manifest.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/format/mod.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/format/serializers/mod.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/format/snapshot.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/format/transaction_log.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/lib.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/ops/gc.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/ops/mod.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/refs.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/session.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/storage/logging.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/store.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/strategies.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/src/virtual_chunks.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/tests/test_concurrency.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/tests/test_distributed_writes.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk/tests/test_gc.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/.gitignore +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/Cargo.lock +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/__init__.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/coiled_runner.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/conftest.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/create_era5.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/datasets.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/envs/icechunk-alpha-12.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/envs/icechunk-alpha-release.txt +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/helpers.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/lib.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/most_recent.sh +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/runner.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/tasks.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/test_benchmark_reads.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/benchmarks/test_benchmark_writes.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/examples/dask_write.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/conflict.ipynb +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/demo-azure-blob.ipynb +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/demo-gcs.ipynb +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/demo-s3.ipynb +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/environment.yml +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/memorystore.ipynb +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/performance/era5_xarray-Icechunk.ipynb +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/performance/era5_xarray-zarr2.ipynb +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/notebooks/performance/era5_xarray-zarr3.ipynb +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/credentials.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/dask.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/distributed.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/py.typed +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/session.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/vendor/__init__.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/vendor/xarray.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/python/icechunk/xarray.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/src/conflicts.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/src/errors.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/src/lib.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/src/session.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/src/store.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/src/streams.rs +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/__init__.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/conftest.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/chunks/09HEW2P03CSMHFAZY7DG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/chunks/52H0E4NSPN8SVRK9EVGG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/chunks/DWQ75SDC624XF9H326RG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/chunks/RW938N1KP2R4BHMW62QG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/config.yaml +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/manifests/CMYVHDWMSTG9R25780YG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/manifests/G3W2W8V6ZG09J6C21WE0 +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/manifests/Q04J7QW5RQ8D17TPA10G +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/manifests/SHTEAP8C784YMZSJKBM0 +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/refs/branch.main/ref.json +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/refs/branch.my-branch/ref.json +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/refs/tag.deleted/ref.json +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/refs/tag.deleted/ref.json.deleted +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/refs/tag.it also works!/ref.json +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/refs/tag.it works!/ref.json +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/snapshots/4QF8JA0YPDN51MHSSYVG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/snapshots/7XAF0Q905SH4938DN9CG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/snapshots/GC4YVH5SKBPEZCENYQE0 +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/snapshots/NXH3M0HJ7EEJ0699DPP0 +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/snapshots/P874YS3J196959RDHX7G +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/snapshots/XDZ162T1TYBEJMK99NPG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/transactions/4QF8JA0YPDN51MHSSYVG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/transactions/7XAF0Q905SH4938DN9CG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/transactions/GC4YVH5SKBPEZCENYQE0 +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/transactions/NXH3M0HJ7EEJ0699DPP0 +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/data/test-repo/transactions/XDZ162T1TYBEJMK99NPG +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/run_xarray_backends_tests.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_commit_properties.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_concurrency.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_conflicts.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_dask.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_distributed_writers.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_error.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_gc.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_pickle.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_regressions.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_store.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_virtual_ref.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_xarray.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_zarr/__init__.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_zarr/test_array.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_zarr/test_group.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_zarr/test_properties.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_zarr/test_stateful.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_zarr/test_store/__init__.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_zarr/test_store/test_core.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/icechunk-python/tests/test_zarr/test_store/test_icechunk_store.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/pyproject.toml +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/credentials.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/dask.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/distributed.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/py.typed +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/session.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/vendor/__init__.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/vendor/xarray.py +0 -0
- {icechunk-0.2.0 → icechunk-0.2.2}/python/icechunk/xarray.py +0 -0
|
@@ -54,6 +54,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
54
54
|
async fn writer(name: &str, range: Range<u64>, store: &Store) {
|
|
55
55
|
println!("Starting writer {name}.");
|
|
56
56
|
for i in range {
|
|
57
|
+
#[allow(clippy::dbg_macro)]
|
|
57
58
|
if let Err(err) = store
|
|
58
59
|
.set(
|
|
59
60
|
format!("array/c/{i}").as_str(),
|
|
@@ -5,7 +5,9 @@ use std::{
|
|
|
5
5
|
sync::Arc,
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
use async_recursion::async_recursion;
|
|
8
9
|
use bytes::Bytes;
|
|
10
|
+
use chrono::{DateTime, Utc};
|
|
9
11
|
use err_into::ErrorInto as _;
|
|
10
12
|
use futures::{
|
|
11
13
|
stream::{FuturesOrdered, FuturesUnordered},
|
|
@@ -37,15 +39,13 @@ use crate::{
|
|
|
37
39
|
Storage, StorageError,
|
|
38
40
|
};
|
|
39
41
|
|
|
40
|
-
#[derive(Debug, Clone,
|
|
42
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
41
43
|
#[non_exhaustive]
|
|
42
44
|
pub enum VersionInfo {
|
|
43
|
-
#[serde(rename = "snapshot_id")]
|
|
44
45
|
SnapshotId(SnapshotId),
|
|
45
|
-
#[serde(rename = "tag")]
|
|
46
46
|
TagRef(String),
|
|
47
|
-
#[serde(rename = "branch")]
|
|
48
47
|
BranchTipRef(String),
|
|
48
|
+
AsOf { branch: String, at: DateTime<Utc> },
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
#[derive(Debug, Error)]
|
|
@@ -60,6 +60,8 @@ pub enum RepositoryErrorKind {
|
|
|
60
60
|
|
|
61
61
|
#[error("snapshot not found: `{id}`")]
|
|
62
62
|
SnapshotNotFound { id: SnapshotId },
|
|
63
|
+
#[error("branch {branch} does not have a snapshots before or at {at}")]
|
|
64
|
+
InvalidAsOfSpec { branch: String, at: DateTime<Utc> },
|
|
63
65
|
#[error("invalid snapshot id: `{0}`")]
|
|
64
66
|
InvalidSnapshotId(String),
|
|
65
67
|
#[error("tag error: `{0}`")]
|
|
@@ -404,11 +406,12 @@ impl Repository {
|
|
|
404
406
|
}
|
|
405
407
|
|
|
406
408
|
/// Returns the sequence of parents of the snapshot pointed by the given version
|
|
409
|
+
#[async_recursion(?Send)]
|
|
407
410
|
#[instrument(skip(self))]
|
|
408
|
-
pub async fn ancestry(
|
|
409
|
-
&self,
|
|
411
|
+
pub async fn ancestry<'a>(
|
|
412
|
+
&'a self,
|
|
410
413
|
version: &VersionInfo,
|
|
411
|
-
) -> RepositoryResult<impl Stream<Item = RepositoryResult<SnapshotInfo>> + '
|
|
414
|
+
) -> RepositoryResult<impl Stream<Item = RepositoryResult<SnapshotInfo>> + 'a> {
|
|
412
415
|
let snapshot_id = self.resolve_version(version).await?;
|
|
413
416
|
self.snapshot_ancestry(&snapshot_id).await
|
|
414
417
|
}
|
|
@@ -572,6 +575,24 @@ impl Repository {
|
|
|
572
575
|
.await?;
|
|
573
576
|
Ok(ref_data.snapshot)
|
|
574
577
|
}
|
|
578
|
+
VersionInfo::AsOf { branch, at } => {
|
|
579
|
+
let tip = VersionInfo::BranchTipRef(branch.clone());
|
|
580
|
+
let snap = self
|
|
581
|
+
.ancestry(&tip)
|
|
582
|
+
.await?
|
|
583
|
+
.try_skip_while(|parent| ready(Ok(&parent.flushed_at > at)))
|
|
584
|
+
.take(1)
|
|
585
|
+
.try_collect::<Vec<_>>()
|
|
586
|
+
.await?;
|
|
587
|
+
match snap.into_iter().next() {
|
|
588
|
+
Some(snap) => Ok(snap.id),
|
|
589
|
+
None => Err(RepositoryErrorKind::InvalidAsOfSpec {
|
|
590
|
+
branch: branch.clone(),
|
|
591
|
+
at: *at,
|
|
592
|
+
}
|
|
593
|
+
.into()),
|
|
594
|
+
}
|
|
595
|
+
}
|
|
575
596
|
}
|
|
576
597
|
}
|
|
577
598
|
|
|
@@ -174,6 +174,9 @@ impl ConcurrencySettings {
|
|
|
174
174
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Default)]
|
|
175
175
|
pub struct Settings {
|
|
176
176
|
pub concurrency: Option<ConcurrencySettings>,
|
|
177
|
+
pub unsafe_use_conditional_update: Option<bool>,
|
|
178
|
+
pub unsafe_use_conditional_create: Option<bool>,
|
|
179
|
+
pub unsafe_use_metadata: Option<bool>,
|
|
177
180
|
}
|
|
178
181
|
|
|
179
182
|
static DEFAULT_CONCURRENCY: OnceLock<ConcurrencySettings> = OnceLock::new();
|
|
@@ -185,6 +188,18 @@ impl Settings {
|
|
|
185
188
|
.unwrap_or_else(|| DEFAULT_CONCURRENCY.get_or_init(Default::default))
|
|
186
189
|
}
|
|
187
190
|
|
|
191
|
+
pub fn unsafe_use_conditional_create(&self) -> bool {
|
|
192
|
+
self.unsafe_use_conditional_create.unwrap_or(true)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
pub fn unsafe_use_conditional_update(&self) -> bool {
|
|
196
|
+
self.unsafe_use_conditional_update.unwrap_or(true)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
pub fn unsafe_use_metadata(&self) -> bool {
|
|
200
|
+
self.unsafe_use_metadata.unwrap_or(true)
|
|
201
|
+
}
|
|
202
|
+
|
|
188
203
|
pub fn merge(&self, other: Self) -> Self {
|
|
189
204
|
Self {
|
|
190
205
|
concurrency: match (&self.concurrency, other.concurrency) {
|
|
@@ -193,6 +208,33 @@ impl Settings {
|
|
|
193
208
|
(Some(c), None) => Some(c.clone()),
|
|
194
209
|
(Some(mine), Some(theirs)) => Some(mine.merge(theirs)),
|
|
195
210
|
},
|
|
211
|
+
unsafe_use_conditional_create: match (
|
|
212
|
+
&self.unsafe_use_conditional_create,
|
|
213
|
+
other.unsafe_use_conditional_create,
|
|
214
|
+
) {
|
|
215
|
+
(None, None) => None,
|
|
216
|
+
(None, Some(c)) => Some(c),
|
|
217
|
+
(Some(c), None) => Some(*c),
|
|
218
|
+
(Some(_), Some(theirs)) => Some(theirs),
|
|
219
|
+
},
|
|
220
|
+
unsafe_use_conditional_update: match (
|
|
221
|
+
&self.unsafe_use_conditional_update,
|
|
222
|
+
other.unsafe_use_conditional_update,
|
|
223
|
+
) {
|
|
224
|
+
(None, None) => None,
|
|
225
|
+
(None, Some(c)) => Some(c),
|
|
226
|
+
(Some(c), None) => Some(*c),
|
|
227
|
+
(Some(_), Some(theirs)) => Some(theirs),
|
|
228
|
+
},
|
|
229
|
+
unsafe_use_metadata: match (
|
|
230
|
+
&self.unsafe_use_metadata,
|
|
231
|
+
other.unsafe_use_metadata,
|
|
232
|
+
) {
|
|
233
|
+
(None, None) => None,
|
|
234
|
+
(None, Some(c)) => Some(c),
|
|
235
|
+
(Some(c), None) => Some(*c),
|
|
236
|
+
(Some(_), Some(theirs)) => Some(theirs),
|
|
237
|
+
},
|
|
196
238
|
}
|
|
197
239
|
}
|
|
198
240
|
}
|
|
@@ -364,7 +406,11 @@ pub trait Storage: fmt::Debug + private::Sealed + Sync + Send {
|
|
|
364
406
|
) -> StorageResult<DateTime<Utc>>;
|
|
365
407
|
|
|
366
408
|
async fn root_is_clean(&self) -> StorageResult<bool> {
|
|
367
|
-
|
|
409
|
+
match self.list_objects(&Settings::default(), "").await?.next().await {
|
|
410
|
+
None => Ok(true),
|
|
411
|
+
Some(Ok(_)) => Ok(false),
|
|
412
|
+
Some(Err(err)) => Err(err),
|
|
413
|
+
}
|
|
368
414
|
}
|
|
369
415
|
|
|
370
416
|
async fn list_chunks(
|
|
@@ -158,16 +158,6 @@ impl ObjectStorage {
|
|
|
158
158
|
self.backend.artificially_sort_refs_in_mem()
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
/// We need this because object_store's local file implementation doesn't support metadata.
|
|
162
|
-
pub fn supports_metadata(&self) -> bool {
|
|
163
|
-
self.backend.supports_metadata()
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/// We need this because object_store's local file implementation doesn't support it
|
|
167
|
-
pub fn supports_conditional_put_updates(&self) -> bool {
|
|
168
|
-
self.backend.supports_conditional_put_updates()
|
|
169
|
-
}
|
|
170
|
-
|
|
171
161
|
/// Return all keys in the store
|
|
172
162
|
///
|
|
173
163
|
/// Intended for testing and debugging purposes only.
|
|
@@ -251,8 +241,12 @@ impl ObjectStorage {
|
|
|
251
241
|
.compat())
|
|
252
242
|
}
|
|
253
243
|
|
|
254
|
-
fn metadata_to_attributes(
|
|
255
|
-
|
|
244
|
+
fn metadata_to_attributes(
|
|
245
|
+
&self,
|
|
246
|
+
settings: &Settings,
|
|
247
|
+
metadata: Vec<(String, String)>,
|
|
248
|
+
) -> Attributes {
|
|
249
|
+
if settings.unsafe_use_metadata() {
|
|
256
250
|
Attributes::from_iter(metadata.into_iter().map(|(key, val)| {
|
|
257
251
|
(
|
|
258
252
|
Attribute::Metadata(std::borrow::Cow::Owned(key)),
|
|
@@ -270,18 +264,24 @@ impl ObjectStorage {
|
|
|
270
264
|
Some(parent.as_ref().to_string())
|
|
271
265
|
}
|
|
272
266
|
|
|
273
|
-
fn get_put_mode(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
267
|
+
fn get_put_mode(
|
|
268
|
+
&self,
|
|
269
|
+
settings: &Settings,
|
|
270
|
+
previous_version: &VersionInfo,
|
|
271
|
+
) -> PutMode {
|
|
272
|
+
match (
|
|
273
|
+
previous_version.is_create(),
|
|
274
|
+
settings.unsafe_use_conditional_create(),
|
|
275
|
+
settings.unsafe_use_conditional_update(),
|
|
276
|
+
) {
|
|
277
|
+
(true, true, _) => PutMode::Create,
|
|
278
|
+
(true, false, _) => PutMode::Overwrite,
|
|
279
|
+
|
|
280
|
+
(false, _, true) => PutMode::Update(UpdateVersion {
|
|
282
281
|
e_tag: previous_version.etag().cloned(),
|
|
283
282
|
version: previous_version.generation().cloned(),
|
|
284
|
-
})
|
|
283
|
+
}),
|
|
284
|
+
(false, _, false) => PutMode::Overwrite,
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
}
|
|
@@ -317,15 +317,15 @@ impl Storage for ObjectStorage {
|
|
|
317
317
|
Err(err) => Err(err.into()),
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
|
-
#[instrument(skip(self,
|
|
320
|
+
#[instrument(skip(self, settings, config))]
|
|
321
321
|
async fn update_config(
|
|
322
322
|
&self,
|
|
323
|
-
|
|
323
|
+
settings: &Settings,
|
|
324
324
|
config: Bytes,
|
|
325
325
|
previous_version: &VersionInfo,
|
|
326
326
|
) -> StorageResult<UpdateConfigResult> {
|
|
327
327
|
let path = self.get_config_path();
|
|
328
|
-
let attributes = if
|
|
328
|
+
let attributes = if settings.unsafe_use_metadata() {
|
|
329
329
|
Attributes::from_iter(vec![(
|
|
330
330
|
Attribute::ContentType,
|
|
331
331
|
AttributeValue::from("application/yaml"),
|
|
@@ -334,7 +334,7 @@ impl Storage for ObjectStorage {
|
|
|
334
334
|
Attributes::new()
|
|
335
335
|
};
|
|
336
336
|
|
|
337
|
-
let mode = self.get_put_mode(previous_version);
|
|
337
|
+
let mode = self.get_put_mode(settings, previous_version);
|
|
338
338
|
|
|
339
339
|
let options = PutOptions { mode, attributes, ..PutOptions::default() };
|
|
340
340
|
let res = self.get_client().await.put_opts(&path, config.into(), options).await;
|
|
@@ -394,48 +394,48 @@ impl Storage for ObjectStorage {
|
|
|
394
394
|
Ok(Box::new(self.get_object_reader(settings, &path).await?))
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
-
#[instrument(skip(self,
|
|
397
|
+
#[instrument(skip(self, settings, metadata, bytes))]
|
|
398
398
|
async fn write_snapshot(
|
|
399
399
|
&self,
|
|
400
|
-
|
|
400
|
+
settings: &Settings,
|
|
401
401
|
id: SnapshotId,
|
|
402
402
|
metadata: Vec<(String, String)>,
|
|
403
403
|
bytes: Bytes,
|
|
404
404
|
) -> StorageResult<()> {
|
|
405
405
|
let path = self.get_snapshot_path(&id);
|
|
406
|
-
let attributes = self.metadata_to_attributes(metadata);
|
|
406
|
+
let attributes = self.metadata_to_attributes(settings, metadata);
|
|
407
407
|
let options = PutOptions { attributes, ..PutOptions::default() };
|
|
408
408
|
// FIXME: use multipart
|
|
409
409
|
self.get_client().await.put_opts(&path, bytes.into(), options).await?;
|
|
410
410
|
Ok(())
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
-
#[instrument(skip(self,
|
|
413
|
+
#[instrument(skip(self, settings, metadata, bytes))]
|
|
414
414
|
async fn write_manifest(
|
|
415
415
|
&self,
|
|
416
|
-
|
|
416
|
+
settings: &Settings,
|
|
417
417
|
id: ManifestId,
|
|
418
418
|
metadata: Vec<(String, String)>,
|
|
419
419
|
bytes: Bytes,
|
|
420
420
|
) -> StorageResult<()> {
|
|
421
421
|
let path = self.get_manifest_path(&id);
|
|
422
|
-
let attributes = self.metadata_to_attributes(metadata);
|
|
422
|
+
let attributes = self.metadata_to_attributes(settings, metadata);
|
|
423
423
|
let options = PutOptions { attributes, ..PutOptions::default() };
|
|
424
424
|
// FIXME: use multipart
|
|
425
425
|
self.get_client().await.put_opts(&path, bytes.into(), options).await?;
|
|
426
426
|
Ok(())
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
-
#[instrument(skip(self,
|
|
429
|
+
#[instrument(skip(self, settings, metadata, bytes))]
|
|
430
430
|
async fn write_transaction_log(
|
|
431
431
|
&self,
|
|
432
|
-
|
|
432
|
+
settings: &Settings,
|
|
433
433
|
id: SnapshotId,
|
|
434
434
|
metadata: Vec<(String, String)>,
|
|
435
435
|
bytes: Bytes,
|
|
436
436
|
) -> StorageResult<()> {
|
|
437
437
|
let path = self.get_transaction_path(&id);
|
|
438
|
-
let attributes = self.metadata_to_attributes(metadata);
|
|
438
|
+
let attributes = self.metadata_to_attributes(settings, metadata);
|
|
439
439
|
let options = PutOptions { attributes, ..PutOptions::default() };
|
|
440
440
|
// FIXME: use multipart
|
|
441
441
|
self.get_client().await.put_opts(&path, bytes.into(), options).await?;
|
|
@@ -506,16 +506,16 @@ impl Storage for ObjectStorage {
|
|
|
506
506
|
.await?)
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
#[instrument(skip(self,
|
|
509
|
+
#[instrument(skip(self, settings, bytes))]
|
|
510
510
|
async fn write_ref(
|
|
511
511
|
&self,
|
|
512
|
-
|
|
512
|
+
settings: &Settings,
|
|
513
513
|
ref_key: &str,
|
|
514
514
|
bytes: Bytes,
|
|
515
515
|
previous_version: &VersionInfo,
|
|
516
516
|
) -> StorageResult<WriteRefResult> {
|
|
517
517
|
let key = self.ref_key(ref_key);
|
|
518
|
-
let mode = self.get_put_mode(previous_version);
|
|
518
|
+
let mode = self.get_put_mode(settings, previous_version);
|
|
519
519
|
let opts = PutOptions { mode, ..PutOptions::default() };
|
|
520
520
|
|
|
521
521
|
match self
|
|
@@ -634,19 +634,7 @@ pub trait ObjectStoreBackend: Debug + Sync + Send {
|
|
|
634
634
|
false
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
-
|
|
638
|
-
fn supports_metadata(&self) -> bool {
|
|
639
|
-
true
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/// We need this because object_store's local file implementation doesn't support it
|
|
643
|
-
fn supports_conditional_put_updates(&self) -> bool {
|
|
644
|
-
true
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
fn default_settings(&self) -> Settings {
|
|
648
|
-
Settings::default()
|
|
649
|
-
}
|
|
637
|
+
fn default_settings(&self) -> Settings;
|
|
650
638
|
}
|
|
651
639
|
|
|
652
640
|
#[derive(Debug, Serialize, Deserialize)]
|
|
@@ -674,6 +662,7 @@ impl ObjectStoreBackend for InMemoryObjectStoreBackend {
|
|
|
674
662
|
NonZeroU64::new(1).unwrap_or(NonZeroU64::MIN),
|
|
675
663
|
),
|
|
676
664
|
}),
|
|
665
|
+
..Default::default()
|
|
677
666
|
}
|
|
678
667
|
}
|
|
679
668
|
}
|
|
@@ -706,14 +695,6 @@ impl ObjectStoreBackend for LocalFileSystemObjectStoreBackend {
|
|
|
706
695
|
true
|
|
707
696
|
}
|
|
708
697
|
|
|
709
|
-
fn supports_metadata(&self) -> bool {
|
|
710
|
-
false
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
fn supports_conditional_put_updates(&self) -> bool {
|
|
714
|
-
false
|
|
715
|
-
}
|
|
716
|
-
|
|
717
698
|
fn default_settings(&self) -> Settings {
|
|
718
699
|
Settings {
|
|
719
700
|
concurrency: Some(ConcurrencySettings {
|
|
@@ -724,6 +705,9 @@ impl ObjectStoreBackend for LocalFileSystemObjectStoreBackend {
|
|
|
724
705
|
NonZeroU64::new(4 * 1024).unwrap_or(NonZeroU64::MIN),
|
|
725
706
|
),
|
|
726
707
|
}),
|
|
708
|
+
unsafe_use_conditional_update: Some(false),
|
|
709
|
+
unsafe_use_metadata: Some(false),
|
|
710
|
+
..Default::default()
|
|
727
711
|
}
|
|
728
712
|
}
|
|
729
713
|
}
|
|
@@ -795,6 +779,10 @@ impl ObjectStoreBackend for S3ObjectStoreBackend {
|
|
|
795
779
|
fn prefix(&self) -> String {
|
|
796
780
|
self.prefix.clone().unwrap_or("".to_string())
|
|
797
781
|
}
|
|
782
|
+
|
|
783
|
+
fn default_settings(&self) -> Settings {
|
|
784
|
+
Default::default()
|
|
785
|
+
}
|
|
798
786
|
}
|
|
799
787
|
|
|
800
788
|
#[derive(Debug, Serialize, Deserialize)]
|
|
@@ -844,6 +832,10 @@ impl ObjectStoreBackend for AzureObjectStoreBackend {
|
|
|
844
832
|
fn prefix(&self) -> String {
|
|
845
833
|
self.prefix.clone().unwrap_or("".to_string())
|
|
846
834
|
}
|
|
835
|
+
|
|
836
|
+
fn default_settings(&self) -> Settings {
|
|
837
|
+
Default::default()
|
|
838
|
+
}
|
|
847
839
|
}
|
|
848
840
|
|
|
849
841
|
#[derive(Debug, Serialize, Deserialize)]
|
|
@@ -910,6 +902,10 @@ impl ObjectStoreBackend for GcsObjectStoreBackend {
|
|
|
910
902
|
fn prefix(&self) -> String {
|
|
911
903
|
self.prefix.clone().unwrap_or("".to_string())
|
|
912
904
|
}
|
|
905
|
+
|
|
906
|
+
fn default_settings(&self) -> Settings {
|
|
907
|
+
Default::default()
|
|
908
|
+
}
|
|
913
909
|
}
|
|
914
910
|
|
|
915
911
|
#[derive(Debug)]
|
|
@@ -196,6 +196,7 @@ impl S3Storage {
|
|
|
196
196
|
I: IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
|
|
197
197
|
>(
|
|
198
198
|
&self,
|
|
199
|
+
settings: &Settings,
|
|
199
200
|
key: &str,
|
|
200
201
|
content_type: Option<impl Into<String>>,
|
|
201
202
|
metadata: I,
|
|
@@ -204,14 +205,17 @@ impl S3Storage {
|
|
|
204
205
|
let mut b =
|
|
205
206
|
self.get_client().await.put_object().bucket(self.bucket.clone()).key(key);
|
|
206
207
|
|
|
207
|
-
if
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
for (k, v) in metadata {
|
|
212
|
-
b = b.metadata(k, v);
|
|
208
|
+
if settings.unsafe_use_metadata() {
|
|
209
|
+
if let Some(ct) = content_type {
|
|
210
|
+
b = b.content_type(ct)
|
|
211
|
+
};
|
|
213
212
|
}
|
|
214
213
|
|
|
214
|
+
if settings.unsafe_use_metadata() {
|
|
215
|
+
for (k, v) in metadata {
|
|
216
|
+
b = b.metadata(k, v);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
215
219
|
b.body(bytes.into()).send().await?;
|
|
216
220
|
Ok(())
|
|
217
221
|
}
|
|
@@ -296,10 +300,10 @@ impl Storage for S3Storage {
|
|
|
296
300
|
}
|
|
297
301
|
}
|
|
298
302
|
|
|
299
|
-
#[instrument(skip(self,
|
|
303
|
+
#[instrument(skip(self, settings, config))]
|
|
300
304
|
async fn update_config(
|
|
301
305
|
&self,
|
|
302
|
-
|
|
306
|
+
settings: &Settings,
|
|
303
307
|
config: Bytes,
|
|
304
308
|
previous_version: &VersionInfo,
|
|
305
309
|
) -> StorageResult<UpdateConfigResult> {
|
|
@@ -310,13 +314,20 @@ impl Storage for S3Storage {
|
|
|
310
314
|
.put_object()
|
|
311
315
|
.bucket(self.bucket.clone())
|
|
312
316
|
.key(key)
|
|
313
|
-
.content_type("application/yaml")
|
|
314
317
|
.body(config.into());
|
|
315
318
|
|
|
316
|
-
if
|
|
317
|
-
req = req.
|
|
318
|
-
}
|
|
319
|
-
|
|
319
|
+
if settings.unsafe_use_metadata() {
|
|
320
|
+
req = req.content_type("application/yaml")
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
match (
|
|
324
|
+
previous_version.etag(),
|
|
325
|
+
settings.unsafe_use_conditional_create(),
|
|
326
|
+
settings.unsafe_use_conditional_update(),
|
|
327
|
+
) {
|
|
328
|
+
(None, true, _) => req = req.if_none_match("*"),
|
|
329
|
+
(Some(etag), _, true) => req = req.if_match(etag),
|
|
330
|
+
(_, _, _) => {}
|
|
320
331
|
}
|
|
321
332
|
|
|
322
333
|
let res = req.send().await;
|
|
@@ -411,53 +422,67 @@ impl Storage for S3Storage {
|
|
|
411
422
|
.await
|
|
412
423
|
}
|
|
413
424
|
|
|
414
|
-
#[instrument(skip(self,
|
|
425
|
+
#[instrument(skip(self, settings, metadata, bytes))]
|
|
415
426
|
async fn write_snapshot(
|
|
416
427
|
&self,
|
|
417
|
-
|
|
428
|
+
settings: &Settings,
|
|
418
429
|
id: SnapshotId,
|
|
419
430
|
metadata: Vec<(String, String)>,
|
|
420
431
|
bytes: Bytes,
|
|
421
432
|
) -> StorageResult<()> {
|
|
422
433
|
let key = self.get_snapshot_path(&id)?;
|
|
423
|
-
self.put_object(key.as_str(), None::<String>, metadata, bytes).await
|
|
434
|
+
self.put_object(settings, key.as_str(), None::<String>, metadata, bytes).await
|
|
424
435
|
}
|
|
425
436
|
|
|
426
|
-
#[instrument(skip(self,
|
|
437
|
+
#[instrument(skip(self, settings, metadata, bytes))]
|
|
427
438
|
async fn write_manifest(
|
|
428
439
|
&self,
|
|
429
|
-
|
|
440
|
+
settings: &Settings,
|
|
430
441
|
id: ManifestId,
|
|
431
442
|
metadata: Vec<(String, String)>,
|
|
432
443
|
bytes: Bytes,
|
|
433
444
|
) -> StorageResult<()> {
|
|
434
445
|
let key = self.get_manifest_path(&id)?;
|
|
435
|
-
self.put_object(
|
|
446
|
+
self.put_object(
|
|
447
|
+
settings,
|
|
448
|
+
key.as_str(),
|
|
449
|
+
None::<String>,
|
|
450
|
+
metadata.into_iter(),
|
|
451
|
+
bytes,
|
|
452
|
+
)
|
|
453
|
+
.await
|
|
436
454
|
}
|
|
437
455
|
|
|
438
|
-
#[instrument(skip(self,
|
|
456
|
+
#[instrument(skip(self, settings, metadata, bytes))]
|
|
439
457
|
async fn write_transaction_log(
|
|
440
458
|
&self,
|
|
441
|
-
|
|
459
|
+
settings: &Settings,
|
|
442
460
|
id: SnapshotId,
|
|
443
461
|
metadata: Vec<(String, String)>,
|
|
444
462
|
bytes: Bytes,
|
|
445
463
|
) -> StorageResult<()> {
|
|
446
464
|
let key = self.get_transaction_path(&id)?;
|
|
447
|
-
self.put_object(
|
|
465
|
+
self.put_object(
|
|
466
|
+
settings,
|
|
467
|
+
key.as_str(),
|
|
468
|
+
None::<String>,
|
|
469
|
+
metadata.into_iter(),
|
|
470
|
+
bytes,
|
|
471
|
+
)
|
|
472
|
+
.await
|
|
448
473
|
}
|
|
449
474
|
|
|
450
|
-
#[instrument(skip(self,
|
|
475
|
+
#[instrument(skip(self, settings, bytes))]
|
|
451
476
|
async fn write_chunk(
|
|
452
477
|
&self,
|
|
453
|
-
|
|
478
|
+
settings: &Settings,
|
|
454
479
|
id: ChunkId,
|
|
455
480
|
bytes: bytes::Bytes,
|
|
456
481
|
) -> Result<(), StorageError> {
|
|
457
482
|
let key = self.get_chunk_path(&id)?;
|
|
458
483
|
//FIXME: use multipart upload
|
|
459
484
|
let metadata: [(String, String); 0] = [];
|
|
460
|
-
self.put_object(key.as_str(), None::<String>, metadata, bytes).await
|
|
485
|
+
self.put_object(settings, key.as_str(), None::<String>, metadata, bytes).await
|
|
461
486
|
}
|
|
462
487
|
|
|
463
488
|
#[instrument(skip(self, _settings))]
|
|
@@ -523,10 +548,10 @@ impl Storage for S3Storage {
|
|
|
523
548
|
Ok(res)
|
|
524
549
|
}
|
|
525
550
|
|
|
526
|
-
#[instrument(skip(self,
|
|
551
|
+
#[instrument(skip(self, settings, bytes))]
|
|
527
552
|
async fn write_ref(
|
|
528
553
|
&self,
|
|
529
|
-
|
|
554
|
+
settings: &Settings,
|
|
530
555
|
ref_key: &str,
|
|
531
556
|
bytes: Bytes,
|
|
532
557
|
previous_version: &VersionInfo,
|
|
@@ -539,10 +564,18 @@ impl Storage for S3Storage {
|
|
|
539
564
|
.bucket(self.bucket.clone())
|
|
540
565
|
.key(key.clone());
|
|
541
566
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
567
|
+
match (
|
|
568
|
+
previous_version.etag(),
|
|
569
|
+
settings.unsafe_use_conditional_create(),
|
|
570
|
+
settings.unsafe_use_conditional_update(),
|
|
571
|
+
) {
|
|
572
|
+
(None, true, _) => {
|
|
573
|
+
builder = builder.if_none_match("*");
|
|
574
|
+
}
|
|
575
|
+
(Some(etag), _, true) => {
|
|
576
|
+
builder = builder.if_match(etag);
|
|
577
|
+
}
|
|
578
|
+
(_, _, _) => {}
|
|
546
579
|
}
|
|
547
580
|
|
|
548
581
|
let res = builder.body(bytes.into()).send().await;
|
|
@@ -14,7 +14,7 @@ use icechunk::{
|
|
|
14
14
|
RefErrorKind,
|
|
15
15
|
},
|
|
16
16
|
storage::{
|
|
17
|
-
new_in_memory_storage, new_s3_storage, ETag, FetchConfigResult, Generation,
|
|
17
|
+
self, new_in_memory_storage, new_s3_storage, ETag, FetchConfigResult, Generation,
|
|
18
18
|
StorageResult, UpdateConfigResult, VersionInfo,
|
|
19
19
|
},
|
|
20
20
|
ObjectStorage, Storage,
|
|
@@ -488,3 +488,55 @@ pub async fn test_write_config_fails_on_bad_version_when_existing(
|
|
|
488
488
|
}).await?;
|
|
489
489
|
Ok(())
|
|
490
490
|
}
|
|
491
|
+
|
|
492
|
+
#[tokio::test]
|
|
493
|
+
#[allow(clippy::panic)]
|
|
494
|
+
pub async fn test_write_config_can_overwrite_with_unsafe_config(
|
|
495
|
+
) -> Result<(), Box<dyn std::error::Error>> {
|
|
496
|
+
with_storage(|_, storage| async move {
|
|
497
|
+
let storage_settings = storage::Settings {
|
|
498
|
+
unsafe_use_conditional_update: Some(false),
|
|
499
|
+
unsafe_use_conditional_create: Some(false),
|
|
500
|
+
..storage.default_settings()
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// create the initial version
|
|
504
|
+
let config = Bytes::copy_from_slice(b"hello");
|
|
505
|
+
match storage
|
|
506
|
+
.update_config(
|
|
507
|
+
&storage_settings,
|
|
508
|
+
config.clone(),
|
|
509
|
+
&VersionInfo {
|
|
510
|
+
etag: Some(ETag("some-bad-etag".to_string())),
|
|
511
|
+
generation: Some(Generation("42".to_string())),
|
|
512
|
+
}
|
|
513
|
+
)
|
|
514
|
+
.await?
|
|
515
|
+
{
|
|
516
|
+
UpdateConfigResult::Updated { new_version } => new_version,
|
|
517
|
+
_ => panic!(),
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// attempt a bad change that should succeed in this config
|
|
521
|
+
let update_res = storage
|
|
522
|
+
.update_config(
|
|
523
|
+
&storage_settings,
|
|
524
|
+
Bytes::copy_from_slice(b"bye"),
|
|
525
|
+
&VersionInfo {
|
|
526
|
+
etag: Some(ETag("other-bad-etag".to_string())),
|
|
527
|
+
generation: Some(Generation("55".to_string())),
|
|
528
|
+
},
|
|
529
|
+
)
|
|
530
|
+
.await?;
|
|
531
|
+
|
|
532
|
+
assert!(matches!(update_res, UpdateConfigResult::Updated { .. }));
|
|
533
|
+
|
|
534
|
+
let fetch_res = storage.fetch_config(&storage_settings).await?;
|
|
535
|
+
assert!(
|
|
536
|
+
matches!(fetch_res, FetchConfigResult::Found{bytes, ..} if bytes.as_ref() == b"bye")
|
|
537
|
+
);
|
|
538
|
+
Ok(())
|
|
539
|
+
})
|
|
540
|
+
.await?;
|
|
541
|
+
Ok(())
|
|
542
|
+
}
|