fix: select_transactions_records edge case#2285
Conversation
There was a problem hiding this comment.
I'm finding this function quite confusing. The fix works - I think - but given the function layout I'm a bit worried there's something I'm overlooking.
However I think what I want to suggest isn't possible because the table is without rowid.
Maybe @kkovaacs can offer some insight.
What I want to do is roughly:
// Find the first applicable row.
let mut row_start = db.select(
"SELECT rowid FROM transactions WHERE <account+block start filter>"
);
// The last completed full block page index ito tx records.
let mut iblock = 0usize;
let mut records = Vec::new();
let mut last_block = BlockNumber::MAX;
loop {
let chunk = db.select(
"SELECT tx, rowid, block FROM transactions WHERE <filter> AND rowid >= row_start LIMIT 1000 ORDER BY rowid"
);
for row in chunk {
if records.size() + row > capacity {
break;
}
row_start = row.rowid;
if row.block != last_block {
last_block = row.block;
iblock = records.len();
}
records.push(row.tx);
}
}
// Truncate to the last full block.
records = records[..iblock];
Ok(records);| // Set to the block number of the first transaction that did not fit within the payload cap. | ||
| // This is the explicit "we truncated" signal; the accumulated byte total cannot be used as a | ||
| // proxy, since a transaction can fail to fit while `total_size` is still below the cap. | ||
| let mut truncated_at_block: Option<i64> = None; |
There was a problem hiding this comment.
I would suggest an alternate approach, like I outlined in https://github.com/0xMiden/node/pull/2285/changes#r3474272591.
| let complete_transactions: Vec<_> = all_transactions | ||
| .into_iter() | ||
| .take_while(|row| row.block_num < truncation_block) | ||
| .collect(); |
There was a problem hiding this comment.
If you track the last index in addition to the last completed block, then you can instead all_transactions.truncate(ilast_completed).
There was a problem hiding this comment.
I went with partition_point and truncate instead of managing the index. Its safer and cleaner, particularly because the vec is mut. The partition point is O(log n), not that it matters with the lengths we are likely to see here.
Summary
select_transactions_recordspaginates by payload size, but it inferred truncation fromtotal_size >= cap, which missed the common case where a transaction fails to fit while the running total is still below the cap — making a partial page look complete.The fix tracks an explicit "transaction didn't fit" signal so a truncated page reports the last complete block as the cursor, and errors when a single block exceeds the payload cap.
Changelog