Skip to content

Add batch mode for JSONL function runs#589

Closed
dnagoda wants to merge 4 commits into
Shopify:mainfrom
dnagoda:dc.batch-mode
Closed

Add batch mode for JSONL function runs#589
dnagoda wants to merge 4 commits into
Shopify:mainfrom
dnagoda:dc.batch-mode

Conversation

@dnagoda

@dnagoda dnagoda commented Jun 30, 2026

Copy link
Copy Markdown

📚 Stack (bottom → top)

  1. Add batch mode for JSONL function runs #589 Add batch mode for processing multiple inputs via JSONL
  2. Preserve JSON input object order #590 Preserve JSON input object order
  3. Refine batch failure handling #591 Refine batch failure handling
  4. Reuse the compiled provider across runs #592 Reuse the compiled provider across runs
  5. Compute humanized bytes lazily #593 Compute humanized bytes lazily
  6. Add minimal batch output with --batch-full-output opt-in #594 Add minimal batch output with --batch-full-output opt-in

Stacked PRs — review bottom-up.


Refs shop/issues-checkout#13420

Why

The function-runner is used by function-rerunner to replay production
function inputs for Checkout Blocks migration parity. Replaying hundreds of
thousands of inputs one process-per-input is dominated by process startup and
Wasm engine/module compilation, which is identical work repeated for every row.

What

Add a --batch mode that reads newline-delimited JSON (JSONL) inputs from a
file (--input) or stdin and runs the function once per line:

  • Creates the Wasmtime Engine and loads the Module once, then reuses
    them across every input row — the expensive setup is amortized.
  • Infers the codec (JSON vs. MessagePack) once from the module's imports.
  • Streams one compact JSON result per line to stdout (JSONL out), so callers
    can parse results incrementally.
  • --batch-continue-on-error keeps processing on per-line read/parse/execute
    failures, emitting a {"success":false,"error":...} record for the failing
    line; without it, batch mode fails fast on the first error.
  • Prints a Batch complete: N processed, S successful, E errors summary to
    stderr so it never contaminates the JSONL on stdout.

Single-run behavior is unchanged — the existing path is factored into
run_single_mode and shares the same engine/module setup.

Testing

  • cargo test
  • cargo build --release

davejcameron and others added 2 commits April 6, 2026 13:27
Function-runner parses JSON inputs into serde_json::Value and reserializes them before passing bytes to the WASM. With serde_json's default map implementation, this sorts object keys lexicographically, which can change JS behavior that depends on Object.keys() ordering for fallback logic.\n\nEnable serde_json's preserve_order feature so JSON input object key order is retained, and add a regression test covering nested metafield-like message objects. This keeps function-rerunner parity closer to production inputs for Checkout Blocks discount functions.\n\nVerified with:\n- cargo test json_input_preserves_object_key_order_in_raw_bytes\n- cargo build --release\n- cargo test\n- Rerunning the 17 discount localized-message mismatch rows with the fixed release runner produced full semantic matches.

Assisted-By: devx/c659e918-9568-4750-b122-e3890447348a
@dnagoda dnagoda changed the title Preserve JSON input object order Add batch mode for JSONL function runs Jun 30, 2026
@dnagoda

dnagoda commented Jun 30, 2026

Copy link
Copy Markdown
Author

@davejcameron I created this draft PR to at least start the conversation about merging these changes into main. Is there any reason not to pursue that?

dnagoda added 2 commits June 30, 2026 15:57
Make batch mode continue by default and add --batch-fail-on-error for callers that want fail-fast behavior. Count actual function successes and failures based on FunctionRunResult.success instead of treating every successful runner invocation as a successful function run.\n\nAdd integration coverage for default continue behavior, fail-fast behavior, and accurate batch summaries. Also update integration tests to use assert_cmd::cargo::cargo_bin! instead of the deprecated Command::cargo_bin helper.\n\nVerified with:\n- cargo test batch_\n- cargo test

Assisted-By: devx/c659e918-9568-4750-b122-e3890447348a
Batch mode is intended to process large JSONL input sets efficiently. Javy/provider functions were still compiling the embedded standard provider module for every input row, so provider setup dominated runtime even though the function module itself was reused.\n\nCompile the standard provider once before the batch loop and pass the compiled provider into each row execution. IOHandler now instantiates the precompiled provider module when it matches the function's standard import, falling back to the old Module::from_binary path otherwise.\n\nAdd batch coverage for a Javy v3 function to exercise the provider path.\n\nMeasured locally on 5,000 js_function_javy_plugin_v3 rows:\n- Before: median 52.31s\n- After: median 0.20s warm run\n\nMeasured on discount-order's 250,003-row parity dataset:\n- Before: 549.85s, 454.68 rows/sec\n- After: 119.87s, 2085.58 rows/sec\n\nVerified with:\n- cargo test batch_\n- cargo test

Assisted-By: devx/c659e918-9568-4750-b122-e3890447348a
@jeffcharles

Copy link
Copy Markdown
Contributor

It makes sense to me to add a mode where we batch a bunch of inputs and write a series of outputs.

The code changes seem a bit messier though. I think we could update the Provider struct to store Module instead of bytes to reduce the number of new abstractions (like CompiledProvider) and methods. Might be worth having the LLM take another pass at generating the code.

You can also open a separate PR to add preserve_order to keep the size of the diff more manageable for reviewing.

@dnagoda

dnagoda commented Jul 2, 2026

Copy link
Copy Markdown
Author

@jeffcharles Yep, code definitely needs to be cleaned up and split into distinct changes. It's a bit of a dumping ground at the moment.

@dnagoda

dnagoda commented Jul 6, 2026

Copy link
Copy Markdown
Author

Superseded by #595 (same branch dc.batch-mode, rebased onto latest main). This PR was auto-closed when the branch was force-updated; continuing in #595 as the base of the stacked batch-mode PRs (#595#590#591#592#593#594).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants