use super::consumer::MemoryConsumer;
use super::grant::{GrantReleaser, MemoryGrant};
use super::region::MemoryRegion;
use super::stats::{BufferStats, PressureLevel};
use parking_lot::RwLock;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
const DEFAULT_MEMORY_FRACTION: f64 = 0.75;
#[derive(Debug, Clone)]
pub struct BufferManagerConfig {
pub budget: usize,
pub soft_limit_fraction: f64,
pub evict_limit_fraction: f64,
pub hard_limit_fraction: f64,
pub background_eviction: bool,
pub spill_path: Option<PathBuf>,
}
impl BufferManagerConfig {
#[must_use]
pub fn detect_system_memory() -> usize {
#[cfg(miri)]
{
return Self::fallback_system_memory();
}
#[cfg(not(miri))]
{
#[cfg(target_os = "windows")]
{
Self::fallback_system_memory()
}
#[cfg(target_os = "linux")]
{
if let Ok(contents) = std::fs::read_to_string("/proc/meminfo") {
for line in contents.lines() {
if line.starts_with("MemTotal:")
&& let Some(kb_str) = line.split_whitespace().nth(1)
&& let Ok(kb) = kb_str.parse::<usize>()
{
return kb * 1024;
}
}
}
Self::fallback_system_memory()
}
#[cfg(target_os = "macos")]
{
Self::fallback_system_memory()
}
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
{
Self::fallback_system_memory()
}
}
}
fn fallback_system_memory() -> usize {
1024 * 1024 * 1024
}
#[must_use]
pub fn with_budget(budget: usize) -> Self {
Self {
budget,
..Default::default()
}
}
}
impl Default for BufferManagerConfig {
fn default() -> Self {
let system_memory = Self::detect_system_memory();
Self {
budget: (system_memory as f64 * DEFAULT_MEMORY_FRACTION) as usize,
soft_limit_fraction: 0.70,
evict_limit_fraction: 0.85,
hard_limit_fraction: 0.95,
background_eviction: false, spill_path: None,
}
}
}
pub struct BufferManager {
config: BufferManagerConfig,
allocated: AtomicUsize,
region_allocated: [AtomicUsize; 4],
consumers: RwLock<Vec<Arc<dyn MemoryConsumer>>>,
soft_limit: usize,
evict_limit: usize,
hard_limit: usize,
shutdown: AtomicBool,
}
impl BufferManager {
#[must_use]
pub fn new(config: BufferManagerConfig) -> Arc<Self> {
let soft_limit = (config.budget as f64 * config.soft_limit_fraction) as usize;
let evict_limit = (config.budget as f64 * config.evict_limit_fraction) as usize;
let hard_limit = (config.budget as f64 * config.hard_limit_fraction) as usize;
Arc::new(Self {
config,
allocated: AtomicUsize::new(0),
region_allocated: [
AtomicUsize::new(0),
AtomicUsize::new(0),
AtomicUsize::new(0),
AtomicUsize::new(0),
],
consumers: RwLock::new(Vec::new()),
soft_limit,
evict_limit,
hard_limit,
shutdown: AtomicBool::new(false),
})
}
#[must_use]
pub fn with_defaults() -> Arc<Self> {
Self::new(BufferManagerConfig::default())
}
#[must_use]
pub fn with_budget(budget: usize) -> Arc<Self> {
Self::new(BufferManagerConfig::with_budget(budget))
}
pub fn try_allocate(
self: &Arc<Self>,
size: usize,
region: MemoryRegion,
) -> Option<MemoryGrant> {
let current = self.allocated.load(Ordering::Relaxed);
if current + size > self.hard_limit {
self.run_eviction_cycle(true);
let current = self.allocated.load(Ordering::Relaxed);
if current + size > self.hard_limit {
return None;
}
}
self.allocated.fetch_add(size, Ordering::Relaxed);
self.region_allocated[region.index()].fetch_add(size, Ordering::Relaxed);
self.check_pressure();
Some(MemoryGrant::new(
Arc::clone(self) as Arc<dyn GrantReleaser>,
size,
region,
))
}
#[must_use]
pub fn pressure_level(&self) -> PressureLevel {
let current = self.allocated.load(Ordering::Relaxed);
self.compute_pressure_level(current)
}
#[must_use]
pub fn stats(&self) -> BufferStats {
let total_allocated = self.allocated.load(Ordering::Relaxed);
BufferStats {
budget: self.config.budget,
total_allocated,
region_allocated: [
self.region_allocated[0].load(Ordering::Relaxed),
self.region_allocated[1].load(Ordering::Relaxed),
self.region_allocated[2].load(Ordering::Relaxed),
self.region_allocated[3].load(Ordering::Relaxed),
],
pressure_level: self.compute_pressure_level(total_allocated),
consumer_count: self.consumers.read().len(),
}
}
pub fn register_consumer(&self, consumer: Arc<dyn MemoryConsumer>) {
self.consumers.write().push(consumer);
}
pub fn unregister_consumer(&self, name: &str) {
self.consumers.write().retain(|c| c.name() != name);
}
pub fn evict_to_target(&self, target_bytes: usize) -> usize {
let current = self.allocated.load(Ordering::Relaxed);
if current <= target_bytes {
return 0;
}
let to_free = current - target_bytes;
self.run_eviction_internal(to_free)
}
pub fn spill_all(&self) -> usize {
let consumers = self.consumers.read();
let mut total_freed = 0;
for consumer in consumers.iter() {
if consumer.can_spill()
&& let Ok(freed) = consumer.spill(usize::MAX)
{
total_freed += freed;
}
}
total_freed
}
#[must_use]
pub fn config(&self) -> &BufferManagerConfig {
&self.config
}
#[must_use]
pub fn budget(&self) -> usize {
self.config.budget
}
#[must_use]
pub fn allocated(&self) -> usize {
self.allocated.load(Ordering::Relaxed)
}
#[must_use]
pub fn available(&self) -> usize {
self.config
.budget
.saturating_sub(self.allocated.load(Ordering::Relaxed))
}
pub fn shutdown(&self) {
self.shutdown.store(true, Ordering::Relaxed);
}
fn compute_pressure_level(&self, current: usize) -> PressureLevel {
if current >= self.hard_limit {
PressureLevel::Critical
} else if current >= self.evict_limit {
PressureLevel::High
} else if current >= self.soft_limit {
PressureLevel::Moderate
} else {
PressureLevel::Normal
}
}
fn check_pressure(&self) {
let level = self.pressure_level();
if level.requires_eviction() {
let aggressive = level >= PressureLevel::High;
self.run_eviction_cycle(aggressive);
}
}
fn run_eviction_cycle(&self, aggressive: bool) -> usize {
let target = if aggressive {
self.soft_limit
} else {
self.evict_limit
};
let current = self.allocated.load(Ordering::Relaxed);
if current <= target {
return 0;
}
let to_free = current - target;
self.run_eviction_internal(to_free)
}
fn run_eviction_internal(&self, to_free: usize) -> usize {
let consumers = self.consumers.read();
let mut sorted: Vec<_> = consumers.iter().collect();
sorted.sort_by_key(|c| c.eviction_priority());
let mut total_freed = 0;
for consumer in &sorted {
if total_freed >= to_free {
break;
}
let remaining = to_free - total_freed;
let consumer_usage = consumer.memory_usage();
let target_evict = remaining.min(consumer_usage / 2);
if target_evict > 0 {
let freed = consumer.evict(target_evict);
total_freed += freed;
}
}
if total_freed < to_free {
for consumer in &sorted {
if total_freed >= to_free {
break;
}
if !consumer.can_spill() {
continue;
}
let remaining = to_free - total_freed;
match consumer.spill(remaining) {
Ok(freed) => total_freed += freed,
Err(_) => continue,
}
}
}
total_freed
}
}
impl GrantReleaser for BufferManager {
fn release(&self, size: usize, region: MemoryRegion) {
self.allocated.fetch_sub(size, Ordering::Relaxed);
self.region_allocated[region.index()].fetch_sub(size, Ordering::Relaxed);
}
fn try_allocate_raw(&self, size: usize, region: MemoryRegion) -> bool {
let current = self.allocated.load(Ordering::Relaxed);
if current + size > self.hard_limit {
self.run_eviction_cycle(true);
let current = self.allocated.load(Ordering::Relaxed);
if current + size > self.hard_limit {
return false;
}
}
self.allocated.fetch_add(size, Ordering::Relaxed);
self.region_allocated[region.index()].fetch_add(size, Ordering::Relaxed);
true
}
}
impl Drop for BufferManager {
fn drop(&mut self) {
self.shutdown.store(true, Ordering::Relaxed);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::buffer::consumer::priorities;
use std::sync::atomic::AtomicUsize;
struct TestConsumer {
name: String,
usage: AtomicUsize,
priority: u8,
region: MemoryRegion,
evicted: AtomicUsize,
}
impl TestConsumer {
fn new(name: &str, usage: usize, priority: u8, region: MemoryRegion) -> Arc<Self> {
Arc::new(Self {
name: name.to_string(),
usage: AtomicUsize::new(usage),
priority,
region,
evicted: AtomicUsize::new(0),
})
}
}
impl MemoryConsumer for TestConsumer {
fn name(&self) -> &str {
&self.name
}
fn memory_usage(&self) -> usize {
self.usage.load(Ordering::Relaxed)
}
fn eviction_priority(&self) -> u8 {
self.priority
}
fn region(&self) -> MemoryRegion {
self.region
}
fn evict(&self, target_bytes: usize) -> usize {
let current = self.usage.load(Ordering::Relaxed);
let to_evict = target_bytes.min(current);
self.usage.fetch_sub(to_evict, Ordering::Relaxed);
self.evicted.fetch_add(to_evict, Ordering::Relaxed);
to_evict
}
}
#[test]
fn test_basic_allocation() {
let config = BufferManagerConfig {
budget: 1024 * 1024, ..Default::default()
};
let manager = BufferManager::new(config);
let grant = manager.try_allocate(1024, MemoryRegion::ExecutionBuffers);
assert!(grant.is_some());
assert_eq!(manager.stats().total_allocated, 1024);
}
#[test]
fn test_grant_raii_release() {
let config = BufferManagerConfig {
budget: 1024,
..Default::default()
};
let manager = BufferManager::new(config);
{
let _grant = manager.try_allocate(512, MemoryRegion::ExecutionBuffers);
assert_eq!(manager.stats().total_allocated, 512);
}
assert_eq!(manager.stats().total_allocated, 0);
}
#[test]
fn test_pressure_levels() {
let config = BufferManagerConfig {
budget: 1000,
soft_limit_fraction: 0.70,
evict_limit_fraction: 0.85,
hard_limit_fraction: 0.95,
background_eviction: false,
spill_path: None,
};
let manager = BufferManager::new(config);
assert_eq!(manager.pressure_level(), PressureLevel::Normal);
let _g1 = manager.try_allocate(700, MemoryRegion::ExecutionBuffers);
assert_eq!(manager.pressure_level(), PressureLevel::Moderate);
let _g2 = manager.try_allocate(150, MemoryRegion::ExecutionBuffers);
assert_eq!(manager.pressure_level(), PressureLevel::High);
}
#[test]
fn test_region_tracking() {
let config = BufferManagerConfig {
budget: 10000,
..Default::default()
};
let manager = BufferManager::new(config);
let _g1 = manager.try_allocate(100, MemoryRegion::GraphStorage);
let _g2 = manager.try_allocate(200, MemoryRegion::IndexBuffers);
let _g3 = manager.try_allocate(300, MemoryRegion::ExecutionBuffers);
let stats = manager.stats();
assert_eq!(stats.region_usage(MemoryRegion::GraphStorage), 100);
assert_eq!(stats.region_usage(MemoryRegion::IndexBuffers), 200);
assert_eq!(stats.region_usage(MemoryRegion::ExecutionBuffers), 300);
assert_eq!(stats.total_allocated, 600);
}
#[test]
fn test_consumer_registration() {
let manager = BufferManager::with_budget(10000);
let consumer = TestConsumer::new(
"test",
1000,
priorities::INDEX_BUFFERS,
MemoryRegion::IndexBuffers,
);
manager.register_consumer(consumer);
assert_eq!(manager.stats().consumer_count, 1);
manager.unregister_consumer("test");
assert_eq!(manager.stats().consumer_count, 0);
}
#[test]
fn test_eviction_ordering() {
let manager = BufferManager::with_budget(10000);
let low_priority = TestConsumer::new(
"low",
500,
priorities::SPILL_STAGING,
MemoryRegion::SpillStaging,
);
let high_priority = TestConsumer::new(
"high",
500,
priorities::ACTIVE_TRANSACTION,
MemoryRegion::ExecutionBuffers,
);
manager.register_consumer(Arc::clone(&low_priority) as Arc<dyn MemoryConsumer>);
manager.register_consumer(Arc::clone(&high_priority) as Arc<dyn MemoryConsumer>);
manager.allocated.store(1000, Ordering::Relaxed);
let freed = manager.evict_to_target(700);
assert!(low_priority.evicted.load(Ordering::Relaxed) > 0);
assert!(freed > 0);
}
#[test]
fn test_hard_limit_blocking() {
let config = BufferManagerConfig {
budget: 1000,
soft_limit_fraction: 0.70,
evict_limit_fraction: 0.85,
hard_limit_fraction: 0.95,
background_eviction: false,
spill_path: None,
};
let manager = BufferManager::new(config);
let _g1 = manager.try_allocate(950, MemoryRegion::ExecutionBuffers);
let g2 = manager.try_allocate(100, MemoryRegion::ExecutionBuffers);
assert!(g2.is_none());
}
#[test]
fn test_available_memory() {
let manager = BufferManager::with_budget(1000);
assert_eq!(manager.available(), 1000);
let _g = manager.try_allocate(300, MemoryRegion::ExecutionBuffers);
assert_eq!(manager.available(), 700);
}
struct SpillableConsumer {
name: String,
usage: AtomicUsize,
priority: u8,
region: MemoryRegion,
evicted: AtomicUsize,
spilled: AtomicUsize,
spillable: bool,
evict_returns_zero: bool,
}
impl SpillableConsumer {
fn new(
name: &str,
usage: usize,
priority: u8,
region: MemoryRegion,
spillable: bool,
) -> Arc<Self> {
Arc::new(Self {
name: name.to_string(),
usage: AtomicUsize::new(usage),
priority,
region,
evicted: AtomicUsize::new(0),
spilled: AtomicUsize::new(0),
spillable,
evict_returns_zero: false,
})
}
fn new_evict_fails(
name: &str,
usage: usize,
priority: u8,
region: MemoryRegion,
spillable: bool,
) -> Arc<Self> {
Arc::new(Self {
name: name.to_string(),
usage: AtomicUsize::new(usage),
priority,
region,
evicted: AtomicUsize::new(0),
spilled: AtomicUsize::new(0),
spillable,
evict_returns_zero: true,
})
}
}
impl MemoryConsumer for SpillableConsumer {
fn name(&self) -> &str {
&self.name
}
fn memory_usage(&self) -> usize {
self.usage.load(Ordering::Relaxed)
}
fn eviction_priority(&self) -> u8 {
self.priority
}
fn region(&self) -> MemoryRegion {
self.region
}
fn evict(&self, target_bytes: usize) -> usize {
if self.evict_returns_zero {
return 0;
}
let current = self.usage.load(Ordering::Relaxed);
let to_evict = target_bytes.min(current);
self.usage.fetch_sub(to_evict, Ordering::Relaxed);
self.evicted.fetch_add(to_evict, Ordering::Relaxed);
to_evict
}
fn can_spill(&self) -> bool {
self.spillable
}
fn spill(
&self,
target_bytes: usize,
) -> Result<usize, crate::memory::buffer::consumer::SpillError> {
if !self.spillable {
return Err(crate::memory::buffer::consumer::SpillError::NotSupported);
}
let current = self.usage.load(Ordering::Relaxed);
let to_spill = target_bytes.min(current);
self.usage.fetch_sub(to_spill, Ordering::Relaxed);
self.spilled.fetch_add(to_spill, Ordering::Relaxed);
Ok(to_spill)
}
}
#[test]
fn test_spill_all_calls_spillable_consumers() {
let manager = BufferManager::with_budget(10000);
let spillable = SpillableConsumer::new(
"spillable",
500,
priorities::QUERY_CACHE,
MemoryRegion::ExecutionBuffers,
true,
);
let non_spillable = SpillableConsumer::new(
"non_spillable",
500,
priorities::QUERY_CACHE,
MemoryRegion::ExecutionBuffers,
false,
);
manager.register_consumer(Arc::clone(&spillable) as Arc<dyn MemoryConsumer>);
manager.register_consumer(Arc::clone(&non_spillable) as Arc<dyn MemoryConsumer>);
let freed = manager.spill_all();
assert_eq!(freed, 500);
assert_eq!(spillable.spilled.load(Ordering::Relaxed), 500);
assert_eq!(non_spillable.spilled.load(Ordering::Relaxed), 0);
}
#[test]
fn test_spill_all_skips_non_spillable() {
let manager = BufferManager::with_budget(10000);
let consumer = SpillableConsumer::new(
"no_spill",
1000,
priorities::INDEX_BUFFERS,
MemoryRegion::IndexBuffers,
false,
);
manager.register_consumer(Arc::clone(&consumer) as Arc<dyn MemoryConsumer>);
assert_eq!(manager.spill_all(), 0);
assert_eq!(consumer.memory_usage(), 1000);
}
#[test]
fn test_eviction_falls_back_to_spill() {
let manager = BufferManager::with_budget(10000);
let consumer = SpillableConsumer::new_evict_fails(
"spill_fallback",
1000,
priorities::QUERY_CACHE,
MemoryRegion::ExecutionBuffers,
true,
);
manager.register_consumer(Arc::clone(&consumer) as Arc<dyn MemoryConsumer>);
manager.allocated.store(2000, Ordering::Relaxed);
let freed = manager.evict_to_target(1500);
assert_eq!(consumer.evicted.load(Ordering::Relaxed), 0);
assert!(consumer.spilled.load(Ordering::Relaxed) > 0);
assert!(freed > 0);
}
#[test]
fn test_eviction_no_spill_when_sufficient() {
let manager = BufferManager::with_budget(10000);
let consumer = SpillableConsumer::new(
"eviction_enough",
1000,
priorities::QUERY_CACHE,
MemoryRegion::ExecutionBuffers,
true,
);
manager.register_consumer(Arc::clone(&consumer) as Arc<dyn MemoryConsumer>);
manager.allocated.store(1200, Ordering::Relaxed);
let freed = manager.evict_to_target(1000);
assert_eq!(freed, 200);
assert_eq!(consumer.spilled.load(Ordering::Relaxed), 0);
}
#[test]
fn test_eviction_spill_skips_non_spillable() {
let manager = BufferManager::with_budget(10000);
let consumer = SpillableConsumer::new_evict_fails(
"no_spill",
1000,
priorities::QUERY_CACHE,
MemoryRegion::ExecutionBuffers,
false,
);
manager.register_consumer(Arc::clone(&consumer) as Arc<dyn MemoryConsumer>);
manager.allocated.store(2000, Ordering::Relaxed);
let freed = manager.evict_to_target(1500);
assert_eq!(freed, 0);
assert_eq!(consumer.memory_usage(), 1000);
}
#[test]
fn alix_with_defaults_creates_manager() {
let manager = BufferManager::with_defaults();
assert!(manager.budget() > 0);
assert_eq!(manager.allocated(), 0);
assert_eq!(manager.available(), manager.budget());
}
#[test]
fn gus_config_accessor_returns_budget() {
let manager = BufferManager::with_budget(4096);
let config = manager.config();
assert_eq!(config.budget, 4096);
assert!(!config.background_eviction);
assert!(config.spill_path.is_none());
}
#[test]
fn vincent_shutdown_sets_flag() {
let manager = BufferManager::with_budget(1000);
manager.shutdown();
assert_eq!(manager.allocated(), 0);
}
#[test]
fn jules_critical_pressure_level() {
let config = BufferManagerConfig {
budget: 1000,
soft_limit_fraction: 0.70,
evict_limit_fraction: 0.85,
hard_limit_fraction: 0.95,
background_eviction: false,
spill_path: None,
};
let manager = BufferManager::new(config);
manager.allocated.store(960, Ordering::Relaxed);
assert_eq!(manager.pressure_level(), PressureLevel::Critical);
}
#[test]
fn mia_evict_to_target_already_below() {
let manager = BufferManager::with_budget(10000);
let freed = manager.evict_to_target(5000);
assert_eq!(freed, 0);
}
#[test]
fn butch_try_allocate_raw_success() {
let config = BufferManagerConfig {
budget: 1000,
soft_limit_fraction: 0.70,
evict_limit_fraction: 0.85,
hard_limit_fraction: 0.95,
background_eviction: false,
spill_path: None,
};
let manager = BufferManager::new(config);
let success = manager.try_allocate_raw(100, MemoryRegion::GraphStorage);
assert!(success);
assert_eq!(manager.allocated(), 100);
assert_eq!(
manager.stats().region_usage(MemoryRegion::GraphStorage),
100
);
}
#[test]
fn django_try_allocate_raw_fails_at_hard_limit() {
let config = BufferManagerConfig {
budget: 1000,
soft_limit_fraction: 0.70,
evict_limit_fraction: 0.85,
hard_limit_fraction: 0.95,
background_eviction: false,
spill_path: None,
};
let manager = BufferManager::new(config);
manager.allocated.store(940, Ordering::Relaxed);
let success = manager.try_allocate_raw(100, MemoryRegion::ExecutionBuffers);
assert!(!success);
}
#[test]
fn shosanna_drop_sets_shutdown() {
let manager = BufferManager::with_budget(512);
drop(manager);
}
#[test]
fn hans_eviction_with_zero_usage_consumer() {
let manager = BufferManager::with_budget(10000);
let consumer = TestConsumer::new(
"empty",
0,
priorities::SPILL_STAGING,
MemoryRegion::SpillStaging,
);
manager.register_consumer(Arc::clone(&consumer) as Arc<dyn MemoryConsumer>);
manager.allocated.store(500, Ordering::Relaxed);
let freed = manager.evict_to_target(200);
assert_eq!(consumer.evicted.load(Ordering::Relaxed), 0);
assert_eq!(freed, 0);
}
#[test]
fn beatrix_grant_releaser_release_decrements() {
let config = BufferManagerConfig {
budget: 1000,
soft_limit_fraction: 0.70,
evict_limit_fraction: 0.85,
hard_limit_fraction: 0.95,
background_eviction: false,
spill_path: None,
};
let manager = BufferManager::new(config);
assert!(manager.try_allocate_raw(200, MemoryRegion::IndexBuffers));
assert_eq!(manager.allocated(), 200);
manager.release(200, MemoryRegion::IndexBuffers);
assert_eq!(manager.allocated(), 0);
assert_eq!(manager.stats().region_usage(MemoryRegion::IndexBuffers), 0);
}
struct FailingSpillConsumer {
name: String,
usage: AtomicUsize,
priority: u8,
region: MemoryRegion,
}
impl FailingSpillConsumer {
fn new(name: &str, usage: usize, priority: u8, region: MemoryRegion) -> Arc<Self> {
Arc::new(Self {
name: name.to_string(),
usage: AtomicUsize::new(usage),
priority,
region,
})
}
}
impl MemoryConsumer for FailingSpillConsumer {
fn name(&self) -> &str {
&self.name
}
fn memory_usage(&self) -> usize {
self.usage.load(Ordering::Relaxed)
}
fn eviction_priority(&self) -> u8 {
self.priority
}
fn region(&self) -> MemoryRegion {
self.region
}
fn evict(&self, _target_bytes: usize) -> usize {
0 }
fn can_spill(&self) -> bool {
true
}
fn spill(
&self,
_target_bytes: usize,
) -> Result<usize, crate::memory::buffer::consumer::SpillError> {
Err(crate::memory::buffer::consumer::SpillError::IoError(
"disk full".to_string(),
))
}
}
#[test]
fn vincent_spill_error_continues_to_next_consumer() {
let manager = BufferManager::with_budget(10000);
let failing = FailingSpillConsumer::new(
"failing_spill",
500,
priorities::SPILL_STAGING,
MemoryRegion::SpillStaging,
);
let working = SpillableConsumer::new_evict_fails(
"working_spill",
500,
priorities::QUERY_CACHE,
MemoryRegion::ExecutionBuffers,
true,
);
manager.register_consumer(Arc::clone(&failing) as Arc<dyn MemoryConsumer>);
manager.register_consumer(Arc::clone(&working) as Arc<dyn MemoryConsumer>);
manager.allocated.store(2000, Ordering::Relaxed);
let freed = manager.evict_to_target(1500);
assert!(working.spilled.load(Ordering::Relaxed) > 0);
assert!(freed > 0);
}
#[test]
fn django_detect_system_memory_returns_positive() {
let mem = BufferManagerConfig::detect_system_memory();
assert!(mem > 0);
}
#[test]
fn shosanna_spill_path_config() {
let config = BufferManagerConfig {
budget: 1024,
spill_path: Some(PathBuf::from("/tmp/grafeo-spill")),
..Default::default()
};
assert_eq!(
config.spill_path.as_ref().unwrap().to_str().unwrap(),
"/tmp/grafeo-spill"
);
let manager = BufferManager::new(config);
assert!(manager.config().spill_path.is_some());
}
}