Skip to content

Commit dfbc335

Browse files
committed
feat: reduce pack buffer pool memory ~40% (640→384 MiB)
Lower default upload concurrency from 4 to 2 (pool 5→3 buffers) and shrink oversized buffers on pool return to reclaim memory from packs that grew beyond target size. Consolidate the default into a single pub const in config::limits.
1 parent f210d44 commit dfbc335

4 files changed

Lines changed: 32 additions & 14 deletions

File tree

‎crates/vger-core/src/config/limits.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use serde::{Deserialize, Serialize};
22

33
use crate::error::{Result, VgerError};
44

5+
/// Default maximum number of in-flight background pack uploads.
6+
pub const DEFAULT_UPLOAD_CONCURRENCY: usize = 2;
7+
58
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
69
pub struct ResourceLimitsConfig {
710
#[serde(default)]
@@ -26,7 +29,7 @@ pub struct CpuLimitsConfig {
2629
/// Unix process niceness target (-20..19). 0 = unchanged.
2730
#[serde(default)]
2831
pub nice: i32,
29-
/// Max in-flight background pack uploads (default: 4, range: 1-16).
32+
/// Max in-flight background pack uploads (default: 2, range: 1-16).
3033
#[serde(default)]
3134
pub max_upload_concurrency: Option<usize>,
3235
/// Batch size in MiB for transform flushes (default: 32, range: 4-256).
@@ -101,7 +104,8 @@ impl CpuLimitsConfig {
101104

102105
/// Effective upload concurrency limit.
103106
pub fn upload_concurrency(&self) -> usize {
104-
self.max_upload_concurrency.unwrap_or(4)
107+
self.max_upload_concurrency
108+
.unwrap_or(DEFAULT_UPLOAD_CONCURRENCY)
105109
}
106110

107111
/// Effective pipeline depth (0 = disabled).

‎crates/vger-core/src/config/mod.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub(crate) use self::hooks::HOOK_COMMANDS;
1212
pub use self::hooks::{HooksConfig, SourceHooksConfig};
1313
pub use self::limits::{
1414
CpuLimitsConfig, IoLimitsConfig, NetworkLimitsConfig, ResourceLimitsConfig,
15+
DEFAULT_UPLOAD_CONCURRENCY,
1516
};
1617

1718
// Re-export from submodules

‎crates/vger-core/src/repo/mod.rs‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
1515
use tracing::{debug, warn};
1616

1717
use crate::compress;
18-
use crate::config::{ChunkerConfig, RepositoryConfig};
18+
use crate::config::{ChunkerConfig, RepositoryConfig, DEFAULT_UPLOAD_CONCURRENCY};
1919
use crate::crypto::chunk_id::ChunkId;
2020
use crate::crypto::key::{EncryptedKey, MasterKey};
2121
use crate::crypto::pack_id::PackId;
@@ -55,9 +55,6 @@ fn default_max_pack_size() -> u32 {
5555
128 * 1024 * 1024
5656
}
5757

58-
/// Default maximum number of in-flight background pack uploads.
59-
const DEFAULT_IN_FLIGHT_PACK_UPLOADS: usize = 4;
60-
6158
/// Maximum total weight (bytes) of cached blobs in the blob cache.
6259
const BLOB_CACHE_MAX_BYTES: usize = 32 * 1024 * 1024; // 32 MiB
6360

@@ -286,7 +283,7 @@ impl Repository {
286283
index_delta: None,
287284
blob_cache: BlobCache::new(BLOB_CACHE_MAX_BYTES),
288285
pack_buffer_pool: None,
289-
max_in_flight_uploads: DEFAULT_IN_FLIGHT_PACK_UPLOADS,
286+
max_in_flight_uploads: DEFAULT_UPLOAD_CONCURRENCY,
290287
manifest_dirty: false,
291288
index_dirty: false,
292289
file_cache_dirty: false,
@@ -406,7 +403,7 @@ impl Repository {
406403
index_delta: None,
407404
blob_cache: BlobCache::new(BLOB_CACHE_MAX_BYTES),
408405
pack_buffer_pool: None,
409-
max_in_flight_uploads: DEFAULT_IN_FLIGHT_PACK_UPLOADS,
406+
max_in_flight_uploads: DEFAULT_UPLOAD_CONCURRENCY,
410407
manifest_dirty: false,
411408
index_dirty: false,
412409
file_cache_dirty: false,
@@ -554,7 +551,8 @@ impl Repository {
554551
/// the rest for in-flight uploads. Buffers start empty and grow lazily.
555552
pub fn activate_pack_buffer_pool(&mut self) {
556553
let pool_size = self.max_in_flight_uploads + 1;
557-
self.pack_buffer_pool = Some(Arc::new(PackBufferPool::new(pool_size)));
554+
let target = self.data_pack_writer.target_size();
555+
self.pack_buffer_pool = Some(Arc::new(PackBufferPool::new(pool_size, target)));
558556
}
559557

560558
#[cfg(test)]

‎crates/vger-core/src/repo/pack.rs‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ impl PackWriter {
196196
self.pack_type
197197
}
198198

199+
/// The target pack size in bytes.
200+
pub fn target_size(&self) -> usize {
201+
self.target_size
202+
}
203+
199204
/// Append the encrypted header trailer to the already-accumulated `pack_bytes`,
200205
/// compute the PackId, and swap the finalized buffer into `out`.
201206
///
@@ -421,18 +426,24 @@ pub fn compute_tree_pack_target(min_pack_size: u32) -> usize {
421426
pub struct PackBufferPool {
422427
inner: Mutex<VecDeque<Vec<u8>>>,
423428
available: Condvar,
429+
/// Desired buffer capacity. Buffers that grow beyond 125% of this are
430+
/// shrunk on return to reclaim memory from oversized final packs.
431+
target_capacity: usize,
424432
}
425433

426434
impl PackBufferPool {
427435
/// Create a pool with `count` empty buffers.
428-
pub fn new(count: usize) -> Self {
436+
/// `target_capacity` is the expected buffer size; buffers that grow beyond
437+
/// 125% of this value are shrunk on return to reclaim memory.
438+
pub fn new(count: usize, target_capacity: usize) -> Self {
429439
let mut bufs = VecDeque::with_capacity(count);
430440
for _ in 0..count {
431441
bufs.push_back(Vec::new());
432442
}
433443
Self {
434444
inner: Mutex::new(bufs),
435445
available: Condvar::new(),
446+
target_capacity,
436447
}
437448
}
438449

@@ -452,7 +463,11 @@ impl PackBufferPool {
452463
}
453464

454465
/// Return a buffer to the pool. Private — only called by `PooledBuffer::drop`.
455-
fn checkin(&self, buf: Vec<u8>) {
466+
fn checkin(&self, mut buf: Vec<u8>) {
467+
// Reclaim memory from buffers that grew beyond target (e.g. large final blob).
468+
if buf.capacity() > self.target_capacity + self.target_capacity / 4 {
469+
buf.shrink_to(self.target_capacity);
470+
}
456471
let mut guard = self.inner.lock().unwrap();
457472
guard.push_back(buf);
458473
self.available.notify_one();
@@ -556,7 +571,7 @@ mod tests {
556571

557572
#[test]
558573
fn pool_checkout_and_return() {
559-
let pool = Arc::new(PackBufferPool::new(2));
574+
let pool = Arc::new(PackBufferPool::new(2, 128 * 1024));
560575

561576
// Can check out up to pool size
562577
let buf1 = pool.checkout();
@@ -576,7 +591,7 @@ mod tests {
576591

577592
#[test]
578593
fn pool_blocks_when_exhausted() {
579-
let pool = Arc::new(PackBufferPool::new(1));
594+
let pool = Arc::new(PackBufferPool::new(1, 128 * 1024));
580595
let _buf1 = pool.checkout();
581596

582597
// Spawn a thread that tries to checkout — it should block
@@ -598,7 +613,7 @@ mod tests {
598613

599614
#[test]
600615
fn pool_retains_buffer_capacity() {
601-
let pool = Arc::new(PackBufferPool::new(1));
616+
let pool = Arc::new(PackBufferPool::new(1, 128 * 1024));
602617

603618
// Check out, grow the buffer, return it
604619
{

0 commit comments

Comments
 (0)