diff options
author | Alex Auvolat <alex@adnab.me> | 2023-04-18 18:03:10 +0200 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2023-10-18 16:31:50 +0200 |
commit | 45b0453d0f5b08f44dbd010c084daa87c2876945 (patch) | |
tree | 833b80404c1eddefb9a8c63e53845a8b8e5d5917 /src | |
parent | a5e8ffeb63a193e5b0e020e4c014687e57f85c23 (diff) | |
download | garage-45b0453d0f5b08f44dbd010c084daa87c2876945.tar.gz garage-45b0453d0f5b08f44dbd010c084daa87c2876945.zip |
Ensure increasing version timestamps in PutObject
Diffstat (limited to 'src')
-rw-r--r-- | src/api/s3/put.rs | 24 |
1 files changed, 18 insertions, 6 deletions
diff --git a/src/api/s3/put.rs b/src/api/s3/put.rs index c7ac5030..bd032165 100644 --- a/src/api/s3/put.rs +++ b/src/api/s3/put.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use base64::prelude::*; use futures::prelude::*; +use futures::try_join; use hyper::body::{Body, Bytes}; use hyper::header::{HeaderMap, HeaderValue}; use hyper::{Request, Response}; @@ -35,7 +36,7 @@ pub async fn handle_put( garage: Arc<Garage>, req: Request<Body>, bucket: &Bucket, - key: &str, + key: &String, content_sha256: Option<Hash>, ) -> Result<Response<Body>, Error> { // Retrieve interesting headers from request @@ -68,16 +69,27 @@ pub(crate) async fn save_stream<S: Stream<Item = Result<Bytes, Error>> + Unpin>( headers: ObjectVersionHeaders, body: S, bucket: &Bucket, - key: &str, + key: &String, content_md5: Option<String>, content_sha256: Option<FixedBytes32>, ) -> Result<(Uuid, String), Error> { + let mut chunker = StreamChunker::new(body, garage.config.block_size); + let (first_block_opt, existing_object) = try_join!( + chunker.next(), + garage + .object_table + .get(&bucket.id, key) + .map_err(Error::from), + )?; + + let first_block = first_block_opt.unwrap_or_default(); + // Generate identity of new version let version_uuid = gen_uuid(); - let version_timestamp = now_msec(); - - let mut chunker = StreamChunker::new(body, garage.config.block_size); - let first_block = chunker.next().await?.unwrap_or_default(); + let version_timestamp = existing_object + .and_then(|obj| obj.versions().iter().map(|v| v.timestamp).max()) + .map(|t| std::cmp::max(t + 1, now_msec())) + .unwrap_or_else(now_msec); // If body is small enough, store it directly in the object table // as "inline data". We can then return immediately. |