Skip to content

Commit fc360a9

Browse files
authored
chore: enable conditional put by default for S3 (#7181)
* fix cargo clippy * add Disabled variant to S3ConditionalPut * restore object_store.yml
1 parent 8df892b commit fc360a9

File tree

4 files changed

+21
-16
lines changed

4 files changed

+21
-16
lines changed

object_store/src/aws/builder.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub struct AmazonS3Builder {
160160
/// Copy if not exists
161161
copy_if_not_exists: Option<ConfigValue<S3CopyIfNotExists>>,
162162
/// Put precondition
163-
conditional_put: Option<ConfigValue<S3ConditionalPut>>,
163+
conditional_put: ConfigValue<S3ConditionalPut>,
164164
/// Ignore tags
165165
disable_tagging: ConfigValue<bool>,
166166
/// Encryption (See [`S3EncryptionConfigKey`])
@@ -525,7 +525,7 @@ impl AmazonS3Builder {
525525
self.copy_if_not_exists = Some(ConfigValue::Deferred(value.into()))
526526
}
527527
AmazonS3ConfigKey::ConditionalPut => {
528-
self.conditional_put = Some(ConfigValue::Deferred(value.into()))
528+
self.conditional_put = ConfigValue::Deferred(value.into())
529529
}
530530
AmazonS3ConfigKey::RequestPayer => {
531531
self.request_payer = ConfigValue::Deferred(value.into())
@@ -583,9 +583,7 @@ impl AmazonS3Builder {
583583
AmazonS3ConfigKey::CopyIfNotExists => {
584584
self.copy_if_not_exists.as_ref().map(ToString::to_string)
585585
}
586-
AmazonS3ConfigKey::ConditionalPut => {
587-
self.conditional_put.as_ref().map(ToString::to_string)
588-
}
586+
AmazonS3ConfigKey::ConditionalPut => Some(self.conditional_put.to_string()),
589587
AmazonS3ConfigKey::DisableTagging => Some(self.disable_tagging.to_string()),
590588
AmazonS3ConfigKey::RequestPayer => Some(self.request_payer.to_string()),
591589
AmazonS3ConfigKey::Encryption(key) => match key {
@@ -827,9 +825,10 @@ impl AmazonS3Builder {
827825
self
828826
}
829827

830-
/// Configure how to provide conditional put operations
828+
/// Configure how to provide conditional put operations.
829+
/// if not set, the default value will be `S3ConditionalPut::ETagMatch`
831830
pub fn with_conditional_put(mut self, config: S3ConditionalPut) -> Self {
832-
self.conditional_put = Some(config.into());
831+
self.conditional_put = config.into();
833832
self
834833
}
835834

@@ -905,7 +904,6 @@ impl AmazonS3Builder {
905904
let region = self.region.unwrap_or_else(|| "us-east-1".to_string());
906905
let checksum = self.checksum_algorithm.map(|x| x.get()).transpose()?;
907906
let copy_if_not_exists = self.copy_if_not_exists.map(|x| x.get()).transpose()?;
908-
let put_precondition = self.conditional_put.map(|x| x.get()).transpose()?;
909907

910908
let credentials = if let Some(credentials) = self.credentials {
911909
credentials
@@ -1045,7 +1043,7 @@ impl AmazonS3Builder {
10451043
disable_tagging: self.disable_tagging.get()?,
10461044
checksum,
10471045
copy_if_not_exists,
1048-
conditional_put: put_precondition,
1046+
conditional_put: self.conditional_put.get()?,
10491047
encryption_headers,
10501048
request_payer: self.request_payer.get()?,
10511049
};

object_store/src/aws/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ pub(crate) struct S3Config {
204204
pub disable_tagging: bool,
205205
pub checksum: Option<Checksum>,
206206
pub copy_if_not_exists: Option<S3CopyIfNotExists>,
207-
pub conditional_put: Option<S3ConditionalPut>,
207+
pub conditional_put: S3ConditionalPut,
208208
pub request_payer: bool,
209209
pub(super) encryption_headers: S3EncryptionHeaders,
210210
}

object_store/src/aws/mod.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ impl ObjectStore for AmazonS3 {
169169

170170
match (opts.mode, &self.client.config.conditional_put) {
171171
(PutMode::Overwrite, _) => request.idempotent(true).do_put().await,
172-
(PutMode::Create | PutMode::Update(_), None) => Err(Error::NotImplemented),
173-
(PutMode::Create, Some(S3ConditionalPut::ETagMatch)) => {
172+
(PutMode::Create, S3ConditionalPut::Disabled) => Err(Error::NotImplemented),
173+
(PutMode::Create, S3ConditionalPut::ETagMatch) => {
174174
match request.header(&IF_NONE_MATCH, "*").do_put().await {
175175
// Technically If-None-Match should return NotModified but some stores,
176176
// such as R2, instead return PreconditionFailed
@@ -184,11 +184,11 @@ impl ObjectStore for AmazonS3 {
184184
r => r,
185185
}
186186
}
187-
(PutMode::Create, Some(S3ConditionalPut::Dynamo(d))) => {
187+
(PutMode::Create, S3ConditionalPut::Dynamo(d)) => {
188188
d.conditional_op(&self.client, location, None, move || request.do_put())
189189
.await
190190
}
191-
(PutMode::Update(v), Some(put)) => {
191+
(PutMode::Update(v), put) => {
192192
let etag = v.e_tag.ok_or_else(|| Error::Generic {
193193
store: STORE,
194194
source: "ETag required for conditional put".to_string().into(),
@@ -221,6 +221,7 @@ impl ObjectStore for AmazonS3 {
221221
})
222222
.await
223223
}
224+
S3ConditionalPut::Disabled => Err(Error::NotImplemented),
224225
}
225226
}
226227
}
@@ -561,7 +562,7 @@ mod tests {
561562
let integration = config.build().unwrap();
562563
let config = &integration.client.config;
563564
let test_not_exists = config.copy_if_not_exists.is_some();
564-
let test_conditional_put = config.conditional_put.is_some();
565+
let test_conditional_put = config.conditional_put != S3ConditionalPut::Disabled;
565566

566567
put_get_delete_list(&integration).await;
567568
get_opts(&integration).await;

object_store/src/aws/precondition.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl Parse for S3CopyIfNotExists {
126126
/// Configure how to provide conditional put support for [`AmazonS3`].
127127
///
128128
/// [`AmazonS3`]: super::AmazonS3
129-
#[derive(Debug, Clone, Eq, PartialEq)]
129+
#[derive(Debug, Clone, Eq, PartialEq, Default)]
130130
#[allow(missing_copy_implementations)]
131131
#[non_exhaustive]
132132
pub enum S3ConditionalPut {
@@ -136,6 +136,7 @@ pub enum S3ConditionalPut {
136136
/// Encoded as `etag` ignoring whitespace
137137
///
138138
/// [HTTP precondition]: https://datatracker.ietf.org/doc/html/rfc9110#name-preconditions
139+
#[default]
139140
ETagMatch,
140141

141142
/// The name of a DynamoDB table to use for coordination
@@ -147,13 +148,17 @@ pub enum S3ConditionalPut {
147148
///
148149
/// This will use the same region, credentials and endpoint as configured for S3
149150
Dynamo(DynamoCommit),
151+
152+
/// Disable `conditional put`
153+
Disabled,
150154
}
151155

152156
impl std::fmt::Display for S3ConditionalPut {
153157
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154158
match self {
155159
Self::ETagMatch => write!(f, "etag"),
156160
Self::Dynamo(lock) => write!(f, "dynamo: {}", lock.table_name()),
161+
Self::Disabled => write!(f, "disabled"),
157162
}
158163
}
159164
}
@@ -162,6 +167,7 @@ impl S3ConditionalPut {
162167
fn from_str(s: &str) -> Option<Self> {
163168
match s.trim() {
164169
"etag" => Some(Self::ETagMatch),
170+
"disabled" => Some(Self::Disabled),
165171
trimmed => match trimmed.split_once(':')? {
166172
("dynamo", s) => Some(Self::Dynamo(DynamoCommit::from_str(s)?)),
167173
_ => None,

0 commit comments

Comments
 (0)