mirror of
https://github.com/ParrotXray/Mantis.git
synced 2026-08-24 18:50:28 +09:00
fix: cap concurrent GeoIP blocking threads with Semaphore
Root cause: WebSocketProvider opens 24 flow-stat WebSocket connections simultaneously. Each connection on every refresh calls get_ipv4/ipv6_flow_data, which calls join_all() on GeoIP lookups for every IP in the flow map. On cache miss, each lookup calls task::spawn_blocking unconditionally — so 24 connections × N flows × cold cache = unbounded OS thread creation. Fix: add OwnedSemaphorePermit (MAX_CONCURRENT_DB_LOOKUPS = 8) to GeoIpService. The permit is moved into spawn_blocking so it is held for the full duration of the blocking call and released when the closure returns. Cached hits and private IPs are unaffected (no semaphore needed). https://claude.ai/code/session_01Wen51749mAnwBEfh7XmvUw
This commit is contained in:
parent
7fe77d235b
commit
2c0a4a8b4d
@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use maxminddb::{geoip2, MaxMindDbError, Reader};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
use lru::LruCache;
|
||||
use std::num::NonZeroUsize;
|
||||
use tokio::task;
|
||||
@ -11,9 +11,12 @@ use tokio::task;
|
||||
use crate::model::geo_stats::GeoLocation;
|
||||
use crate::utils::ip_address;
|
||||
|
||||
const MAX_CONCURRENT_DB_LOOKUPS: usize = 8;
|
||||
|
||||
pub struct GeoIpService {
|
||||
reader: Arc<Reader<Vec<u8>>>,
|
||||
cache: Arc<RwLock<LruCache<IpAddr, Option<GeoLocation>>>>,
|
||||
lookup_sem: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl GeoIpService {
|
||||
@ -33,6 +36,7 @@ impl GeoIpService {
|
||||
Ok(Self {
|
||||
reader: Arc::new(reader),
|
||||
cache: Arc::new(RwLock::new(LruCache::new(cache_capacity))),
|
||||
lookup_sem: Arc::new(Semaphore::new(MAX_CONCURRENT_DB_LOOKUPS)),
|
||||
})
|
||||
}
|
||||
|
||||
@ -55,8 +59,15 @@ impl GeoIpService {
|
||||
}
|
||||
}
|
||||
|
||||
let permit = self.lookup_sem.clone().acquire_owned().await
|
||||
.map_err(|_| MaxMindDbError::InvalidDatabase {
|
||||
message: "GeoIP semaphore closed".to_string(),
|
||||
offset: None,
|
||||
})?;
|
||||
|
||||
let reader = self.reader.clone();
|
||||
let result = task::spawn_blocking(move || {
|
||||
let _permit = permit;
|
||||
Self::lookup_from_db_blocking(&reader, ip)
|
||||
})
|
||||
.await
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user