|
1 | 1 | # What's New in DataJoint 2.3 |
2 | 2 |
|
3 | | -DataJoint 2.3 introduces the **provenance trinity** — `Diagram.trace`, `self.upstream`, and `strict_provenance` — which together turn "a computed row can be traced to the exact upstream rows it came from" from a convention into something the framework helps construct and check. It also ships the **SparkAdapter Codec Protocol** for typed rendering to Spark-native types, **`dj.deploy.set_replica_identity`** for PostgreSQL change-data-capture, and a **cascade fix** for Part-of-Part and renamed-foreign-key chains. |
| 3 | +DataJoint 2.3 adds a first-class **upstream read surface** — `Diagram.trace` and `self.upstream` — which make "a computed row derives only from its declared upstream inputs" easy to follow inside `make()` and easy to query afterward. It also ships the **SparkAdapter Codec Protocol** for typed rendering to Spark-native types, **`dj.deploy.set_replica_identity`** for PostgreSQL change-data-capture, and a **cascade fix** for Part-of-Part and renamed-foreign-key chains. |
4 | 4 |
|
5 | | -> **Upgrading from 2.0, 2.1, or 2.2?** No breaking changes. Everything here is additive, and `strict_provenance` defaults to off — existing pipelines run identically. |
| 5 | +> **Upgrading from 2.0, 2.1, or 2.2?** No breaking changes. Everything here is additive — existing pipelines run identically. |
6 | 6 |
|
7 | 7 | > **Citation:** Yatsenko D, Nguyen TT. *DataJoint 2.0: A Computational Substrate for Agentic Scientific Workflows.* arXiv:2602.16585. 2026. [doi:10.48550/arXiv.2602.16585](https://doi.org/10.48550/arXiv.2602.16585) |
8 | 8 |
|
9 | 9 | ## Overview |
10 | 10 |
|
11 | | -DataJoint's provenance guarantee rests on the convention that `make(self, key)` reads only from declared upstream dependencies and writes only to `self` (and its Parts). The framework has always *defined* this boundary but never *checked* it: a `make()` could `fetch()` from any table, making the dependency invisible to the foreign-key graph and silently breaking the provenance claim downstream. |
12 | | - |
13 | | -The 2.3 trinity closes that loop with three pieces designed as a unit: |
| 11 | +A computed row is reproducible only when `make(self, key)` reads only from its declared upstream dependencies and writes only to `self` (and its Parts). DataJoint 2.3 makes that read/write boundary easy to follow — and the resulting data lineage easy to query — with two features designed as a unit: |
14 | 12 |
|
15 | 13 | 1. **`Diagram.trace()`** constructs the upstream view as a first-class query object. |
16 | 14 | 2. **`self.upstream`** exposes that view ergonomically inside `make()`. |
17 | | -3. **`strict_provenance`** checks (best-effort) that nothing else is read or written. |
18 | 15 |
|
19 | | -## The Provenance Trinity |
| 16 | +## Upstream Trace and `self.upstream` |
20 | 17 |
|
21 | 18 | ### `Diagram.trace()` — the upstream mirror of `cascade` |
22 | 19 |
|
@@ -48,31 +45,13 @@ class Spectrum(dj.Computed): |
48 | 45 | self.insert1({**key, "spectrum": compute_spectrum(samples, rate)}) |
49 | 46 | ``` |
50 | 47 |
|
51 | | -Construction is lazy — the SQL fires only when you access an ancestor and fetch. Outside `make()`, accessing `self.upstream` raises a clear error. Even without `strict_provenance`, it's a pure ergonomic win over `(Recording & key).fetch1(...)`. |
52 | | - |
53 | | -### `strict_provenance` — an opt-in runtime guardrail |
54 | | - |
55 | | -Setting the flag makes the provenance boundary checked at runtime: |
56 | | - |
57 | | -```python |
58 | | -dj.config["strict_provenance"] = True |
59 | | -``` |
60 | | - |
61 | | -When enabled, inside a `make()`: |
62 | | - |
63 | | -- **Reads** of tables outside the declared-ancestor set (plus `self` and its Parts) raise `DataJointError`. |
64 | | -- **Writes** to anything other than `self` and its Parts raise `DataJointError`. |
65 | | -- **Key consistency** is checked: rows inserted into `self`/Parts must carry primary-key values consistent with the current `key`. |
66 | | - |
67 | | -`strict_provenance` is an **operational** flag — a property of how a deployment runs, not of the schema. It's a **best-effort development guardrail**, not an airtight boundary: it observes access through the DataJoint Python client and is designed to surface *accidental* undeclared dependencies (turn it on in staging, fix what it flags). Comprehensive enforcement across every access path is handled on the DataJoint platform, which combines these runtime checks with agentic review of `make()` source in its code-deployment CI/CD. See the [Provenance Specification](../reference/specs/provenance.md) for the exact enforcement model and its documented limits. |
| 48 | +Construction is lazy — the SQL fires only when you access an ancestor and fetch. Outside `make()`, accessing `self.upstream` raises a clear error. It's a pure ergonomic win over `(Recording & key).fetch1(...)`, and it keeps the read inside the declared upstream — which is what makes the result reproducible. See the [make() reproducibility contract](../reference/specs/autopopulate.md#43-the-make-reproducibility-contract) for the full rule set. |
68 | 49 |
|
69 | | -### Adopting the trinity incrementally |
| 50 | +### Adopting `self.upstream` incrementally |
70 | 51 |
|
71 | | -1. Upgrade to 2.3 — the APIs are available; `strict_provenance` stays off. |
| 52 | +1. Upgrade to 2.3 — the new APIs are available; existing code is unaffected. |
72 | 53 | 2. Use `self.upstream` in new `make()` implementations. |
73 | | -3. Migrate existing `make()` reads from `(Upstream & key).fetch(...)` to `self.upstream[Upstream].fetch(...)`. |
74 | | -4. Enable `strict_provenance=True` in staging and fix the undeclared dependencies it surfaces. |
75 | | -5. Enable in production. |
| 54 | +3. Migrate existing `make()` reads from `(Upstream & key).fetch(...)` to `self.upstream[Upstream].fetch(...)` — no semantic change, but the read is now visibly confined to the declared upstream. |
76 | 55 |
|
77 | 56 | ## SparkAdapter Codec Protocol |
78 | 57 |
|
@@ -113,7 +92,7 @@ It is PostgreSQL-only (raising a clear error on other backends), idempotent at t |
113 | 92 |
|
114 | 93 | ## See Also |
115 | 94 |
|
116 | | -- [Provenance Specification](../reference/specs/provenance.md) — `trace`, `self.upstream`, and `strict_provenance` in full |
| 95 | +- [Upstream Trace Specification](../reference/specs/trace.md) — `Diagram.trace` and `self.upstream` in full |
117 | 96 | - [SparkAdapter Codec Protocol](../reference/specs/spark-adapter.md) — typed rendering to Spark-native types |
118 | 97 | - [Deployment Operations](../reference/specs/deploy-operations.md) — the `dj.deploy` module |
119 | 98 | - [Cascade Specification](../reference/specs/cascade.md) — propagation rules shared with `trace` |
|
0 commit comments