1# Chapter 3: postjevsql/src, the judge
2
3Two files, and one of them is long. `lib.rs` is about two thousand lines,
4but it reads well from top to bottom once you know that it has exactly one
5job: it is the `Judge` the scan (chapter 7) calls. The scan hands it one
6row's calls at a time and gets back one answer per call. Everything between
7those two moments is here.
8
9Follow one call, `jev_prob(t, 'Urgent?')` on one row:
10
11```mermaid
12flowchart TD
13  C["a call, with the row as canonical JSON"] --> K["its cache key: the hash of the exact bytes it would send"]
14  K --> S{"an equal call already asked in this statement?"}
15  S -->|yes| W["wait on that one's answer (dedupe_hits)"]
16  S -->|no| L{"in jev_cache?"}
17  L -->|yes| H["the kept answer, for nothing"]
18  L -->|no| B{"within jev.max_rows and jev.max_cost?"}
19  B -->|no| R["refuse: 54000"]
20  B -->|yes| Q["one request: the row as the state, each distinct question about it once"]
21  Q --> J["jev-client: send, retry, classify"]
22  J --> A["the answer, checked against the pinned model"]
23  A --> St["settle: stored in jev_cache, on the backend"]
24```
25
26The last box is the subtle one. A judgment runs as a future on the
27backend's executor (chapter 6), polled from inside the executor's wait in
28the middle of the scan's own work, and the `Judge` contract says plainly
29that such a future must not call Postgres. So answers pile up as *receipts*, and the scan
30calls `settle` between waits, on the backend proper, which writes them to
31`jev_cache` with ordinary SQL.
32
33> **Aside.** The same trick, run backwards, is how a label-tree search works.
34> It takes several rounds, and each round's Choices should be looked up in
35> the cache before anything is sent. But the cache is SQL, and the search is
36> a future. So the future hands a closure to the backend
37> (`scan::on_backend`), the scan runs it between waits, and the future
38> resumes with the result. A future asking a database for a favour, and
39> waiting politely for it.
40
41## lib.rs, in the order it is written
42
431. **The settings.** `_PG_init` registers every `jev.*` GUC, with who may
44   set it and why, then registers the scan (`scan::register::<JevScan>()`).
45   `PinnedModel` refuses an alias when `jev.model` is set.
462. **The SQL around the functions.** Three `extension_sql!` blocks: the
47   `jev_cache` table, the five result types (`jev_noul_result`,
48   `jev_score_result`, `jev_choice_result`, `jev_tree_result`,
49   `jev_shortlist_result`), and the `REVOKE … FROM PUBLIC` of every function.
503. **The functions.** One `#[pg_extern]` each, all `volatile`,
51   `parallel_restricted` and planned with `jev_support`, all with a body that
52   refuses (chapter 2 says why). Then `jev_support`, the planner support
53   function that prices them, and `jev_stats`.
544. **`JevScan`**, the `Judge`. `begin` reads the settings once per statement
55   (a prepared plan outlives a `SET`) and builds the client; `afford` refuses
56   a statement whose estimate is over budget; `explain` is what EXPLAIN
57   shows; `window` is `jev.concurrency` clamped to the server's stream limit;
58   `judge` answers one row; `settle` stores what was answered.
595. **Sharing.** `Asking` and `Shared` are the statement's registry of
60   judgments in flight, so an equal call waits instead of sending. `Owed`
61   unregisters a key whose request was dropped, so its waiters ask again
62   themselves. `Budget` and `Committed` hold each request's worst case
63   against the spend guards until its real `usage` replaces it.
646. **The searches.** `TreeCall` drives `postjevsql-core`'s label-tree search
65   for `jev_choice_tree`, and `ShortlistCall` its chunk-and-shortlist search
66   for `jev_choice_shortlist`, a round at a time, each round looked up through
67   `on_backend`. `tree_out` turns a search's outcome into the SQL result.
687. **Asking.** `Lookup` finds calls in the cache; `Judged` is a judgment's
69   answer and how each function reads it; `Sender` sends the misses within
70   the budget, serves their waiters and queues their receipts.
718. **Odds and ends.** `api_key` (exactly one source, never both),
72   `learned_ratio` (characters per token, learned from `jev_cache` for the
73   pinned model), and `setting`.
74
75> **Try it.** `cargo test -p postjevsql-core` exercises the planner, key and
76> searches this file drives. The judge itself needs a running Postgres, so its
77> tests are the integration tests: `nix develop -c buck2 test //tests:cache`
78> runs the cache's.
79
80## For the people who maintain it
81
82| Path | What |
83| --- | --- |
84| [lib.rs](lib.rs) | Settings, the SQL objects, the functions, and `JevScan`: begin, afford, explain, judge, settle; sharing, budgets, the searches, the sender. |
85| [failure.rs](failure.rs) | `Failure`: every way a call fails, the one place each becomes an ERROR with its SQLSTATE and DETAIL, and `is_failed_judgment`, which decides what `jev.on_error = unsure` may turn into NULL. |
86| [bin/](bin/) | The schema binary cargo-pgrx runs. Chapter 4. |
87
88← Previous: [Chapter 2, postjevsql/](../) · Up: [postjevsql](../) · Next: [Chapter 4, src/bin/](bin/) →