fix(app): hold input until the schema-cache refresh lands (#39)
A simple-mode Form-B insert (`insert into T values (…)`) submitted faster than the post-DDL schema-cache refresh was validated against the stale cache and wrongly rejected as "trying to write SQL?". The cache is refreshed asynchronously after the worker applies a command, posting `SchemaCacheRefreshed` on the same FIFO channel as key events; under faster-than-human input the next Enter is processed before the refresh. Add a submission gate: `dispatch_dsl` arms `awaiting_schema_refresh` on every `ExecuteDsl` dispatch, holds further DSL submissions in a `held_submissions` FIFO while armed, and the `SchemaCacheRefreshed` handler drains them against the fresh cache in submission order. App-lifecycle commands bypass the gate. Arm-on-every-dispatch keeps exactly one DSL command in flight, so the boolean is provably correct. Interactive-only: the replay/batch path already re-snapshots the schema synchronously per line. No interactive-user impact; held input is never lost (a refresh always follows a dispatch). Tests (both verified red→green): a Tier-1 deterministic ordering test and a Tier-4 PTY back-to-back regression (the unpaced inverse of flow 3). Records the design as ADR-0022 Amendment 8.
This commit is contained in:
@@ -377,6 +377,33 @@ fn undo_after_drop_table_restores_it() {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
/// Flow 5 — issue #39 regression: commands submitted **back-to-back**, with
|
||||
/// no wait between them, must still execute correctly. This is the inverse of
|
||||
/// flow 3 (which paces each command on purpose). Pre-fix, a Form-B insert sent
|
||||
/// immediately after `add column` was validated against the *stale* schema
|
||||
/// cache — the worker hadn't yet refreshed it — and wrongly rejected as
|
||||
/// "trying to write SQL?", so the row never landed. The schema-refresh gate
|
||||
/// (`App::awaiting_schema_refresh`) now holds each submission until the prior
|
||||
/// command's refresh arrives, making the outcome independent of input speed.
|
||||
#[test]
|
||||
fn back_to_back_insert_after_ddl_still_succeeds() {
|
||||
let mut app = PtyApp::launch(&[]);
|
||||
app.wait_for_no_tables(); // fresh project
|
||||
|
||||
// Fire all three with no readiness wait in between — the faster-than-human
|
||||
// input that triggered issue #39 (paste / script / unpaced driver).
|
||||
app.submit("create table Customers with pk id(serial)");
|
||||
app.submit("add column to Customers: Name (text)");
|
||||
app.submit("insert into Customers values ('Alice')");
|
||||
|
||||
// The insert's OWN success echo (value + ✓) — proof the row reached the
|
||||
// database, not the "trying to write SQL?" rejection. If the gate
|
||||
// regressed, the insert would misparse against the stale schema and this
|
||||
// would time out.
|
||||
app.wait_for("('Alice') ✓");
|
||||
app.quit();
|
||||
}
|
||||
|
||||
// ===================== NFR perf (measured, generous) ===================
|
||||
//
|
||||
// These run against the DEBUG binary, so the bounds are loose
|
||||
|
||||
Reference in New Issue
Block a user