mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-06 21:33:14 +00:00
069194f553
* fix getobject content length resp * Fix regression in exception handling for non-existent key with enhanced compression predicate and metadata improvements (#915) * Initial plan * Fix GetObject regression by excluding error responses from compression The issue was that CompressionLayer was attempting to compress error responses, which could cause Content-Length header mismatches. By excluding 4xx and 5xx responses from compression, we ensure error responses (like NoSuchKey) are sent correctly without body truncation. Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> * Enhance NoSuchKey fix with improved compression predicate and comprehensive tests - Enhanced ShouldCompress predicate with size-based exclusion (< 256 bytes) - Added detailed documentation explaining the compression logic - Added debug logging for better observability - Created comprehensive test suite with 4 test cases: - test_get_deleted_object_returns_nosuchkey - test_head_deleted_object_returns_nosuchkey - test_get_nonexistent_object_returns_nosuchkey - test_multiple_gets_deleted_object - Added extensive inline documentation and comments - Created docs/fix-nosuchkey-regression.md with full analysis Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> * Add compression best practices documentation Added comprehensive guide covering: - Best practices for HTTP response compression - Common pitfalls and solutions - Performance considerations and trade-offs - Testing guidelines and examples - Monitoring and alerting recommendations - Migration guide for existing services Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> * fix * fmt * fmt * Fix/objectdelete (#917) * fix getobject content length resp * fix delete object --------- Co-authored-by: houseme <housemecn@gmail.com> * Add comprehensive analysis of NoSuchKey fix and related improvements Created detailed documentation analyzing: - HTTP compression layer fix (primary issue) - Content-length calculation fix from PR #917 - Delete object metadata fixes from PR #917 - How all components work together - Complete scenario walkthrough - Performance impact analysis - Testing strategy and deployment checklist This ties together all the changes in the PR branch including the merged improvements from PR #917. Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> * replace `once_cell` to `std` * fmt --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> Co-authored-by: houseme <housemecn@gmail.com> Co-authored-by: weisd <im@weisd.in> * fmt --------- Co-authored-by: weisd <weishidavip@163.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> Co-authored-by: weisd <im@weisd.in>
118 lines
3.9 KiB
Rust
118 lines
3.9 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use crate::{client::LockClient, types::LockId};
|
|
use std::sync::{Arc, LazyLock};
|
|
use tokio::sync::mpsc;
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct UnlockJob {
|
|
lock_id: LockId,
|
|
clients: Vec<Arc<dyn LockClient>>, // cloned Arcs; cheap and shares state
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct UnlockRuntime {
|
|
tx: mpsc::Sender<UnlockJob>,
|
|
}
|
|
|
|
// Global unlock runtime with background worker
|
|
static UNLOCK_RUNTIME: LazyLock<UnlockRuntime> = LazyLock::new(|| {
|
|
// Larger buffer to reduce contention during bursts
|
|
let (tx, mut rx) = mpsc::channel::<UnlockJob>(8192);
|
|
|
|
// Spawn background worker when first used; assumes a Tokio runtime is available
|
|
tokio::spawn(async move {
|
|
while let Some(job) = rx.recv().await {
|
|
// Best-effort release across clients; try all, success if any succeeds
|
|
let mut any_ok = false;
|
|
let lock_id = job.lock_id.clone();
|
|
for client in job.clients.into_iter() {
|
|
if client.release(&lock_id).await.unwrap_or(false) {
|
|
any_ok = true;
|
|
}
|
|
}
|
|
if !any_ok {
|
|
tracing::warn!("LockGuard background release failed for {}", lock_id);
|
|
} else {
|
|
tracing::debug!("LockGuard background released {}", lock_id);
|
|
}
|
|
}
|
|
});
|
|
|
|
UnlockRuntime { tx }
|
|
});
|
|
|
|
/// A RAII guard that releases the lock asynchronously when dropped.
|
|
#[derive(Debug)]
|
|
pub struct LockGuard {
|
|
lock_id: LockId,
|
|
clients: Vec<Arc<dyn LockClient>>,
|
|
/// If true, Drop will not try to release (used if user manually released).
|
|
disarmed: bool,
|
|
}
|
|
|
|
impl LockGuard {
|
|
pub(crate) fn new(lock_id: LockId, clients: Vec<Arc<dyn LockClient>>) -> Self {
|
|
Self {
|
|
lock_id,
|
|
clients,
|
|
disarmed: false,
|
|
}
|
|
}
|
|
|
|
/// Get the lock id associated with this guard
|
|
pub fn lock_id(&self) -> &LockId {
|
|
&self.lock_id
|
|
}
|
|
|
|
/// Manually disarm the guard so dropping it won't release the lock.
|
|
/// Call this if you explicitly released the lock elsewhere.
|
|
pub fn disarm(&mut self) {
|
|
self.disarmed = true;
|
|
}
|
|
}
|
|
|
|
impl Drop for LockGuard {
|
|
fn drop(&mut self) {
|
|
if self.disarmed {
|
|
return;
|
|
}
|
|
|
|
let job = UnlockJob {
|
|
lock_id: self.lock_id.clone(),
|
|
clients: self.clients.clone(),
|
|
};
|
|
|
|
// Try a non-blocking send to avoid panics in Drop
|
|
if let Err(err) = UNLOCK_RUNTIME.tx.try_send(job) {
|
|
// Channel full or closed; best-effort fallback: spawn a detached task
|
|
let lock_id = self.lock_id.clone();
|
|
let clients = self.clients.clone();
|
|
tracing::warn!("LockGuard channel send failed ({}), spawning fallback unlock task for {}", err, lock_id);
|
|
|
|
// If runtime is not available, this will panic; but in RustFS we are inside Tokio contexts.
|
|
let handle = tokio::spawn(async move {
|
|
let futures_iter = clients.into_iter().map(|client| {
|
|
let id = lock_id.clone();
|
|
async move { client.release(&id).await.unwrap_or(false) }
|
|
});
|
|
let _ = futures::future::join_all(futures_iter).await;
|
|
});
|
|
// Explicitly drop the JoinHandle to acknowledge detaching the task.
|
|
drop(handle);
|
|
}
|
|
}
|
|
}
|