@@ -170,7 +170,7 @@ where
170170
171171 // Our provisional value from the previous iteration, when doing fixpoint iteration.
172172 // This is different from `opt_old_memo` which might be from a different revision.
173- let mut last_provisional_memo : Option < & Memo < ' db , C > > = None ;
173+ let mut last_provisional_memo_opt : Option < & Memo < ' db , C > > = None ;
174174
175175 // TODO: Can we seed those somehow?
176176 let mut last_stale_tracked_ids: Vec < ( Identity , Id ) > = Vec :: new ( ) ;
@@ -194,7 +194,7 @@ where
194194 // Only use the last provisional memo if it was a cycle head in the last iteration. This is to
195195 // force at least two executions.
196196 if old_memo. cycle_heads ( ) . contains ( & database_key_index) {
197- last_provisional_memo = Some ( old_memo) ;
197+ last_provisional_memo_opt = Some ( old_memo) ;
198198 }
199199
200200 iteration_count = old_memo. revisions . iteration ( ) ;
@@ -219,7 +219,7 @@ where
219219 db,
220220 zalsa,
221221 active_query,
222- last_provisional_memo . or ( opt_old_memo) ,
222+ last_provisional_memo_opt . or ( opt_old_memo) ,
223223 ) ;
224224
225225 // Take the cycle heads to not-fight-rust's-borrow-checker.
@@ -329,10 +329,7 @@ where
329329
330330 // Get the last provisional value for this query so that we can compare it with the new value
331331 // to test if the cycle converged.
332- let last_provisional_value = if let Some ( last_provisional) = last_provisional_memo {
333- // We have a last provisional value from our previous time around the loop.
334- last_provisional. value . as_ref ( )
335- } else {
332+ let last_provisional_memo = last_provisional_memo_opt. unwrap_or_else ( || {
336333 // This is our first time around the loop; a provisional value must have been
337334 // inserted into the memo table when the cycle was hit, so let's pull our
338335 // initial provisional value from there.
@@ -346,8 +343,10 @@ where
346343 } ) ;
347344
348345 debug_assert ! ( memo. may_be_provisional( ) ) ;
349- memo. value . as_ref ( )
350- } ;
346+ memo
347+ } ) ;
348+
349+ let last_provisional_value = last_provisional_memo. value . as_ref ( ) ;
351350
352351 let last_provisional_value = last_provisional_value. expect (
353352 "`fetch_cold_cycle` should have inserted a provisional memo with Cycle::initial" ,
@@ -389,9 +388,24 @@ where
389388 }
390389 }
391390
392- let this_converged = C :: values_equal ( & new_value, last_provisional_value) ;
393391 let mut completed_query = active_query. pop ( ) ;
394392
393+ let value_converged = C :: values_equal ( & new_value, last_provisional_value) ;
394+
395+ // It's important to force a re-execution of the cycle if `changed_at` or `durability` has changed
396+ // to ensure the reduced durability and changed propagates to all queries depending on this head.
397+ let metadata_converged = last_provisional_memo. revisions . durability
398+ == completed_query. revisions . durability
399+ && last_provisional_memo. revisions . changed_at
400+ == completed_query. revisions . changed_at
401+ && last_provisional_memo
402+ . revisions
403+ . origin
404+ . is_derived_untracked ( )
405+ == completed_query. revisions . origin . is_derived_untracked ( ) ;
406+
407+ let this_converged = value_converged && metadata_converged;
408+
395409 if let Some ( outer_cycle) = outer_cycle {
396410 tracing:: info!(
397411 "Detected nested cycle {database_key_index:?}, iterate it as part of the outer cycle {outer_cycle:?}"
@@ -494,7 +508,7 @@ where
494508 memo_ingredient_index,
495509 ) ;
496510
497- last_provisional_memo = Some ( new_memo) ;
511+ last_provisional_memo_opt = Some ( new_memo) ;
498512
499513 last_stale_tracked_ids = completed_query. stale_tracked_structs ;
500514
0 commit comments