4. Live blocks
The self-healing construct: retries, invariants, telemetry.
What is a live block?
A live block is the core Resilient construct for
self-healing code. Its body runs; if anything inside
panics — a failed assert, a contract violation, a
division by zero — the runtime doesn’t just bubble the error
up. Instead it:
- Logs the failure.
- Restores the caller’s local state to what it was before the block entered.
- Retries the body, up to a bounded retry count.
If the bound is exhausted, the error finally propagates. If the body succeeds on any retry, the program continues as if the earlier failures had never happened.
A concrete example
This program “fails” twice before succeeding on the third
attempt. static let counter survives the state restore
(which only resets LOCAL bindings inside the live block), so
each retry increments the counter and eventually clears the
failing condition:
static let counter = 0;
fn flaky() -> int {
counter = counter + 1;
if counter < 3 {
assert(false);
}
return counter;
}
fn main() {
live {
let n = flaky();
println("succeeded on attempt " + n);
}
}
main();
Run it:
[LIVE BLOCK] Starting execution of live block
[LIVE BLOCK] Error detected (attempt 1/3): ASSERTION ERROR: …
[LIVE BLOCK] Retrying execution (attempt 2/3)
[LIVE BLOCK] Error detected (attempt 2/3): ASSERTION ERROR: …
[LIVE BLOCK] Retrying execution (attempt 3/3)
succeeded on attempt 3
[LIVE BLOCK] Successfully executed live block
Program executed successfully
The retry budget is 3 by default. Exceeding it propagates the
final error — live is a recovery mechanism, not an infinite
loop.
Invariants
Sometimes “no panic” isn’t enough — you want to guarantee a
property holds after the block. invariant <expr> on the
same line as live makes the runtime check that expression
after every iteration:
fn main() {
let total = 0;
live invariant total >= 0 {
total = total + 1;
}
println(total);
}
main();
The invariant total >= 0 holds trivially, so the block
succeeds on the first attempt. If an iteration ever left
total negative, the runtime would treat it as a failure
and restart the block.
Telemetry
live_total_retries() returns the total retry count
accumulated across every live block the process has run.
Useful for health dashboards — a spike signals something
flaky in production.
static let counter = 0;
fn flaky() -> int {
counter = counter + 1;
if counter < 3 {
assert(false);
}
return counter;
}
fn main() {
live {
let n = flaky();
println("done on " + n);
}
println("retries: " + live_total_retries());
}
main();
Prints retries: 2 — the number of failed attempts before
the block finally succeeded. live_total_exhaustions() is the
complementary counter for blocks that hit the retry cap.
What you learned
live { … }retries the body on any panic, up to a bounded retry count, with state restored between retries.live invariant <expr> { … }additionally asserts a property after each iteration; failing the invariant is treated like a panic.live_total_retries()+live_total_exhaustions()give you process-wide health signals.