The read preparation Ramal.Resource installs on every read action.
Inside Ramal.run/2 it makes a read see the base data patched with the branch's overlay. Outside a branch it
returns the query untouched, so the action behaves as if the extension were absent.
How it works, confirmed against Ash 3.37 (lib/ash/actions/read/read.ex):
Ash.Query.before_action/2hooks run after all preparations and before the data-layer query is built, so a hook there can still removelimitandoffset. The base query keeps its filter, so Postgres still narrows the rows.Ash.Query.after_action/2hooks receive the base rows after the data layer and before relationship loads, runtime calculations and the not-found check ofAsh.get/3. Patching there means loads, calculations andAsh.get/3all see the patched rows.- A relationship load runs a read on the related resource through its own preparation, in a task that does not
inherit the process dictionary. The branch id therefore also travels in the query's
sharedcontext, which Ash copies into related queries (lib/ash/actions/read/relationships.ex). - Filters are evaluated on overlay records with
Ash.Filter.Runtime.filter_matches/4and sorting uses Ash.Actions.Sort.runtime_sort/3.
Every base row the read returns is also fingerprinted (Ramal.Resource.fingerprint/1) for the staleness check at
merge time, unless the query selected only some attributes.
Known limits in 0.1, documented rather than hidden:
- aggregates and calculations the data layer computes do not see the overlay; reading one emits
[:ramal, :read, :unpatched_aggregate].Ramal.aggregate/4is the exact alternative for the common kinds; - overlay records are added without running the action's
authorize_resultschecks, though the query's filter, including any policy filter, is applied to them; - paginated reads (
page:) are paged by the database and patched afterwards, so pages can be short or miss rows created in the branch, andcountis the database's.
Example
# With a branch current, the same call returns rows created in the branch.
Ramal.run!(branch, fn -> Ash.read!(MyApp.Farm.Herd) end)
Summary
Functions
Callback implementation for Ash.Resource.Preparation.init/1.
Callback implementation for Ash.Resource.Preparation.supports/1.
Callback implementation for Ash.Resource.Preparation.temporal_safe?/1.
Functions
Callback implementation for Ash.Resource.Preparation.init/1.
Callback implementation for Ash.Resource.Preparation.supports/1.
Callback implementation for Ash.Resource.Preparation.temporal_safe?/1.