mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-09 14:49:25 +00:00
73bde843d6
* refactor(common): introduce rustfs-data-usage core crate * refactor(concurrency): migrate workers crate into concurrency * refactor(crypto): migrate appauth token APIs into crypto * fix docs urls * remove unused crate * refactor(data-usage): switch consumers to rustfs-data-usage * chore(fmt): apply cargo fmt and lockfile sync * refactor(common): remove data_usage compatibility re-export * refactor(capacity): move capacity_scope to object-capacity * refactor(io-metrics): relocate internode metrics from common * refactor(common): decouple scanner report from madmin * chore(fmt): normalize import ordering after pre-commit * refactor(s3): split s3 types and ops crates * refactor(s3): centralize event version and safe parsing * refactor(s3): add op-event compatibility guardrails * refactor(s3): add runtime op-event mismatch observability * refactor(s3): extract delete event mapping helper * refactor(s3): extract put event mapping helper * refactor(s3): consolidate remaining event semantic helpers * refactor(s3): add op-event coverage checks and observability alerts * refactor(s3-ops): consolidate op-event semantic mapping * refactor(scanner): remove last_minute wrapper module * refactor(scanner): consolidate duplicated data usage models
142 lines
4.5 KiB
Rust
142 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 crate::{Event, integration::LiveEventBatch, notifier::EventNotifier};
|
|
use std::collections::VecDeque;
|
|
use std::sync::Arc;
|
|
use tokio::sync::{RwLock, broadcast};
|
|
|
|
const MAX_RECENT_LIVE_EVENTS: usize = 1024;
|
|
|
|
#[derive(Default)]
|
|
pub struct LiveEventHistory {
|
|
next_sequence: u64,
|
|
events: VecDeque<(u64, Arc<Event>)>,
|
|
}
|
|
|
|
impl LiveEventHistory {
|
|
pub fn record(&mut self, event: Arc<Event>) {
|
|
self.next_sequence = self.next_sequence.saturating_add(1);
|
|
self.events.push_back((self.next_sequence, event));
|
|
while self.events.len() > MAX_RECENT_LIVE_EVENTS {
|
|
self.events.pop_front();
|
|
}
|
|
}
|
|
|
|
pub fn snapshot_since(&self, after_sequence: u64, limit: usize) -> LiveEventBatch {
|
|
let mut events = Vec::new();
|
|
let mut next_sequence = after_sequence;
|
|
let mut truncated = false;
|
|
|
|
for (sequence, event) in self.events.iter() {
|
|
if *sequence <= after_sequence {
|
|
continue;
|
|
}
|
|
if events.len() >= limit {
|
|
truncated = true;
|
|
break;
|
|
}
|
|
next_sequence = *sequence;
|
|
events.push(event.clone());
|
|
}
|
|
|
|
LiveEventBatch {
|
|
events,
|
|
next_sequence,
|
|
truncated,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct NotifyPipeline {
|
|
notifier: Arc<EventNotifier>,
|
|
live_event_sender: broadcast::Sender<Arc<Event>>,
|
|
live_event_history: Arc<RwLock<LiveEventHistory>>,
|
|
}
|
|
|
|
impl NotifyPipeline {
|
|
pub fn new(
|
|
notifier: Arc<EventNotifier>,
|
|
live_event_sender: broadcast::Sender<Arc<Event>>,
|
|
live_event_history: Arc<RwLock<LiveEventHistory>>,
|
|
) -> Self {
|
|
Self {
|
|
notifier,
|
|
live_event_sender,
|
|
live_event_history,
|
|
}
|
|
}
|
|
|
|
pub fn has_live_listeners(&self) -> bool {
|
|
self.live_event_sender.receiver_count() > 0
|
|
}
|
|
|
|
pub fn subscribe_live_events(&self) -> broadcast::Receiver<Arc<Event>> {
|
|
self.live_event_sender.subscribe()
|
|
}
|
|
|
|
pub async fn recent_live_events_since(&self, after_sequence: u64, limit: usize) -> LiveEventBatch {
|
|
let history = self.live_event_history.read().await;
|
|
history.snapshot_since(after_sequence, limit.max(1))
|
|
}
|
|
|
|
pub async fn send_event(&self, event: Arc<Event>) {
|
|
self.live_event_history.write().await.record(event.clone());
|
|
let _ = self.live_event_sender.send(event.clone());
|
|
self.notifier.send(event).await;
|
|
}
|
|
}
|
|
|
|
pub type NotifyEventBridge = NotifyPipeline;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{LiveEventHistory, NotifyPipeline};
|
|
use crate::{Event, integration::NotificationMetrics, notifier::EventNotifier, rule_engine::NotifyRuleEngine};
|
|
use rustfs_s3_types::EventName;
|
|
use std::sync::Arc;
|
|
use tokio::sync::{RwLock, broadcast};
|
|
|
|
fn build_pipeline() -> NotifyPipeline {
|
|
let metrics = Arc::new(NotificationMetrics::new());
|
|
let notifier = Arc::new(EventNotifier::new(metrics, NotifyRuleEngine::new()));
|
|
let (live_event_sender, _) = broadcast::channel(16);
|
|
NotifyPipeline::new(notifier, live_event_sender, Arc::new(RwLock::new(LiveEventHistory::default())))
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn pipeline_reports_live_listener_subscription_state() {
|
|
let pipeline = build_pipeline();
|
|
assert!(!pipeline.has_live_listeners());
|
|
|
|
let _rx = pipeline.subscribe_live_events();
|
|
assert!(pipeline.has_live_listeners());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn pipeline_records_recent_live_events() {
|
|
let pipeline = build_pipeline();
|
|
let event = Arc::new(Event::new_test_event("bucket", "one", EventName::ObjectCreatedPut));
|
|
|
|
pipeline.send_event(event).await;
|
|
|
|
let batch = pipeline.recent_live_events_since(0, 16).await;
|
|
assert_eq!(batch.next_sequence, 1);
|
|
assert!(!batch.truncated);
|
|
assert_eq!(batch.events.len(), 1);
|
|
assert_eq!(batch.events[0].s3.object.key, "one");
|
|
}
|
|
}
|