Files
rustfs/crates/lock/src/fast_lock/optimized_notify.rs
T
houseme 069194f553 Fix/getobjectlength (#920)
* 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>
2025-11-24 18:56:34 +08:00

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());
}
}