mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-24 00:52:24 +00:00
fix(rust): treat ConditionNotMet as an existing blob on sync Azure Blob writes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
b5548082c6
commit
0e281d458f
2 changed files with 47 additions and 10 deletions
|
|
@ -19,7 +19,6 @@ use url::Url;
|
|||
|
||||
use crate::credential::AzureBlobCredential;
|
||||
|
||||
/// Synchronous methods block on `runtime` and therefore must run outside of it
|
||||
pub struct AzureBlobCache<C> {
|
||||
container: BlobContainerClient,
|
||||
codec: C,
|
||||
|
|
@ -54,14 +53,15 @@ impl<C: CacheCodec> AzureBlobCache<C> {
|
|||
codec: C,
|
||||
runtime: Handle,
|
||||
) -> Result<Self, Error> {
|
||||
let mut url = Url::parse(account_url).map_err(|_| Error::Unavailable)?;
|
||||
let account_url = url.as_str().trim_end_matches('/').to_string();
|
||||
url.path_segments_mut()
|
||||
.map_err(|()| Error::Unavailable)?
|
||||
.pop_if_empty()
|
||||
.push(container);
|
||||
let account_url = Url::parse(account_url)
|
||||
.map_err(|_| Error::Unavailable)?
|
||||
.as_str()
|
||||
.trim_end_matches('/')
|
||||
.to_string();
|
||||
let container_url =
|
||||
Url::parse(&format!("{account_url}/{container}")).map_err(|_| Error::Unavailable)?;
|
||||
let client = BlobContainerClient::new(
|
||||
url,
|
||||
container_url,
|
||||
credential,
|
||||
Some(BlobContainerClientOptions {
|
||||
client_options,
|
||||
|
|
@ -108,7 +108,7 @@ impl<C: CacheCodec> AzureBlobCache<C> {
|
|||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) if is_storage_error(&error, StorageErrorCode::BlobAlreadyExists) => Ok(()),
|
||||
Err(error) if !overwrite && is_already_present(&error) => Ok(()),
|
||||
Err(_) => Err(Error::Unavailable),
|
||||
}
|
||||
}
|
||||
|
|
@ -153,6 +153,11 @@ impl<C: CacheCodec> AzureBlobCache<C> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_already_present(error: &azure_core::Error) -> bool {
|
||||
is_storage_error(error, StorageErrorCode::BlobAlreadyExists)
|
||||
|| is_storage_error(error, StorageErrorCode::ConditionNotMet)
|
||||
}
|
||||
|
||||
fn is_storage_error(error: &azure_core::Error, code: StorageErrorCode) -> bool {
|
||||
matches!(
|
||||
error.kind(),
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ struct FakeState {
|
|||
blobs: BTreeMap<String, Vec<u8>>,
|
||||
requests: Vec<RecordedRequest>,
|
||||
failing: bool,
|
||||
precondition_conflicts: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
|
|
@ -79,6 +80,10 @@ impl FakeBlobService {
|
|||
self.state.lock().unwrap().failing = failing;
|
||||
}
|
||||
|
||||
fn set_precondition_conflicts(&self, enabled: bool) {
|
||||
self.state.lock().unwrap().precondition_conflicts = enabled;
|
||||
}
|
||||
|
||||
fn requests(&self) -> Vec<RecordedRequest> {
|
||||
self.state.lock().unwrap().requests.clone()
|
||||
}
|
||||
|
|
@ -158,7 +163,15 @@ impl HttpClient for FakeBlobService {
|
|||
}
|
||||
(Method::Put, false, Some(name)) => {
|
||||
if if_none_match.as_deref() == Some("*") && state.blobs.contains_key(&name) {
|
||||
Self::respond(StatusCode::Conflict, Some("BlobAlreadyExists"), Vec::new())
|
||||
if state.precondition_conflicts {
|
||||
Self::respond(
|
||||
StatusCode::PreconditionFailed,
|
||||
Some("ConditionNotMet"),
|
||||
Vec::new(),
|
||||
)
|
||||
} else {
|
||||
Self::respond(StatusCode::Conflict, Some("BlobAlreadyExists"), Vec::new())
|
||||
}
|
||||
} else {
|
||||
let bytes = match request.body() {
|
||||
Body::Bytes(bytes) => bytes.to_vec(),
|
||||
|
|
@ -369,6 +382,25 @@ fn sync_set_does_not_overwrite_an_existing_blob() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_set_treats_a_precondition_conflict_as_an_existing_blob() {
|
||||
let fixture = Fixture::new(FakeBlobService::default());
|
||||
fixture.service.set_precondition_conflicts(true);
|
||||
fixture
|
||||
.cache
|
||||
.set_cache("key", entry(json!({"v": "first"})), &no_ttl())
|
||||
.unwrap();
|
||||
fixture
|
||||
.cache
|
||||
.set_cache("key", entry(json!({"v": "second"})), &no_ttl())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
fixture.stored_json("key")["response"],
|
||||
json!({"v": "first"})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_set_overwrites_an_existing_blob() {
|
||||
let fixture = Fixture::new(FakeBlobService::default());
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue