Skip to content

[enhance] buffer storage#83

Open
iaojnh wants to merge 13 commits intomainfrom
feat/buffer_storage_vec
Open

[enhance] buffer storage#83
iaojnh wants to merge 13 commits intomainfrom
feat/buffer_storage_vec

Conversation

@iaojnh
Copy link
Collaborator

@iaojnh iaojnh commented Feb 9, 2026

resolve #64

@Cuiyus
Copy link
Collaborator

Cuiyus commented Feb 27, 2026

@greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 27, 2026

Greptile Summary

This PR replaces the existing BufferManager with a new VecBufferPool implementation that uses LRU caching and lock-free concurrent queues for better performance. The changes introduce several critical bugs that need to be addressed:

Key Changes:

  • Adds new buffer pool implementation (VecBufferPool, LPMap, LRUCache) using moodycamel's concurrent queue library
  • Refactors MemoryBlock to use the new buffer pool instead of BufferManager
  • Adds context reset functionality to prevent memory leaks in search operations
  • Updates test files to use the new buffer storage

Critical Issues Found:

  • Race condition in LPMap::acquire_block: Multiple non-atomic operations on ref_count allow blocks to be evicted between checks, causing use-after-free
  • Double-release bug in MemoryBlock move operations: Move constructor/assignment don't invalidate the source, leading to reference count corruption
  • Memory leaks in error paths: Failed pread and aligned_alloc operations don't return buffers to the pool
  • Unused variable: Syntax error with unused ok variable in initialization

These bugs can cause crashes, memory corruption, and resource exhaustion in production.

Confidence Score: 1/5

  • This PR has critical bugs that will cause crashes and memory corruption in production
  • Score reflects multiple critical issues: race conditions leading to use-after-free, double-release bugs in move semantics, and memory leaks. These are not edge cases but fundamental correctness issues in core memory management code that will manifest under concurrent load.
  • Pay critical attention to src/ailego/buffer/buffer_pool.cc and src/include/zvec/core/framework/index_storage.h - both contain bugs that will cause crashes

Important Files Changed

Filename Overview
src/ailego/buffer/buffer_pool.cc New buffer pool implementation with critical race conditions, memory leaks, and resource management issues
src/include/zvec/ailego/buffer/buffer_pool.h New buffer pool header defining LRUCache, LPMap, and VecBufferPool classes for memory-mapped file buffer management
src/core/utility/buffer_storage.cc Migrated from BufferManager to VecBufferPool, added segment_id tracking and buffer pool initialization logic
src/include/zvec/core/framework/index_storage.h Updated MemoryBlock RAII wrapper with critical double-release bugs in move semantics
src/core/interface/index.cc Added context->reset() call after search completes to clean up resources

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class VecBufferPool {
        -int fd_
        -size_t file_size_
        -size_t pool_capacity_
        +LPMap lp_map_
        -mutex mutex_
        -ConcurrentQueue~char*~ free_buffers_
        +init(pool_capacity, block_size)
        +acquire_buffer(block_id, offset, size, retry)
        +get_meta(offset, length, buffer)
        +get_handle()
    }
    
    class VecBufferPoolHandle {
        +VecBufferPool& pool
        +get_block(offset, size, block_id)
        +get_meta(offset, length, buffer)
        +release_one(block_id)
        +acquire_one(block_id)
    }
    
    class LPMap {
        -size_t entry_num_
        -Entry* entries_
        -LRUCache cache_
        +init(entry_num)
        +acquire_block(block_id)
        +release_block(block_id)
        +evict_block(block_id)
        +set_block_acquired(block_id, buffer)
        +recycle(free_buffers)
    }
    
    class LRUCache {
        -int block_size_
        -vector~ConcurrentQueue~ queues_
        -atomic~size_t~ evict_queue_insertions_
        +init(block_size)
        +evict_single_block(item)
        +add_single_block(lp_map, block, block_type)
        +clear_dead_node(lp_map)
    }
    
    class MemoryBlock {
        -MemoryBlockType type_
        -void* data_
        -VecBufferPoolHandle* buffer_pool_handle_
        -int buffer_block_id_
        +data()
        +reset(buffer_pool_handle, block_id, data)
    }
    
    class BufferStorage {
        -VecBufferPool::Pointer buffer_pool_
        -VecBufferPoolHandle::Pointer buffer_pool_handle_
        -map~string,SegmentInfo~ segments_
        -map~string,size_t~ id_hash_
        +open(path, create)
        +get_buffer(offset, length, block_id)
        +get_meta(offset, length, out)
    }
    
    VecBufferPool "1" --o "1" VecBufferPoolHandle : creates
    VecBufferPool "1" *-- "1" LPMap : contains
    LPMap "1" *-- "1" LRUCache : contains
    BufferStorage "1" --> "1" VecBufferPool : uses
    BufferStorage "1" --> "1" VecBufferPoolHandle : uses
    MemoryBlock --> VecBufferPoolHandle : references
    LPMap --> LRUCache : eviction policy
Loading

Last reviewed commit: 95b1c16

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

15 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

for (size_t i = 0; i < buffer_num; i++) {
char *buffer = (char *)aligned_alloc(64, block_size);
if (buffer != nullptr) {
bool ok = free_buffers_.try_enqueue(buffer);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused variable ok

Suggested change
bool ok = free_buffers_.try_enqueue(buffer);
free_buffers_.try_enqueue(buffer);

Comment on lines +157 to +160
char *buffer = (char *)aligned_alloc(64, block_size);
if (buffer != nullptr) {
bool ok = free_buffers_.try_enqueue(buffer);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memory leak when aligned_alloc fails - allocated buffers are not freed on failure, and no error is returned

Comment on lines +75 to +82
if (entry.ref_count.load(std::memory_order_relaxed) == 0) {
entry.load_count.fetch_add(1, std::memory_order_relaxed);
}
entry.ref_count.fetch_add(1, std::memory_order_relaxed);
if (entry.ref_count.load(std::memory_order_relaxed) < 0) {
return nullptr;
}
return entry.buffer;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

race condition: ref_count is checked at line 75, incremented at 78, then checked again at 79 - another thread could evict the block between these operations, making entry.buffer invalid by line 82

Comment on lines +194 to +198
ssize_t read_bytes = pread(fd_, buffer, size, offset);
if (read_bytes != static_cast<ssize_t>(size)) {
LOG_ERROR("Buffer pool failed to read file at offset: %zu", offset);
return nullptr;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memory leak when pread fails - buffer is not returned to free_buffers_, causing the pool to permanently lose this buffer

@greptile-apps
Copy link

greptile-apps bot commented Feb 27, 2026

Additional Comments (2)

src/include/zvec/core/framework/index_storage.h
move constructor doesn't invalidate source - the source MemoryBlock will still call release_one in its destructor, causing double-release. Need to clear rhs.buffer_pool_handle_ after moving


src/include/zvec/core/framework/index_storage.h
move assignment doesn't invalidate source - same double-release issue as move constructor. Need to clear rhs.buffer_pool_handle_ after moving

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhance]: Buffer Storage

5 participants