ErrorsReads and writes · rule
data.update_by_id_value
An update by id sets a value that the database can't work out on its own, such as one read earlier in the action.
Area 6, Reads and writes; not declinable; the next step is usually the agent's.
Why Jadpo refuses it
An update by id is one statement, worked out by the database at the
moment it writes. Its values may use only the action's parameters,
constants, clock.now and the record's stored fields, and a stored
field only with + and -.
Here hits comes from counter, a copy loaded earlier. Another request
could add a hit between the read and the write, and this update would
then overwrite it, so a hit would be lost.
What it looks like
main.jadpo:
type Count = Int { min: 0}entity Counter { id: Uuid hits: Count}action hit(counter_id: Counter.id) fails CounterNotFound -> Counter { var counter: Counter = attempt Counter.get(counter_id) return attempt Counter.update(counter_id) { set: { hits: Count(counter.hits + 1) } }}
error Counter.update by id sets a value from counter, a variable
--> main.jadpo:14:25
|14 | hits: Count(counter.hits + 1) | ^^^^^^^ a variable
- reason
- An update by
idis worked out by the database in one step, so its values use only the action's parameters, constants,clock.nowandCounter's stored fields; a value read earlier could have changed by the time it writes. - next
- Load the record and update it,
Counter.update(counter) { set: { ... } }, or pass the value in as a parameter (agent) - rule
- data.
update_ by_ id_ value - help
- https://jadpo.
dev/docs/errors/data. update_ by_ id_ value
(agent) at the end of the next: line means your coding agent can make
the fix itself.
How to fix it
If the new value depends only on the stored one, name the stored
field, Counter.hits, and drop the read:
action hit(counter_id: Counter.id) fails CounterNotFound -> Counter { return attempt Counter.update(counter_id) { set: { hits: Count(Counter.hits + 1) } }}
The database adds 1 to the stored count in one statement, so no hit is lost. The action loads no record, so it runs at read committed, and a second request counting at the same time waits for the first and then adds to its count.
If the action needs the loaded record anyway, update that record
rather than its id:
var counter: Counter = attempt Counter.get(counter_id) return Counter.update(counter) { set: { hits: Count(counter.hits + 1) } }
The action now runs at repeatable read, because it loads the counter. A request that changes the same counter at the same time makes it run again, so the hit still isn't lost.
Related
Markdown for agents: /docs/errors/data.update_by_id_value.md.Built from Jadpo main at aee2e8f.