; fault-tolerant execution: failure confined to its closure, cache never poisoned. (define ft-BAD (artdag/op-table-runner {:boom (fn (p i) (artdag/fail "kaboom")) :in (fn (p i) (get p :v)) :add (fn (p i) (+ (nth i 0) (nth i 1))) :inc (fn (p i) (+ 1 (first i)))})) (define ft-GOOD (artdag/op-table-runner {:boom (fn (p i) 99) :in (fn (p i) (get p :v)) :add (fn (p i) (+ (nth i 0) (nth i 1))) :inc (fn (p i) (+ 1 (first i)))})) ; p,q leaves; b=inc(p) (independent); c=boom(q); d=add(b,c) (define ft-D (artdag/build (list (list "p" "in" (list) {:v 10}) (list "q" "in" (list) {:v 20}) (list "b" "inc" (list "p") {}) (list "c" "boom" (list "q") {}) (list "d" "add" (list "b" "c") {} true)))) ; ---- markers ---- (artdag-test "fail constructor is detected" (artdag/failed? (artdag/fail "x")) true) (artdag-test "plain values are not failures" (artdag/failed? 42) false) ; ---- failure confinement ---- (artdag-test "failure count covers node and its dependents" (let ((cache (persist/open))) (artdag/failure-count (artdag/run-safe ft-D ft-BAD cache))) 2) (artdag-test "failed set is exactly c and d" (let ((cache (persist/open))) (artdag/failed-nodes (artdag/run-safe ft-D ft-BAD cache))) (artdag/sort-strings (list (artdag/dag-id ft-D "c") (artdag/dag-id ft-D "d")))) (artdag-test "independent branch still computes" (let ((cache (persist/open))) (artdag/recompute-count (artdag/run-safe ft-D ft-BAD cache))) 3) (artdag-test "independent node result is available" (let ((cache (persist/open))) (artdag/result-of (artdag/run-safe ft-D ft-BAD cache) (artdag/dag-id ft-D "b"))) 11) (artdag-test "all-ok? is false when something failed" (let ((cache (persist/open))) (artdag/all-ok? (artdag/run-safe ft-D ft-BAD cache))) false) (artdag-test "all-ok? is true on a clean run" (let ((cache (persist/open))) (artdag/all-ok? (artdag/run-safe ft-D ft-GOOD cache))) true) ; ---- cache integrity ---- (artdag-test "good node is cached" (let ((cache (persist/open))) (begin (artdag/run-safe ft-D ft-BAD cache) (persist/kv-has? cache (artdag/dag-id ft-D "b")))) true) (artdag-test "failed node is never cached" (let ((cache (persist/open))) (begin (artdag/run-safe ft-D ft-BAD cache) (persist/kv-has? cache (artdag/dag-id ft-D "c")))) false) ; ---- retry after fix ---- (artdag-test "retry recomputes only the failed closure" (let ((cache (persist/open))) (begin (artdag/run-safe ft-D ft-BAD cache) (artdag/recompute-count (artdag/run-safe ft-D ft-GOOD cache)))) 2) (artdag-test "retry reuses the good nodes from cache" (let ((cache (persist/open))) (begin (artdag/run-safe ft-D ft-BAD cache) (artdag/hit-count (artdag/run-safe ft-D ft-GOOD cache)))) 3) (artdag-test "retry produces the correct result" (let ((cache (persist/open))) (begin (artdag/run-safe ft-D ft-BAD cache) (artdag/result-of (artdag/run-safe ft-D ft-GOOD cache) (artdag/dag-id ft-D "d")))) 110) ; ---- transitive cascade ---- (artdag-test "failure cascades through a deep chain" (let ((cache (persist/open))) (artdag/failure-count (artdag/run-safe (artdag/build (list (list "a" "in" (list) {:v 1}) (list "b" "boom" (list "a") {}) (list "c" "inc" (list "b") {}) (list "d" "inc" (list "c") {}))) ft-BAD cache))) 3)