@@ -81,19 +81,24 @@ def merge_lob_records(records):
8181 return merged_after , first_before
8282
8383
84- def compare_values (lm_after , olr_after , table ):
85- """Compare two normalized column dicts. Returns list of diff strings."""
84+ def compare_values (lm_cols , olr_cols , table , section = 'after' ):
85+ """Compare two normalized column dicts. Returns list of diff strings.
86+
87+ section: 'before' or 'after'. LOB unavailable markers are only skipped
88+ in 'before' images — Oracle doesn't provide old LOB values in redo.
89+ In 'after' images, unavailable markers indicate a real problem.
90+ """
8691 diffs = []
87- all_keys = set (lm_after .keys ()) | set (olr_after .keys ())
92+ all_keys = set (lm_cols .keys ()) | set (olr_cols .keys ())
8893 for key in sorted (all_keys ):
8994 if key in ('EVENT_ID' ,):
9095 continue # Event ID verified separately
91- va = lm_after .get (key )
92- vb = olr_after .get (key )
93- if key not in lm_after or key not in olr_after :
96+ va = lm_cols .get (key )
97+ vb = olr_cols .get (key )
98+ if key not in lm_cols or key not in olr_cols :
9499 continue # Supplemental logging differences
95- if is_unavailable (va ) or is_unavailable (vb ):
96- continue # LOB unavailable markers
100+ if section == 'before' and ( is_unavailable (va ) or is_unavailable (vb ) ):
101+ continue # LOB before-image unavailable (Oracle limitation)
97102 if va != vb :
98103 diffs .append (f" { key } : LM={ va !r} OLR={ vb !r} " )
99104 return diffs
@@ -165,7 +170,25 @@ def main():
165170 if time .time () - last_new_events > IDLE_TIMEOUT :
166171 print (f"[validator] Idle timeout ({ IDLE_TIMEOUT } s). "
167172 f"Final validation pass..." , flush = True )
168- break
173+ # Widen frontier to max of both sides per node to catch
174+ # truly missing events (one side never delivered them).
175+ for node_prefix in ('N1' , 'N2' ):
176+ lm_n = conn .execute (
177+ "SELECT MAX(event_id) FROM lm_events WHERE event_id LIKE ?" ,
178+ (f'{ node_prefix } _%' ,)).fetchone ()[0 ]
179+ olr_n = conn .execute (
180+ "SELECT MAX(event_id) FROM olr_events WHERE event_id LIKE ?" ,
181+ (f'{ node_prefix } _%' ,)).fetchone ()[0 ]
182+ if lm_n or olr_n :
183+ node_frontiers [node_prefix ] = max (lm_n or '' , olr_n or '' )
184+ # Re-check if there's anything new with widened frontier
185+ any_new = any (
186+ nf > cursor_by_node .get (np , '' )
187+ for np , nf in node_frontiers .items ()
188+ )
189+ if not any_new :
190+ break
191+ # Fall through to validate the widened range
169192 else :
170193 continue
171194
@@ -252,8 +275,8 @@ def main():
252275 olr_after , olr_before = merge_lob_records (
253276 [dict (r ) for r in olr_recs ])
254277
255- diffs = compare_values (lm_after , olr_after , lm_table )
256- diffs .extend (compare_values (lm_before , olr_before , lm_table ))
278+ diffs = compare_values (lm_after , olr_after , lm_table , 'after' )
279+ diffs .extend (compare_values (lm_before , olr_before , lm_table , 'before' ))
257280 if diffs :
258281 if is_lob :
259282 total_lob_known += 1
0 commit comments