5058: Retry if deserialization of remote response failed r=irevoire a=dureuill

v1.11.2 introduces a new error condition (instead of a possibly infinite wait): deserializing the body of a remote response can cause a timeout and fail immediately.

This PR makes it so that deserialization failures are inside the retry loop, meaning they obey the exponential backoff retry strategy.

Co-authored-by: Louis Dureuil <louis@meilisearch.com>
This commit is contained in:
meili-bors[bot] 2024-11-14 15:55:14 +00:00 committed by GitHub
commit cfaac6f7ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 18 additions and 12 deletions

View File

@ -58,7 +58,7 @@ pub enum EmbedErrorKind {
ManualEmbed(String), ManualEmbed(String),
#[error("model not found. Meilisearch will not automatically download models from the Ollama library, please pull the model manually{}", option_info(.0.as_deref(), "server replied with "))] #[error("model not found. Meilisearch will not automatically download models from the Ollama library, please pull the model manually{}", option_info(.0.as_deref(), "server replied with "))]
OllamaModelNotFoundError(Option<String>), OllamaModelNotFoundError(Option<String>),
#[error("error deserialization the response body as JSON:\n - {0}")] #[error("error deserializing the response body as JSON:\n - {0}")]
RestResponseDeserialization(std::io::Error), RestResponseDeserialization(std::io::Error),
#[error("expected a response containing {0} embeddings, got only {1}")] #[error("expected a response containing {0} embeddings, got only {1}")]
RestResponseEmbeddingCount(usize, usize), RestResponseEmbeddingCount(usize, usize),

View File

@ -252,12 +252,12 @@ where
for attempt in 0..10 { for attempt in 0..10 {
let response = request.clone().send_json(&body); let response = request.clone().send_json(&body);
let result = check_response(response, data.configuration_source); let result = check_response(response, data.configuration_source).and_then(|response| {
response_to_embedding(response, data, expected_count, expected_dimension)
});
let retry_duration = match result { let retry_duration = match result {
Ok(response) => { Ok(response) => return Ok(response),
return response_to_embedding(response, data, expected_count, expected_dimension)
}
Err(retry) => { Err(retry) => {
tracing::warn!("Failed: {}", retry.error); tracing::warn!("Failed: {}", retry.error);
if let Some(deadline) = deadline { if let Some(deadline) = deadline {
@ -289,6 +289,7 @@ where
let result = check_response(response, data.configuration_source); let result = check_response(response, data.configuration_source);
result.map_err(Retry::into_error).and_then(|response| { result.map_err(Retry::into_error).and_then(|response| {
response_to_embedding(response, data, expected_count, expected_dimension) response_to_embedding(response, data, expected_count, expected_dimension)
.map_err(Retry::into_error)
}) })
} }
@ -330,23 +331,28 @@ fn response_to_embedding(
data: &EmbedderData, data: &EmbedderData,
expected_count: usize, expected_count: usize,
expected_dimensions: Option<usize>, expected_dimensions: Option<usize>,
) -> Result<Vec<Embeddings<f32>>, EmbedError> { ) -> Result<Vec<Embeddings<f32>>, Retry> {
let response: serde_json::Value = let response: serde_json::Value = response
response.into_json().map_err(EmbedError::rest_response_deserialization)?; .into_json()
.map_err(EmbedError::rest_response_deserialization)
.map_err(Retry::retry_later)?;
let embeddings = data.response.extract_embeddings(response)?; let embeddings = data.response.extract_embeddings(response).map_err(Retry::give_up)?;
if embeddings.len() != expected_count { if embeddings.len() != expected_count {
return Err(EmbedError::rest_response_embedding_count(expected_count, embeddings.len())); return Err(Retry::give_up(EmbedError::rest_response_embedding_count(
expected_count,
embeddings.len(),
)));
} }
if let Some(dimensions) = expected_dimensions { if let Some(dimensions) = expected_dimensions {
for embedding in &embeddings { for embedding in &embeddings {
if embedding.dimension() != dimensions { if embedding.dimension() != dimensions {
return Err(EmbedError::rest_unexpected_dimension( return Err(Retry::give_up(EmbedError::rest_unexpected_dimension(
dimensions, dimensions,
embedding.dimension(), embedding.dimension(),
)); )));
} }
} }
} }