Steps to reproduce
- Create a fact with a property and run
Fbe.conclude with on '(exists property)' and a consider block.
- In the block, mutate the yielded fact and then abort the transaction with
throw(:rollback) (or raise an error).
- Read the fact after the block returns or raises.
Actual result
The mutation can remain in the factbase even though its transaction rolled back. roll materializes the matching facts with @fb.query(@query).each.to_a before opening each transaction. The consider callback then receives that pre-transaction fact handle (yield(a)), while the transaction's fbt handle is ignored. A write through the yielded fact therefore changes the original map directly instead of the transaction's copy-on-write map.
Expected result
A consider block's changes to its current fact should go through the transaction and be discarded when that transaction rolls back.
Technical evidence
In lib/fbe/conclude.rb, roll queries into a before @fb.txn, then calls the block inside @fb.txn. consider is implemented as roll { |_fbt, a| yield(a); nil }, so it passes the old handle rather than a transaction-backed one. Factbase transactions use copy-on-write, so writes through that handle bypass rollback.
Steps to reproduce
Fbe.concludewithon '(exists property)'and aconsiderblock.throw(:rollback)(or raise an error).Actual result
The mutation can remain in the factbase even though its transaction rolled back.
rollmaterializes the matching facts with@fb.query(@query).each.to_abefore opening each transaction. Theconsidercallback then receives that pre-transaction fact handle (yield(a)), while the transaction'sfbthandle is ignored. A write through the yielded fact therefore changes the original map directly instead of the transaction's copy-on-write map.Expected result
A
considerblock's changes to its current fact should go through the transaction and be discarded when that transaction rolls back.Technical evidence
In
lib/fbe/conclude.rb,rollqueries intoabefore@fb.txn, then calls the block inside@fb.txn.consideris implemented asroll { |_fbt, a| yield(a); nil }, so it passes the old handle rather than a transaction-backed one. Factbase transactions use copy-on-write, so writes through that handle bypass rollback.