doc update

This commit is contained in:
2026-03-04 01:07:42 -05:00
parent 566b599512
commit 8d0369c672

View File

@ -92,13 +92,21 @@ If a schema originates in the `user` bucket, the validator does *not* rigidly re
To support polymorphic API contracts (e.g., heterogeneous arrays of generic widgets) without manually writing massive `oneOf` blocks, JSPG provides the `$family` macro.
While `$ref` defines rigid structure, `$family` relies on an abstract **Descendants Graph**.
During compilation, `jspg` temporarily tracks every `$ref` pointer globally to build a reverse-lookup graph of "Descendants".
During compilation, `jspg` temporarily tracks every `$ref` pointer globally to build a reverse-lookup graph of "Descendants". It also calculates the **Inheritance Depth** of every schema (how far removed it is from the root entity).
When `{"$family": "widget"}` is encountered, JSPG:
1. Locates the `widget` schema in the Descendants graph.
2. Expands the macro by finding *every* schema in the entire database that structurally `$ref`s `widget`, directly or indirectly (e.g., `stock.widget`, an anonymous object, etc.).
3. Replaces the `$family` keyword with a standard `one_of` array containing `$ref`s to those discovered descendants.
3. Evaluates the incoming JSON against **every** descendant schema in that family *strictly*.
If you request `{"$family": "light.widget"}`, it simply evaluates all schemas that `$ref` the generic abstract `light.widget` interface.
#### E. Strict Matches & The Depth Heuristic
JSPG strictly enforces that polymorphic structures (`oneOf`, or a `$family` expansion) match **exactly one** valid schema permutation. It does not support fuzzy matching (`anyOf`).
If a JSON payload matches more than one schema in a union (which happens frequently due to implicit inheritance where an object might technically satisfy the requirements of both `entity` and `user`), JSPG automatically applies the **Depth Heuristic Tie-Breaker**:
* It looks up the pre-calculated Inheritance Depth for all valid passing candidates.
* It selects the candidate that is **deepest** in the inheritance tree (the most explicitly defined descendant).
* If multiple passing candidates tie at the exact same depth level, an `AMBIGUOUS` error is thrown, forcing the developer to supply a more precise type discriminator or payload.
If you request `{"$family": "light.widget"}`, it simply expands to all schemas that `$ref` the generic abstract `light.widget` interface.
This cleanly separates **Database Physics** (derived from the Postgres `Types` bucket and viral `$ref` inheritance) from **Structural Polymorphism** (derived purely from the abstract `$ref` tree).
### 2. Strict by Default & Extensibility
@ -129,14 +137,19 @@ The extension is written in Rust using `pgrx` and structures its schema parser t
* **Compiler Phase**: schema JSONs are parsed into this struct, linked (references resolved), and then compiled into an efficient validation tree.
* **Validation Phase**: The compiled validators traverse the JSON instance using `serde_json::Value`.
### Concurrency & Threading ("Atomic Swap")
### Concurrency & Threading ("Immutable Graphs")
To support high-throughput validation while allowing for runtime schema updates (e.g., during development or hot-reloading), JSPG uses an **Atomic Swap** pattern.
To support high-throughput validation while allowing for runtime schema updates (e.g., during development or hot-reloading), JSPG uses an **Atomic Swap** pattern based on 100% immutable schemas.
1. **Immutable Validator**: The `Validator` struct immutably owns the `Registry`. Once created, a validator instance (and its registry) never changes.
2. **Global Pointer**: A global `RwLock<Option<Arc<Validator>>>` holds the current active validator.
3. **Lock-Free Reads**: Validation requests acquire a read lock just long enough to clone the `Arc` (incrementing a reference count), then release the lock immediately. Validation proceeds on the snapshot, ensuring no blocking during schema updates.
4. **Atomic Updates**: When schemas are reloaded (`cache_json_schemas`), a new `Registry` and `Validator` are built entirely on the stack. The global pointer is then atomically swapped to the new instance under a write lock.
1. **Parser Phase**: Schema JSONs are parsed into ordered `Schema` structs.
2. **Compiler Phase**: The database iterates all parsed schemas and pre-computes native optimization maps:
* **Descendants Map**: A reverse `$ref` lookup graph for instant `$family` resolution.
* **Depths Map**: The `$ref` lineage distance of every schema for heuristic tie-breaking.
* **Variations Map**: The Native Type inheritance hierarchy.
3. **Immutable Validator**: The `Validator` struct immutably owns the `Database` registry and all its global maps. Once created, a validator instance (and its registry) never changes. Schemas themselves are completely frozen; `$ref` strings are resolved dynamically at runtime using the pre-computed O(1) maps, eliminating the need to physically mutate or link pointers across structures.
4. **Global Pointer**: A global `RwLock<Option<Arc<Validator>>>` holds the current active validator.
5. **Lock-Free Reads**: Validation requests acquire a read lock just long enough to clone the `Arc` (incrementing a reference count), then release the lock immediately. Validation proceeds on the snapshot, ensuring no blocking during schema updates.
6. **Atomic Updates**: When schemas are reloaded (`cache_json_schemas`), a new `Registry` and `Validator` are built entirely on the stack. The global pointer is then atomically swapped to the new instance under a write lock.
## 🧪 Testing