Summary
The TTL-cascade Lua script is run via EVALSHA inside the triggering action's transactional pipeline (MULTI/EXEC). If Redis has evicted the script (server restart, failover to a replica that never loaded it, or SCRIPT FLUSH), the EVALSHA returns NOSCRIPT at EXEC time — and this is not recoverable.
Why it's unrecoverable
Redis transactions do not roll back on a runtime error. NOSCRIPT is an execution-time error, so at EXEC:
- the action's own writes (e.g.
JSON.SET, INCRBY, list append) commit, while
- the cascade
EVALSHA fails and the cascade silently does not run.
You cannot transparently recover:
- Replaying the whole transaction re-applies the action's writes — double-counts any non-idempotent command (
aincrease, append, apop, …).
- Only the script alone can be safely rerun (a
NOSCRIPT means it never executed), but that necessarily happens outside the committed transaction.
Impact
Rare in normal operation (scripts are loaded at init_rapyer, so NOSCRIPT only happens on eviction/restart/failover), but when it occurs the TTL cascade to referenced targets silently does not happen for that operation, so referenced models can expire earlier than intended. Fails silently, which makes it hard to detect.
Proper fix
Defer the cascade to a standalone, awaited arun_sha after the owning pipeline commits. arun_sha already implements NOSCRIPT recovery (catch → SCRIPT LOAD all scripts → retry), and a standalone rerun is exactly-once because NOSCRIPT means the script never ran. This requires a post-commit deferral mechanism: enqueue the cascade during the action flush, then have the pipeline creator drain the queue after a successful execute() (reusers don't drain).
Note
This deferral infrastructure existed (a DeferredCascade protocol + PendingTTLCascade carrier + a context-scoped queue drained post-commit) but was removed to reduce complexity in the current change; the cascade now runs inline in the action's MULTI, accepting this limitation. This issue tracks restoring correct NOSCRIPT handling by re-introducing post-commit deferral.
Tasks
Summary
The TTL-cascade Lua script is run via
EVALSHAinside the triggering action's transactional pipeline (MULTI/EXEC). If Redis has evicted the script (server restart, failover to a replica that never loaded it, orSCRIPT FLUSH), theEVALSHAreturnsNOSCRIPTatEXECtime — and this is not recoverable.Why it's unrecoverable
Redis transactions do not roll back on a runtime error.
NOSCRIPTis an execution-time error, so atEXEC:JSON.SET,INCRBY, list append) commit, whileEVALSHAfails and the cascade silently does not run.You cannot transparently recover:
aincrease, append,apop, …).NOSCRIPTmeans it never executed), but that necessarily happens outside the committed transaction.Impact
Rare in normal operation (scripts are loaded at
init_rapyer, soNOSCRIPTonly happens on eviction/restart/failover), but when it occurs the TTL cascade to referenced targets silently does not happen for that operation, so referenced models can expire earlier than intended. Fails silently, which makes it hard to detect.Proper fix
Defer the cascade to a standalone, awaited
arun_shaafter the owning pipeline commits.arun_shaalready implementsNOSCRIPTrecovery (catch →SCRIPT LOADall scripts → retry), and a standalone rerun is exactly-once becauseNOSCRIPTmeans the script never ran. This requires a post-commit deferral mechanism: enqueue the cascade during the action flush, then have the pipeline creator drain the queue after a successfulexecute()(reusers don't drain).Note
This deferral infrastructure existed (a
DeferredCascadeprotocol +PendingTTLCascadecarrier + a context-scoped queue drained post-commit) but was removed to reduce complexity in the current change; the cascade now runs inline in the action'sMULTI, accepting this limitation. This issue tracks restoring correctNOSCRIPThandling by re-introducing post-commit deferral.Tasks
execute())arun_sha(with itsNOSCRIPTreload+retry) after commitNOSCRIPTmid-operation no longer drops the cascade (test withSCRIPT FLUSHbetween init and a cascade-triggering action)