Commit graph

237 commits

Author SHA1 Message Date
Jussi Saurio
84244cf5f8 sim: fix stale name in TxOperation::DropColumn
When processing AddColumn and DropColumn operations, the code looks up tables in transaction_tables.current_tables using the table name stored in the operation. However, transaction_tables.current_tables has tables with their final names (after all renames), while operations store the name at the time they were recorded.

  In the failing case:
  1. DropColumn was recorded with table_name = "stellar_katerina_672"
  2. RenameTable changed the name to "captivating_eco_1159"
  3. At commit, when processing DropColumn, we looked for "stellar_katerina_672" but the table was now named "captivating_eco_1159"

  The fix: Build a mapping from old names → final names by scanning all RenameTable operations first, then use the final name when looking up in transaction_tables.current_tables.
2025-12-20 09:52:58 +02:00
Jussi Saurio
87f9cb71c7 sim: fix apply_snapshot for create table and create index
have to leave for train soon, so claude summary:

Changes in runner/env.rs:
  1. Renamed RowOperation to TxOperation
  2. Fixed the CreateTable variant to store the full Table instead of separate fields
  3. Added CreateIndex and DropIndex variants
  4. Added corresponding record_* methods to both TransactionTables and ShadowTablesMut
  5. Updated apply_snapshot() to handle CreateTable, CreateIndex, and DropIndex operations in order
  6. Removed the index sync loop at the end of apply_snapshot() since operations are now tracked properly

  Changes in model/mod.rs:
  1. Updated Shadow for Create to call record_create_table() before applying the change
  2. Updated Shadow for CreateIndex to call record_create_index() before applying the change
  3. Updated Shadow for DropIndex to call record_drop_index() before applying the change

  The core fix is that CreateTable and CreateIndex operations are now recorded as they happen, so when apply_snapshot() replays operations in order, subsequent operations like AlterTable find the table already created in committed_tables.
2025-12-20 07:51:24 +02:00
pedrocarlo
1c737cfac5 refactor simulator to use new helpers 2025-12-19 17:20:46 -03:00
Pekka Enberg
edd45ff7b8 Improve MVCC DX by dropping --experimental-mvcc flag
The DX is right now pretty terrible:

```
penberg@vonneumann turso % cargo run -- hello.db
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.15s
     Running `target/debug/tursodb hello.db`
Turso v0.4.0-pre.18
Enter ".help" for usage hints.
Did you know that Turso supports live materialized views? Type .manual materialized-views to learn more.
This software is in BETA, use caution with production data and ensure you have backups.
turso> PRAGMA journal_mode = 'experimental_mvcc';
  × Invalid argument supplied: MVCC is not enabled. Enable it with `--experimental-mvcc` flag in the CLI or by setting the MVCC option in `DatabaseOpts`

turso>
```

To add insult to the injury, many SDKs don't even have a way to enable
MVCC via database options. Therefore, let's remove the flag altogether.
2025-12-19 12:59:42 +02:00
Jussi Saurio
9115b74751
Merge 'fix/sim: all alter table ops must be recorded and applied in order' from Jussi Saurio
## Background
In the simulator, we do our best to replicate the effects of an
interactive transaction into the simulator's shadow state whenever that
transaction commits.
## Problem
this didn't work:
BEGIN;
ALTER TABLE t ADD COLUMN foo;
DELETE FROM t WHERE bar != 5;
COMMIT;
None of the rows where bar != 5 were deleted because apply_snapshot()
was checking that the rows in the committed table were exactly equal to
the rows that were recorded, but since the recorded deletes contained a
NULL `foo` column, they never matched. This meant that the sim thought
it should still have all the rows that were deleted.
## Fix:
like all the other operations, record add column / drop column too so
that they are applied in sequential order in apply_snapshot()
No explicit test for this - I ran into this in another branch of mine
whose seed doesn't reproduce on main (because I changed the simulator in
that branch).

Reviewed-by: Pedro Muniz (@pedrocarlo)

Closes #4264
2025-12-18 09:28:28 +02:00
PThorpe92
a9842d1116
Add simulator run with io_uring io backend to github action 2025-12-17 17:22:41 -05:00
PThorpe92
378de85657
Add io_uring IoBackend to simulator 2025-12-17 16:41:24 -05:00
Jussi Saurio
d0f92856fb be stricter in apply_snapshot() 2025-12-17 22:32:58 +02:00
PThorpe92
db60db7d9b
impl cache spilling in pager 2025-12-17 11:57:34 -05:00
Jussi Saurio
da0ec21580 fix/sim: all alter table ops must be recorded and applied in order
this didn't work:

BEGIN;
ALTER TABLE t ADD COLUMN foo;
DELETE FROM t WHERE bar != 5;
COMMIT;

None of the rows where bar != 5 were deleted because apply_snapshot()
was checking that the rows in the committed table were exactly equal
to the rows that were recorded, but since the recorded deletes contained
a NULL `foo` column, they never matched. This meant that the sim thought
it should still have all the rows that were deleted.

Fix:

like all the other operations, record add column / drop column too so
that they are applied in sequential order in apply_snapshot()

No explicit test for this - I ran into this in another branch of mine
whose seed doesn't reproduce on main (because I changed the simulator
in that branch).
2025-12-17 16:27:36 +02:00
Jussi Saurio
4a57710640
fix/core: decouple autocheckpoint result from transaction durability
After flushing the WAL, the db may attempt an automatic checkpoint. Previously,
a checkpoint failure for any other reason than `Busy` rolled back the transaction;
this is incorrect because the WAL has already been written and synced, so the transaction
is actually committed and durable.

For this reason, do the following decoupling:

- Mark the transaction as committed after `SyncWal`
- Any errors beyond that point are wrapped as `LimboError::CheckpointFailed` which is
  handled specially:
    * Tx rollback is not attempted in `abort()` - only the WAL locks are cleaned up
      and checkpoint state machines are reset
    * In the simulator, the results of the query are shadowed into the sim environment
      so that the sim correctly assumes that the transaction's effects were in fact
      committed.
2025-12-16 10:42:48 -05:00
Jussi Saurio
26367a9580
derive debug for sim IO ops 2025-12-16 10:41:54 -05:00
Jussi Saurio
e9975378a5
sim: don't print stats table for ephemeral files 2025-12-16 10:41:48 -05:00
Jussi Saurio
712eb82382 fix/sim: modify existing rows in ALTER COLUMN properly
Bug 1:

apply_snapshot(), which handles committing transaction changes to the
simulator state, had this bug:

let's say a transaction does:
1. ALTER TABLE t ADD COLUMN (e.g., adding 9th column)
2. INSERT INTO t ... (populating all 9 columns)

then apply_snapshot() would:
1. First push the INSERT row (with 9 values) to committed_tables
2. Then detect new_col_count (9) > old_col_count (8) and add NULLs to ALL rows

hence, adding a 10th column to the new row too.

Fix 1:

only add nulls to existing rows that weren't inserted in the same tx after
the ADD COLUMN.

Bug 2:

the exact same thing, but for DROP COLUMN.

Fix 2:

only remove the i'th column from rows that existed prior to the DROP COLUMN
was issued.
2025-12-15 15:20:26 +02:00
Jussi Saurio
83d433bf8f sim: fix bug in apply_snapshot
When a new table was created within a transaction, we were incorrectly clearing the rows of the new table when committing.

This resulted in false positives where the sim thought there shouldn't be any
rows in the table.

Fix: don't do that.
2025-12-11 10:04:05 +02:00
Jussi Saurio
b10c85f4cb Ensure that 1. insert rows 2. table rename 3. insert more rows is tracked correctly 2025-12-10 08:51:59 +02:00
Jussi Saurio
f8d291402a fix/sim: abandon the entire property if precondition fails
e.g. in "ReadYourUpdatesBack", we first do an UPDATE and then a
SELECT to verify that those updates were actually made.

Problem is the SELECT assertion was made even if the UPDATE failed.

Fix: change interaction handling so that if a precondition in a
property fails, the entire property is abandoned.
2025-12-09 16:06:14 +02:00
Jussi Saurio
7c3bfeed7c sim: fix applying transaction effects
the issue was that the sim was executing queries on
commit, instead of applying their effects. for example,
if transaction T1 did a `DELETE FROM t WHERE TRUE` it would
delete all the rows even if another mvcc transaction T2 had
inserted rows concurrently that should not be visible to
T1.
2025-12-09 13:03:53 +02:00
Jussi Saurio
eea3a8b9f8 sim: handle recoverable errors
WriteWriteConflict should rollback in memory tx but not fail sim.
TxError should not rollback tx but not fail sim either.

Neither of the above should cause sim to shadow the query.
2025-12-09 13:03:49 +02:00
Jussi Saurio
826ca4d44d chore: remove experimental_indexes feature flags 2025-12-08 13:00:37 +02:00
Jussi Saurio
77ad9d87c5 sim: stop ignoring sql execution errors 2025-12-05 13:14:39 +02:00
pedrocarlo
087d5f59a1 fix execution ticks not ticking enough 2025-11-17 11:45:02 -03:00
pedrocarlo
157a5cf10a - add Interaction Builder to simplify code for building an interaction.
Modify `generation/property.rs` to use the Builder
- add additional metadata to `Interaction` to give more context for
  shrinking and iterating over interactions that originated from the
  same interaction.
- add Iterator like utilities for `InteractionPlan` to facilitate
  iterating over interactions that came from the same property:
2025-11-17 11:45:02 -03:00
pedrocarlo
a4f0f2364d disable Watch Mode until we can properly serialize interaction plan 2025-11-17 11:45:02 -03:00
pedrocarlo
4f143f385a - modify bugbase to not save interaction plan in a plan.json. As we
will track `Interaction` instead of `Interactions` in the Plan, this
  change will impossibilitate the serialization of the InteractionPlan with Serde Json. 
- make --load just load the previous cli args
2025-11-17 11:45:02 -03:00
pedrocarlo
8f50154db2 separate struct defining code from struct generation code. Also move Remaining to a metrics file 2025-11-17 11:44:20 -03:00
Pekka Enberg
913b7ac600 core: Disable autovacuum by default
People have discovered various bugs in autovacuum so let's disable it by
default for now.
2025-11-02 12:09:21 +02:00
pedrocarlo
72baf48863 add random generation in simulator IO 2025-10-21 14:10:38 -03:00
Jussi Saurio
b67fabdd62 Fix git directory resolution in simulator to support worktrees
sim cannot be run in a git worktree on main
2025-10-21 14:34:27 +03:00
Bob Peterson
b92f4cb9c4 Make Miri easier to run 2025-10-20 23:48:19 -05:00
Pekka Enberg
9a1bd2112d
Merge 'Run simulator under Miri' from Bob Peterson
This adds support for running the simulator under Miri to detect UB.
There are a few things to note about Miri and its limitations
- It has limited `libc` coverage, so it's not really possible to have
Miri help with `UringIO`/`UringFile` or `UnixIO`/`UnixFile`. That's a
big gap ☹️
- It **can** work for `GenericIO`/`GenericFile`, which only uses `std`
- It can't call external C libraries, so even using `sqlite` is out
(hence adding `--disable-integrity-check` to the simulator for Miri use)
- It runs on nightly, consequently there are a few new lints that don't
exist on turso's pinned version of rustc
Some questions I have about this MR
- I made `GenericFile::{lock_file,unlock_file}` noops so I could use
`GenericIO`. This isn't great, but if/when you update from Rust 1.88.0
to 1.89.0, `std::File::{lock,lock_shared,unlock}` will be stabilized and
available. Should I note that as a TODO or something?
- Previously, the sim runner shelled out to `git` to get stuff like the
current git hash and the repo directory. For Miri, that's out, and so is
`git2`. Unfortunately, `gix` is also out since it has a required
dependency that uses inline assembly, which Miri doesn't like. I wrote a
hacky shim that uses only std to look for `.git` and find the hash that
HEAD is pointing to. It doesn't deal with stuff like packed-refs or the
repo being a secondary one made with `git worktree`. I'm happy to
support that, but wanted to hear from maintainers before doing more
work.
Two UB occurrences I already found:
- `TursoRwLock::read` used `AtomicU64::compare_exchange_weak`, which is
(evidently) [allowed to spuriously fail](https://doc.rust-lang.org/std/s
ync/atomic/struct.AtomicU64.html#method.compare_exchange_weak) in
exchange for perf. Miri forces this behavior, which triggers trivial
read deadlocks even with zero readers/writers. I changed it to
`compare_exchange`, but I'm not an atomics expert.
- Uninitialized read in non-Unix
`core::storage::buffer_pool::arena::alloc`. This is a simple one,
resolved by using `std::alloc::alloc_zeroed` instead of
`std::alloc::alloc`
Moving forward, I'd be interested in potentially getting the tests to
run in Miri, too. `tokio` looks like a good example of a project with
partial coverage that runs it where they can. They have some extra test
config to allow as many as possible to run under Miri, with
appropriately scaled-down parameter values since Miri is super slow

Closes #3720
2025-10-14 09:26:55 +03:00
Bob Peterson
4d843804b7 Add --disable-integrity-check option to simulator
Miri can't execute sqlite via the FFI, so this needs to be configurable
2025-10-13 14:54:16 -05:00
Bob Peterson
ce2f286df0 Replace git shell commands with std shims
gix doesn't work here, since while it's pure Rust, it has a
non-configurable dependency on crates using inline assembly, which Miri
does not support. This commit is a bit of a hack, and only works in
non-bare git repos without e.g packed-refs.
2025-10-13 14:54:16 -05:00
Jussi Saurio
c12c1db275
Merge 'Simulator: persist files in sim memory IO for integrity check' from Pedro Muniz
Some checks are pending
Python / Release (push) Blocked by required conditions
Rust / clippy (push) Waiting to run
Rust / cargo-fmt-check (push) Waiting to run
Rust Benchmarks+Nyrkiö / tpc-h (push) Waiting to run
Build & publish @tursodatabase/database / db-bindings-x86_64-pc-windows-msvc - node@20 (push) Waiting to run
Build & publish @tursodatabase/database / db-bindings-x86_64-unknown-linux-gnu - node@20 (push) Waiting to run
Build & publish @tursodatabase/database / sync-bindings-aarch64-apple-darwin - node@20 (push) Waiting to run
Build & publish @tursodatabase/database / sync-bindings-aarch64-unknown-linux-gnu - node@20 (push) Waiting to run
Build & publish @tursodatabase/database / sync-bindings-wasm32-wasip1-threads - node@20 (push) Waiting to run
Build & publish @tursodatabase/database / sync-bindings-x86_64-pc-windows-msvc - node@20 (push) Waiting to run
Build & publish @tursodatabase/database / sync-bindings-x86_64-unknown-linux-gnu - node@20 (push) Waiting to run
Build & publish @tursodatabase/database / Test DB bindings on Linux-x64-gnu - node@20 (push) Blocked by required conditions
Build & publish @tursodatabase/database / Test DB bindings on browser@20 (push) Blocked by required conditions
Build & publish @tursodatabase/database / Publish (push) Blocked by required conditions
Python / configure-strategy (push) Waiting to run
Python / test (push) Blocked by required conditions
Python / lint (push) Waiting to run
Python / linux (x86_64) (push) Waiting to run
Python / macos-arm64 (aarch64) (push) Waiting to run
Python / sdist (push) Waiting to run
Rust / build-native (blacksmith-4vcpu-ubuntu-2404) (push) Waiting to run
Rust / build-native (macos-latest) (push) Waiting to run
Rust / build-native (windows-latest) (push) Waiting to run
Rust / simulator (push) Waiting to run
Rust / test-limbo (push) Waiting to run
Rust / test-sqlite (push) Waiting to run
Rust Benchmarks+Nyrkiö / bench (push) Waiting to run
Rust Benchmarks+Nyrkiö / clickbench (push) Waiting to run
Rust Benchmarks+Nyrkiö / tpc-h-criterion (push) Waiting to run
Rust Benchmarks+Nyrkiö / vfs-bench-compile (push) Waiting to run
If we don't persist the files, rusqlite will open an empty file and
perform integrity check on it.

Reviewed-by: bit-aloo (@Shourya742)

Closes #3676
2025-10-13 14:23:53 +03:00
pedrocarlo
773fa28063 workaround in sqlite for schema changes become visible to other connections 2025-10-13 02:34:43 -03:00
pedrocarlo
dca1137f81 rusqlite stop trying to get rows when we error with InvalidColumnIndex 2025-10-13 02:34:43 -03:00
pedrocarlo
5f65196115 fix load_bug 2025-10-13 02:34:43 -03:00
pedrocarlo
a18a472685 add option to disable alter column for differential testing 2025-10-13 02:34:43 -03:00
pedrocarlo
fafbdbfa9d persist files in sim memory io for integrity check 2025-10-11 15:03:22 -03:00
pedrocarlo
b6f94b2fa1 remove dead code in sim 2025-10-09 17:25:04 -03:00
pedrocarlo
6d5443d4f0 add Query::Placeholder 2025-10-07 11:38:47 -03:00
pedrocarlo
4fc7be5042 as we have DROP table now, if we want to generate extensional queries eagerly, without affecting how we document interactions with MVCC, we need to travel forward in time and shadow queries eagerly so we can generate queries correctly. This involves cloning the tables unfortunately which is inneficient but correct 2025-10-07 11:38:47 -03:00
bit-aloo
ff381c1036
clippy'ed 2025-10-02 19:30:57 +05:30
bit-aloo
46fab87634
Replaced manual validation in SimulatorCLI::validate with Clap features:
- Added `conflicts_with` for mutually exclusive flags
- Removed redundant default values for bool flags.
- Dropped manual validation checks in favor of Clap's built-in parsing guarantees.
2025-10-02 19:18:38 +05:30
pedrocarlo
d3c2198a75 keep track of each pending query for the transaction and then apply the queries on commit 2025-09-27 23:52:57 -03:00
pedrocarlo
399f35f73c for each connection keep track of the tables at the start of the
transaction as well
2025-09-27 23:52:57 -03:00
pedrocarlo
bbbdd8c1e2 simplify rusqlite execution 2025-09-27 23:52:57 -03:00
pedrocarlo
df420ab815 consolidate SimulatorEnv rollback code 2025-09-27 23:52:57 -03:00
pedrocarlo
026fd9ae9e add ignore_error flag to interaction so that we fail on errors when executing interactions 2025-09-27 23:52:57 -03:00
pedrocarlo
f2d29ffaaf make transactions truly concurrent with mvcc 2025-09-27 23:52:57 -03:00