Skip to content

Commit 3bc1477

Browse files
committed
chore: address code review findings
- Fix deadlock in BufferPoolManager by using internal lookups directly. - Safe heap_table cross-iterator destruction. - Restore undo_log in query executor. - Add safety checks to execution benchmarks. - Align benchmark documentation.
1 parent 8e0402d commit 3bc1477

5 files changed

Lines changed: 35 additions & 14 deletions

File tree

benchmarks/sqlite_comparison_bench.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,15 +174,26 @@ static void BM_CloudSQL_Scan(benchmark::State& state) {
174174
"INSERT INTO bench_table VALUES (" + std::to_string(i) + ", 1.1, 'data');"));
175175
}
176176

177+
auto parsed_base = ParseSQL("SELECT * FROM bench_table");
178+
if (!parsed_base || parsed_base->type() != parser::StmtType::Select) {
179+
state.SkipWithError("Failed to parse SELECT statement");
180+
return;
181+
}
177182
auto select_stmt = std::unique_ptr<parser::SelectStatement>(
178-
static_cast<parser::SelectStatement*>(ParseSQL("SELECT * FROM bench_table").release()));
183+
static_cast<parser::SelectStatement*>(parsed_base.release()));
179184

180185
auto root = ctx.executor->build_plan(*select_stmt, nullptr);
186+
if (!root) {
187+
state.SkipWithError("Failed to build execution plan");
188+
return;
189+
}
181190
root->set_memory_resource(&ctx.executor->arena());
182191

183192
for (auto _ : state) {
184-
root->init();
185-
root->open();
193+
if (!root->init() || !root->open()) {
194+
state.SkipWithError("Failed to open plan");
195+
return;
196+
}
186197
cloudsql::executor::Tuple tuple;
187198
while (root->next(tuple)) {
188199
benchmark::DoNotOptimize(tuple);

docs/performance/SQLITE_COMPARISON.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ This report documents the head-to-head performance comparison between the `cloud
2121
## 4. Architectural Analysis
2222

2323
### Point Inserts
24-
The 7.1x gap in insertion speed is attributed to:
25-
1. **Statement Parsing Overhead**: Our benchmark currently re-parses SQL strings for every `INSERT` in `cloudSQL`, whereas SQLite uses a prepared statement (`sqlite3_prepare_v2`).
26-
2. **Object Allocations**: `cloudSQL` allocates multiple `std::unique_ptr` objects (Statements, Expressions, Tuples) per row. SQLite uses a specialized register-based virtual machine with minimal allocations.
27-
3. **Storage Engine Maturity**: SQLite's B-Tree implementation is highly optimized for write-ahead logging and paged I/O compared to our current Heap Table.
24+
Following our latest optimizations, `cloudSQL` completely bridged the insert gap and is now **~58x faster** than SQLite. The dramatic inversion in performance is attributed to:
25+
1. **Prepared Statement Execution**: `cloudSQL` benchmarks now correctly cache and reuse prepared insert statements matching SQLite's `sqlite3_prepare_v2` approach, completely skipping re-parsing overheads per row.
26+
2. **Batch Insert Fast-Path**: By detecting bulk loads into memory, `cloudSQL` entirely bypasses single-row exclusive lock acquisitions (while correctly maintaining undo logs).
27+
3. **In-Memory Architecture**: This configuration allows `cloudSQL` to behave as a massive unhindered memory bump-allocator, whereas SQLite still respects basic transactional boundaries even with `PRAGMA synchronous=OFF`.
2828

2929
### Sequential Scans
30-
The 6.5x gap in scan speed is attributed to:
30+
We reduced the scan gap from 6.5x down to **4.0x** slower than SQLite. The remaining gap is attributed to:
3131
1. **Volcano Model Overhead**: `cloudSQL` uses a tuple-at-a-time iterator model with virtual function calls for `next()`.
32-
2. **Value Type Overhead**: Our `common::Value` class uses `std::variant`, which introduces a small overhead for every column access compared to SQLite's raw buffer indexing.
32+
2. **Value Type Allocations**: Scanning in `cloudSQL` fundamentally builds `std::pmr::vector<common::Value>` using `std::variant` properties for each row, constructing dense memory structures. SQLite's cursor is highly optimized to avoid unnecessary buffer copying unless columns are fetched.
3333

3434
## 5. Post-Optimization Enhancements
3535
We addressed the gaps via the following optimizations:

src/executor/query_executor.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,13 @@ QueryResult QueryExecutor::execute(const PreparedStatement& prepared,
211211
}
212212
}
213213

214-
if (txn != nullptr && !batch_insert_mode_) {
214+
if (txn != nullptr) {
215215
txn->add_undo_log(transaction::UndoLog::Type::INSERT, prepared.table_meta->name,
216216
tid);
217-
if (!lock_manager_.acquire_exclusive(txn, tid)) {
218-
throw std::runtime_error("Failed to acquire exclusive lock");
217+
if (!batch_insert_mode_) {
218+
if (!lock_manager_.acquire_exclusive(txn, tid)) {
219+
throw std::runtime_error("Failed to acquire exclusive lock");
220+
}
219221
}
220222
}
221223
rows_inserted++;

src/storage/buffer_pool_manager.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ bool BufferPoolManager::unpin_page_by_id(uint32_t file_id, uint32_t page_id, boo
146146
bool BufferPoolManager::flush_page(const std::string& file_name, uint32_t page_id) {
147147
const std::scoped_lock<std::mutex> lock(latch_);
148148

149-
const uint32_t file_id = get_file_id(file_name);
149+
const uint32_t file_id = get_file_id_internal(file_name);
150150
const PageKey key{file_id, page_id};
151151

152152
if (page_table_.find(key) == page_table_.end()) {
@@ -204,7 +204,7 @@ Page* BufferPoolManager::new_page(const std::string& file_name, uint32_t* page_i
204204
bool BufferPoolManager::delete_page(const std::string& file_name, uint32_t page_id) {
205205
const std::scoped_lock<std::mutex> lock(latch_);
206206

207-
const uint32_t file_id = get_file_id(file_name);
207+
const uint32_t file_id = get_file_id_internal(file_name);
208208
const PageKey key{file_id, page_id};
209209

210210
if (page_table_.find(key) != page_table_.end()) {

src/storage/heap_table.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ HeapTable::Iterator::Iterator(Iterator&& other) noexcept
8181

8282
HeapTable::Iterator& HeapTable::Iterator::operator=(Iterator&& other) noexcept {
8383
if (this != &other) {
84+
if (&table_ != &other.table_) {
85+
if (other.current_page_) {
86+
other.table_.bpm_.unpin_page_by_id(other.table_.file_id_, other.current_page_num_, false);
87+
other.current_page_ = nullptr;
88+
}
89+
return *this;
90+
}
91+
8492
if (current_page_) {
8593
table_.bpm_.unpin_page_by_id(table_.file_id_, current_page_num_, false);
8694
}

0 commit comments

Comments
 (0)