| Crates.io | jdb_buf |
| lib.rs | jdb_buf |
| version | 0.1.4 |
| created_at | 2025-12-23 17:06:14.404154+00 |
| updated_at | 2025-12-23 17:34:01.573952+00 |
| description | Thread-local buffer pool with zero-copy cross-thread recycling / 线程本地缓冲池,支持零拷贝跨线程回收 |
| homepage | https://github.com/js0/jdb/tree/main/jdb_buf |
| repository | https://github.com/js0/jdb.git |
| max_upload_size | |
| id | 2001937 |
| size | 64,772 |
jdb_buf is a high-performance buffer pool designed for database storage engines. It provides thread-local memory allocation with automatic cross-thread recycling, eliminating syscall overhead for Direct I/O operations.
The pool bridges the gap between low-level aligned memory allocation (jdb_alloc) and upper-layer storage logic, serving as the "circulatory system" of the JDB database kernel.
Page implements IoBuf, IoBufMut, SetBufInit traitscrossfire high-performance MPSC channelcargo add jdb_buf
use jdb_buf::LocalPool;
fn main() -> Result<(), jdb_buf::Error> {
// Create pool with 64-slot recycle channel
let mut pool = LocalPool::with_cap(64);
// Allocate 4KB aligned page
let page = pool.alloc()?;
assert_eq!(page.cap(), 4096);
// Page auto-recycles on drop
drop(page);
// Move recycled buffers to local stack
pool.maintain();
assert_eq!(pool.cached(), 1);
Ok(())
}
use jdb_buf::LocalPool;
use std::thread;
fn main() -> Result<(), jdb_buf::Error> {
let mut pool = LocalPool::with_cap(64);
let page = pool.alloc()?;
// Move page to another thread
let handle = thread::spawn(move || {
// Page dropped here, sent back via channel
drop(page);
});
handle.join().unwrap();
// Receive recycled buffer
pool.maintain();
assert_eq!(pool.cached(), 1);
Ok(())
}
use jdb_buf::LocalPool;
fn main() -> Result<(), jdb_buf::Error> {
// 8KB pages with 32-slot channel
let mut pool = LocalPool::new(8192, 32);
let page = pool.alloc()?;
assert_eq!(page.cap(), 8192);
Ok(())
}
use jdb_buf::LocalPool;
use compio_buf::{IoBuf, IoBufMut};
fn main() -> Result<(), jdb_buf::Error> {
let mut pool = LocalPool::with_cap(8);
let mut page = pool.alloc()?;
// Use as compio buffer
let ptr = page.as_buf_mut_ptr();
unsafe { *ptr = 0x42 };
assert_eq!(page.buf_capacity(), 4096);
Ok(())
}
LocalPoolThread-local buffer pool.
| Method | Description |
|---|---|
new(page_size, cap) |
Create pool with custom page size and channel capacity |
with_cap(cap) |
Create pool with default 4KB page size |
alloc() |
Allocate page (returns Result<Page>) |
maintain() |
Drain recycle channel to local stack |
cached() |
Get cached buffer count |
PageSmart buffer with auto-recycle on drop. Implements Deref<Target=RawIoBuf>.
| Trait | Description |
|---|---|
IoBuf |
Read buffer for compio async I/O |
IoBufMut |
Write buffer for compio async I/O |
SetBufInit |
Set initialized length after I/O completion |
Deref / DerefMut |
Access underlying RawIoBuf methods |
ErrorError type for buffer operations.
pub enum Error {
Alloc(jdb_alloc::Error),
}
Result<T>Alias for std::result::Result<T, Error>.
graph TD
A[LocalPool::alloc] --> B{Local Stack?}
B -->|Yes| C[Pop from Stack]
B -->|No| D{Recycle Channel?}
D -->|Yes| E[try_recv]
D -->|No| F[AlignedBuf::page]
C --> G[Page]
E --> G
F --> H[into_raw_parts]
H --> I[RawIoBuf::from_raw_parts]
I --> G
G --> J[User Code]
J --> K[Page::drop]
K --> L{try_send OK?}
L -->|Yes| M[Recycle Channel]
L -->|No| N[free_raw]
M --> O[LocalPool::maintain]
O --> P[Local Stack]
Allocation Flow:
jdb_allocRecycling Flow:
Page::drop attempts try_send to channelAlignedBuf::from_raw_partsmaintain() drains channel to local stackModule Interaction:
graph LR
A[jdb_alloc] -->|AlignedBuf| B[jdb_buf]
B -->|RawIoBuf| C[compio-buf]
B -->|MPSC| D[crossfire]
subgraph jdb_buf
B --> E[LocalPool]
E --> F[Page]
F --> G[IoBuf/IoBufMut]
end
The Page struct acts as a smart pointer wrapper around RawIoBuf from jdb_alloc, providing automatic lifecycle management and seamless integration with async I/O frameworks through trait implementations.
| Component | Purpose |
|---|---|
| jdb_alloc | 4KB-aligned memory allocation for Direct I/O |
| crossfire | Lock-free MPSC channel with blocking sender |
| compio-buf | Async I/O buffer traits |
jdb_buf/
├── src/
│ ├── lib.rs # LocalPool, Page implementation
│ └── error.rs # Error types
├── tests/
│ └── main.rs # Integration tests
└── Cargo.toml
The concept of buffer pools dates back to the 1970s when IBM's System R introduced the idea of managing database pages in memory. The term "buffer pool" became standard in database literature after the influential 1981 paper "The Design of POSTGRES" by Michael Stonebraker.
Modern buffer pools face a unique challenge: io_uring and Direct I/O require page-aligned memory, but frequent mmap/munmap syscalls are expensive. Thread-local pools with cross-thread recycling solve this by amortizing allocation costs across many I/O operations.
The "shared-nothing" architecture used here, where each thread owns its pool but accepts returns from other threads, mirrors the design of ScyllaDB and Redpanda—databases that achieve millions of IOPS by eliminating lock contention.
This approach draws inspiration from the buffer management techniques pioneered in the 1990s by the PostgreSQL project, which introduced the concept of buffer pinning and local buffer caches to reduce contention in multi-core systems. The zero-copy recycling mechanism is reminiscent of the slab allocators used in the Linux kernel for efficient memory management.
This project is an open-source component of js0.site ⋅ Refactoring the Internet Plan.
We are redefining the development paradigm of the Internet in a componentized way. Welcome to follow us:
jdb_buf 是为数据库存储引擎设计的高性能缓冲池。提供线程本地内存分配与自动跨线程回收,消除 Direct I/O 操作的 syscall 开销。
该模块连接底层对齐内存分配器 (jdb_alloc) 与上层存储逻辑,是 JDB 数据库内核的"血液循环系统"。
Page 实现 IoBuf、IoBufMut、SetBufInit traitcrossfire 高性能 MPSC 通道cargo add jdb_buf
use jdb_buf::LocalPool;
fn main() -> Result<(), jdb_buf::Error> {
// 创建池,回收通道容量 64
let mut pool = LocalPool::with_cap(64);
// 分配 4KB 对齐页面
let page = pool.alloc()?;
assert_eq!(page.cap(), 4096);
// Page drop 时自动回收
drop(page);
// 将回收的缓冲区搬运到本地栈
pool.maintain();
assert_eq!(pool.cached(), 1);
Ok(())
}
use jdb_buf::LocalPool;
use std::thread;
fn main() -> Result<(), jdb_buf::Error> {
let mut pool = LocalPool::with_cap(64);
let page = pool.alloc()?;
// 将 page 移动到另一线程
let handle = thread::spawn(move || {
// Page 在此处 drop,通过通道发回
drop(page);
});
handle.join().unwrap();
// 接收回收的缓冲区
pool.maintain();
assert_eq!(pool.cached(), 1);
Ok(())
}
use jdb_buf::LocalPool;
fn main() -> Result<(), jdb_buf::Error> {
// 8KB 页面,通道容量 32
let mut pool = LocalPool::new(8192, 32);
let page = pool.alloc()?;
assert_eq!(page.cap(), 8192);
Ok(())
}
use jdb_buf::LocalPool;
use compio_buf::{IoBuf, IoBufMut};
fn main() -> Result<(), jdb_buf::Error> {
let mut pool = LocalPool::with_cap(8);
let mut page = pool.alloc()?;
// 作为 compio 缓冲区使用
let ptr = page.as_buf_mut_ptr();
unsafe { *ptr = 0x42 };
assert_eq!(page.buf_capacity(), 4096);
Ok(())
}
LocalPool线程本地缓冲池。
| 方法 | 说明 |
|---|---|
new(page_size, cap) |
创建池,指定页大小和通道容量 |
with_cap(cap) |
创建池,默认 4KB 页大小 |
alloc() |
分配页面(返回 Result<Page>) |
maintain() |
将回收通道数据搬运到本地栈 |
cached() |
获取缓存的缓冲区数量 |
Page智能缓冲区,drop 时自动回收。实现 Deref<Target=RawIoBuf>。
| Trait | 说明 |
|---|---|
IoBuf |
compio 异步 I/O 读缓冲区 |
IoBufMut |
compio 异步 I/O 写缓冲区 |
SetBufInit |
I/O 完成后设置已初始化长度 |
Deref / DerefMut |
访问底层 RawIoBuf 方法 |
Error缓冲区操作错误类型。
pub enum Error {
Alloc(jdb_alloc::Error),
}
Result<T>std::result::Result<T, Error> 的类型别名。
graph TD
A[LocalPool::alloc] --> B{本地栈?}
B -->|是| C[从栈弹出]
B -->|否| D{回收通道?}
D -->|是| E[try_recv]
D -->|否| F[AlignedBuf::page]
C --> G[Page]
E --> G
F --> H[into_raw_parts]
H --> I[RawIoBuf::from_raw_parts]
I --> G
G --> J[用户代码]
J --> K[Page::drop]
K --> L{try_send 成功?}
L -->|是| M[回收通道]
L -->|否| N[free_raw]
M --> O[LocalPool::maintain]
O --> P[本地栈]
分配流程:
jdb_alloc 从 OS 分配回收流程:
Page::drop 尝试 try_send 到通道AlignedBuf::from_raw_parts 释放内存maintain() 将通道数据搬运到本地栈模块交互:
graph LR
A[jdb_alloc] -->|AlignedBuf| B[jdb_buf]
B -->|RawIoBuf| C[compio-buf]
B -->|MPSC| D[crossfire]
subgraph jdb_buf
B --> E[LocalPool]
E --> F[Page]
F --> G[IoBuf/IoBufMut]
end
Page 结构体作为 RawIoBuf(来自 jdb_alloc)的智能指针包装器,提供自动生命周期管理,并通过 trait 实现与异步 I/O 框架的无缝集成。
| 组件 | 用途 |
|---|---|
| jdb_alloc | 4KB 对齐内存分配,用于 Direct I/O |
| crossfire | 无锁 MPSC 通道,阻塞发送端 |
| compio-buf | 异步 I/O 缓冲区 trait |
jdb_buf/
├── src/
│ ├── lib.rs # LocalPool、Page 实现
│ └── error.rs # 错误类型
├── tests/
│ └── main.rs # 集成测试
└── Cargo.toml
缓冲池的概念可追溯到 1970 年代,IBM System R 首次引入在内存中管理数据库页面的思想。1981 年 Michael Stonebraker 的论文《The Design of POSTGRES》使"缓冲池"成为数据库领域的标准术语。
现代缓冲池面临独特挑战:io_uring 和 Direct I/O 要求页对齐内存,但频繁的 mmap/munmap syscall 开销巨大。线程本地池配合跨线程回收,通过在多次 I/O 操作间分摊分配成本来解决此问题。
此处采用的"Shared-Nothing"架构——每个线程拥有自己的池但接受其他线程的返还——与 ScyllaDB 和 Redpanda 的设计如出一辙。这些数据库通过消除锁竞争实现了百万级 IOPS。
这种方法借鉴了 1990 年代 PostgreSQL 项目开创的缓冲区管理技术,该项目引入了缓冲区固定和本地缓冲区缓存的概念,以减少多核系统中的竞争。零拷贝回收机制让人联想到 Linux 内核中用于高效内存管理的 slab 分配器。
本项目为 js0.site ⋅ 重构互联网计划 的开源组件。
我们正在以组件化的方式重新定义互联网的开发范式,欢迎关注: