Commit Graph

572 Commits

Author SHA1 Message Date
Jussi Saurio
85316d8419 Merge 'clear page cache on transaction failure' from Pere Diaz Bou
This is the first step towards rollback, since we still don't spill
pages with WAL, we can simply invalidate page cache in case of failure.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #1599
2025-05-28 23:14:44 +03:00
Pere Diaz Bou
28bd24b7d4 clear page cache on transaction failure
This is the first step towards rollback, since we still don't spill
pages with WAL, we can simply invalidate page cache in case of failure.
2025-05-28 15:54:28 +02:00
Jussi Saurio
dad1e6293b Btree: fix cursor record state not being updated in insert_into_page()
overwrite_cell() requires that the cursor state is pointing to a valid
record, but this was not currently set properly.
2025-05-28 16:54:00 +03:00
Pekka Enberg
59d28eac93 core: Switch Completion "is_completed" to use Cell
Suggested by Jussi
2025-05-27 14:05:07 +03:00
Pekka Enberg
3250560eb8 sqlite3: Add libsql_wal_get_frame() API 2025-05-27 13:47:40 +03:00
Pekka Enberg
05df548b10 core/io: Add wait_for_completion() to I/O dispatcher 2025-05-27 13:47:40 +03:00
Pekka Enberg
eca9a5b703 core/io: Switch to Arc<Completion> 2025-05-27 11:28:49 +03:00
Pekka Enberg
21535018aa core: Don't pass page to begin_read_wal_frame()
Make `begin_read_wal_frame()` a bit more generic by not requiring a page
to be passed.
2025-05-27 10:02:36 +03:00
Jussi Saurio
3ba9f2ab97 Small cleanups to pager/wal/vdbe - mostly naming
- Instead of using a confusing CheckpointStatus for many different things,
  introduce the following statuses:
    * PagerCacheflushStatus - cacheflush can result in either:
      - the WAL being written to disk and fsynced
      - but also a checkpoint to the main BD file, and fsyncing the main DB file

      Reflect this in the type.
    * WalFsyncStatus - previously CheckpointStatus was also used for this, even
      though fsyncing the WAL doesn't checkpoint.
    * CheckpointStatus/CheckpointResult is now used only for actual checkpointing.

- Rename HaltState to CommitState (program.halt_state -> program.commit_state)
- Make WAL a non-optional property in Pager
  * This gets rid of a lot of if let Some(...) boilerplate
  * For ephemeral indexes, provide a DummyWAL implementation that does nothing.
- Rename program.halt() to program.commit_txn()
- Add some documentation comments to structs and functions
2025-05-26 10:37:34 +03:00
Jussi Saurio
b5ac095716 Fix off-by-one error in max_frame after WAL load 2025-05-25 19:34:51 +03:00
Jussi Saurio
385c0d8987 clippy stfu part 2: electric boogaloo 2025-05-25 10:32:23 +03:00
Jussi Saurio
64ef3f1343 simplify condition 2025-05-25 10:22:46 +03:00
Jussi Saurio
20e65c0125 bump max_loops to 100k 2025-05-25 10:21:41 +03:00
Jussi Saurio
fc45e0ec0d Reconstruct WAL frame cache when WAL is opened
Currently we are simply unable to read any WAL frames from disk
once a fresh process w/ Limbo is opened, since we never try to read
anything from disk unless we already have it in our in-memory
frame cache.

This commit implements a crude way of reading entire WAL into memory
as a single buffer and reconstructing the frame cache.
2025-05-24 18:29:44 +03:00
Jussi Saurio
70433e100d Merge 'btree: fix infinite looping in backwards iteration of btree table' from Jussi Saurio
Closes #1562
Existing "fuzz test" (not really fuzz, but kinda) didn't catch this due
to `LIMIT 3` clause

Closes #1563
2025-05-23 21:46:16 +03:00
Jussi Saurio
1a937462b3 Merge 'core/pragma: Add support for update user_version' from Diego Reis
It also changes the type from u32 to i32 since
sqlite supports negative values

Closes #1559
2025-05-23 17:00:55 +03:00
Jussi Saurio
cbb56a182e Fix bug: backwards iteration of table btree hangs 2025-05-23 14:23:18 +03:00
Diego Reis
2f8042da22 core/pragma: Add support for update user_version
It also changes the type from u32 to i32 since
sqlite supports negative values
2025-05-22 20:38:27 -03:00
Zaid Humayun
4312d371fb addresses comment https://github.com/tursodatabase/limbo/pull/1548#discussion_r2102606810 by @jussisaurio
this commit changes the btree_destroy() signature to return an Option<usize>. This more closely resembles Rust semantics instead of passing a pointer to a usize.
However, I'm unsure if I'm handling the cursor result correctly
2025-05-23 00:46:05 +05:30
Zaid Humayun
4072a41c9c Drop Table now uses an ephemeral table as a scratch table
Now when dropping a table, an ephemeral table is created as a scratch table. If a root page of some other table is moved into the page occupied by the root page of the table being dropped, that row is first written into an ephemeral table. Then on a next pass, it is deleted from the schema table and then re-inserted with the new root page.

This happens during AUTOVACUUM when deleting a root page will force the last root page to move into the slot being vacated by the root page of the table being deleted
2025-05-22 19:39:46 +05:30
Pere Diaz Bou
b135bf449f reduce attempts for fuzz_long overflow 2025-05-21 15:40:42 +02:00
Pere Diaz Bou
7143e43dd4 clippy 2025-05-21 15:27:15 +02:00
Pere Diaz Bou
a69f85be84 cacheflush clear cache 2025-05-21 14:20:11 +02:00
Pere Diaz Bou
4704cdd24f validate_btree pin pages 2025-05-21 14:20:11 +02:00
Pere Diaz Bou
ddb166f0f0 custom hashmap for page cache 2025-05-21 14:19:56 +02:00
Pere Diaz Bou
c365d79cb1 minimum capacity 10 in page cache 2025-05-21 14:19:56 +02:00
Pere Diaz Bou
b76961ce35 balance mark dirty from start 2025-05-21 14:19:56 +02:00
Pere Diaz Bou
591c674e86 Introduce PageRef wrapper BTreePage.
One problem we have with PageRef, is that this Page reference can be
unloaded, this means if we read the page again instead of loading the
page onto the same reference, we will have split brain of references.

To solve this we wrap PageRef in `BTreePage` so that if a page is seen
as unloaded, we will replace BTreePage::page with the newest version of
the page.
2025-05-21 14:19:41 +02:00
Pere Diaz Bou
35f7317724 add default page cache 2025-05-21 14:11:21 +02:00
Pere Diaz Bou
15d24bd818 Start transactions in fuzz tests to flush pages
Previously, fuzz tests increase the size of page cache indefinitely,
therefore the was no problem of reaching the capacity of a page cache.
By adding transactions to fuzz tests we allow pages to remove dirty
flags once insert is finished.
2025-05-21 14:11:20 +02:00
Pere Diaz Bou
adf72f2bf8 allow updating a page id in page cache 2025-05-21 14:09:39 +02:00
Pere Diaz Bou
35e2088b7e cacheflush move dirty page to new snapshot
After inserting a page into the wal, we dispose of the modified page.
This is unnecessary as we can simply move new page to the newest
snapshot where this page can be read.
2025-05-21 14:09:39 +02:00
Pere Diaz Bou
9677997c63 fix page cache fuzz
to test whether a key is in the cache, we must use peek without touching
the value in order to not promote and change the order of values in lru
cache
2025-05-21 14:09:39 +02:00
Pere Diaz Bou
04323f95a5 increase cache size in empty_btree 2025-05-21 14:09:39 +02:00
Pere Diaz Bou
67e260ff71 allow delete of dirty page in cacheflush
Dirty pages can be deleted in `cacheflush`. Furthermore, there could be
multiple live references in the stack of a cursor so let's allow them to
exist while deleting.
2025-05-21 14:09:39 +02:00
Alecco
e2f99a1ad2 page_cache: implement resize 2025-05-21 14:09:39 +02:00
Alecco
e808a28c98 WIP (squash) adapt pager and btree to page cache error handling 2025-05-21 14:09:39 +02:00
Alecco
4ef3c1d04d page_cache: fix insert and evict logic
insert() fails if key exists (there shouldn't be two) and panics if
it's different pages, and also fails if it can't make room for the page.

Replaced the limited pop_if_not_dirty() function with make_room_for().
It tries to evict many pages as requested spare capacity. It should come
handy later by resize() and Pager. make_room_for() tries to make room or
fails if it can't evict enough entries.

For make_room_for() I also tried with an all-or-nothing approach, so if
say a query requests a lot more than possible to make room for, it
doesn't evict a bunch of pages from the cache that might be useful. But
implementing this approach got very complicated since it needs to keep
exclusive PageRefs and collecting this caused segfaults. Might be worth
trying again in the future. But beware the rabbit hole.

Updated page cache test logic for new insert rules.

Updated Pager.allocate_page() to handle failure logic but needs further
work. This is to show new cache insert handling. There are many places
to update.

Left comments on callers of pager and page cache needing to update
error handling, for now.
2025-05-21 14:09:39 +02:00
Alecco
bdf427c329 page_cache: proper error handling for deletions
Add error handling and results for insert(), delete(), _delete(),
_detach(), pop_if_not_dirty(), and clear.

Now these functions fail if a page is dirty, locked, or has other
references.

insert() makes room with pop_if_not_dirty() beforehand to handle
cache full and un-evictable, else it would evict this page
silently.

_delete() returns Ok when key is not present in cache and it tries
first to detach the cache entry and clean its page *before*
removing the entry from the map.

detach() checks firstt if it's possible to evict the page and if
there are no other references to the page before taking its
contents.

test_detach_via_delete() and test_detach_via_insert() fixed by
properly checking before and after dropping the page reference.

test_page_cache_fuzz() fixed by reordering and moving reference to
the page into insert.

Other page cache tests fixed to check new function results.

All page cache tests pass.

Error handling and test fixes for Pager and BTree will be added in
a subsequent commit.
2025-05-21 14:09:39 +02:00
Alecco
c8beddab09 page_cache: split unlink() out of detach()
The unlink function removes an entry from the LRU.

The detach function removes an entry in the cache and clears page
contents.
2025-05-21 14:09:39 +02:00
Alecco
6763aa0cd5 page_cache: tests: helper functions and more tests
test_detach_via_insert fails as it repros insert not removing duplicate
page entries with same cache key (id, frame) issue #1348
2025-05-21 14:09:39 +02:00
Alecco
7e898eb8ca page_cache: tests: move helper function up 2025-05-21 14:09:39 +02:00
Jussi Saurio
e4334dcfdf Add enum CursorHasRecord to remove assumption that all btrees have rowid 2025-05-20 14:22:17 +03:00
Jussi Saurio
35350a2368 Add IndexKeyInfo to btree 2025-05-20 14:22:17 +03:00
Pekka Enberg
e102cd0be5 Merge 'Add support for DISTINCT aggregate functions' from Jussi Saurio
Reviewable commit by commit. CI failures are not related.
Adds support for e.g. `select first_name, sum(distinct age),
count(distinct age), avg(distinct age) from users group by 1`
Implementation details:
- Creates an ephemeral index per distinct aggregate, and jumps over the
accumulation step if a duplicate is found

Closes #1507
2025-05-20 13:58:57 +03:00
pedrocarlo
52533cab40 only pass collations for index in cursor + adhere to order of columns in index 2025-05-19 15:22:55 -03:00
pedrocarlo
22b6b88f68 fix rebase type errors 2025-05-19 15:22:55 -03:00
pedrocarlo
4a3119786e refactor BtreeCursor and Sorter to accept Vec of collations 2025-05-19 15:22:55 -03:00
pedrocarlo
f28ce2b757 add collations to btree cursor 2025-05-19 15:22:55 -03:00
pedrocarlo
5bd47d7462 post rebase adjustments to accomodate new instructions that were created before the merge conflicts 2025-05-19 15:22:15 -03:00