mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-05 12:57:42 +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>
136 lines
4.5 KiB
Rust
136 lines
4.5 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 std::sync::Arc;
|
|
use std::sync::LazyLock;
|
|
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
|
|
use tokio::sync::Notify;
|
|
|
|
/// Optimized notification pool to reduce memory overhead and thundering herd effects
|
|
/// Increased pool size for better performance under high concurrency
|
|
static NOTIFY_POOL: LazyLock<Vec<Arc<Notify>>> = LazyLock::new(|| (0..128).map(|_| Arc::new(Notify::new())).collect());
|
|
|
|
/// Optimized notification system for object locks
|
|
#[derive(Debug)]
|
|
pub struct OptimizedNotify {
|
|
/// Number of readers waiting
|
|
pub reader_waiters: AtomicU32,
|
|
/// Number of writers waiting
|
|
pub writer_waiters: AtomicU32,
|
|
/// Index into the global notify pool
|
|
pub notify_pool_index: AtomicUsize,
|
|
}
|
|
|
|
impl OptimizedNotify {
|
|
pub fn new() -> Self {
|
|
// Use random pool index to distribute load
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
let seed = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.map(|d| d.as_nanos() as u64)
|
|
.unwrap_or(0);
|
|
let pool_index = (seed as usize) % NOTIFY_POOL.len();
|
|
|
|
Self {
|
|
reader_waiters: AtomicU32::new(0),
|
|
writer_waiters: AtomicU32::new(0),
|
|
notify_pool_index: AtomicUsize::new(pool_index),
|
|
}
|
|
}
|
|
|
|
/// Notify waiting readers
|
|
pub fn notify_readers(&self) {
|
|
if self.reader_waiters.load(Ordering::Acquire) > 0 {
|
|
let pool_index = self.notify_pool_index.load(Ordering::Relaxed) % NOTIFY_POOL.len();
|
|
NOTIFY_POOL[pool_index].notify_waiters();
|
|
}
|
|
}
|
|
|
|
/// Notify one waiting writer
|
|
pub fn notify_writer(&self) {
|
|
if self.writer_waiters.load(Ordering::Acquire) > 0 {
|
|
let pool_index = self.notify_pool_index.load(Ordering::Relaxed) % NOTIFY_POOL.len();
|
|
NOTIFY_POOL[pool_index].notify_one();
|
|
}
|
|
}
|
|
|
|
/// Wait for reader notification
|
|
pub async fn wait_for_read(&self) {
|
|
self.reader_waiters.fetch_add(1, Ordering::AcqRel);
|
|
let pool_index = self.notify_pool_index.load(Ordering::Relaxed) % NOTIFY_POOL.len();
|
|
NOTIFY_POOL[pool_index].notified().await;
|
|
self.reader_waiters.fetch_sub(1, Ordering::AcqRel);
|
|
}
|
|
|
|
/// Wait for writer notification
|
|
pub async fn wait_for_write(&self) {
|
|
self.writer_waiters.fetch_add(1, Ordering::AcqRel);
|
|
let pool_index = self.notify_pool_index.load(Ordering::Relaxed) % NOTIFY_POOL.len();
|
|
NOTIFY_POOL[pool_index].notified().await;
|
|
self.writer_waiters.fetch_sub(1, Ordering::AcqRel);
|
|
}
|
|
|
|
/// Check if anyone is waiting
|
|
pub fn has_waiters(&self) -> bool {
|
|
self.reader_waiters.load(Ordering::Acquire) > 0 || self.writer_waiters.load(Ordering::Acquire) > 0
|
|
}
|
|
}
|
|
|
|
impl Default for OptimizedNotify {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use tokio::time::{Duration, timeout};
|
|
|
|
#[tokio::test]
|
|
async fn test_optimized_notify() {
|
|
let notify = OptimizedNotify::new();
|
|
|
|
// Test that notification works
|
|
let notify_clone = Arc::new(notify);
|
|
let notify_for_task = notify_clone.clone();
|
|
|
|
let handle = tokio::spawn(async move {
|
|
notify_for_task.wait_for_read().await;
|
|
});
|
|
|
|
// Give some time for the task to start waiting
|
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
notify_clone.notify_readers();
|
|
|
|
// Should complete quickly
|
|
assert!(timeout(Duration::from_millis(100), handle).await.is_ok());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_writer_notification() {
|
|
let notify = Arc::new(OptimizedNotify::new());
|
|
let notify_for_task = notify.clone();
|
|
|
|
let handle = tokio::spawn(async move {
|
|
notify_for_task.wait_for_write().await;
|
|
});
|
|
|
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
notify.notify_writer();
|
|
|
|
assert!(timeout(Duration::from_millis(100), handle).await.is_ok());
|
|
}
|
|
}
|