@@ -19,7 +19,10 @@ use dexzd_apply::diagnostics::{
1919 TargetResolutionForTest , TimeTravelTargetForTest , resolve_time_travel_target_for_test,
2020} ;
2121use dexzd_apply:: read_model:: { get_history_events, get_history_events_from_source} ;
22- use dexzd_apply:: { ApplyContext , ApplyError , HistoryRecords , HistorySource , StateWrite } ;
22+ use dexzd_apply:: {
23+ ApplyContext , ApplyError , BoundedRowSeek , FlowStateReader , HistoryRecords , HistorySource ,
24+ StateReadError , StateRow , StateWrite ,
25+ } ;
2326use dexzd_keyspace:: { ColumnFamily , RunId , RunTable , run_table_prefix} ;
2427use dexzd_protocol:: dex:: { FlowResetStepMethod , FlowResetType , ResetFlowRequest } ;
2528use dexzd_protocol:: dexzd:: v1:: {
@@ -233,6 +236,21 @@ impl CheckedFlow<'_> {
233236 continue ;
234237 }
235238 let history = MemoryHistorySource :: of_run ( state, & context, run_id, run) ;
239+ // The sourced read must take every History row from `history`, never from the store.
240+ let state_without_history = WithoutHistoryColumn { state } ;
241+ let local_without_history = get_history_events (
242+ & state_without_history,
243+ context. flow_hash ,
244+ run_id,
245+ 0 ,
246+ 0 ,
247+ & [ ] ,
248+ 0 ,
249+ ) ;
250+ assert ! (
251+ local_without_history. is_err( ) ,
252+ "{site}: a local read of run {run_id} passed without History rows"
253+ ) ;
236254 let attempts = [
237255 0 ,
238256 1 ,
@@ -255,7 +273,7 @@ impl CheckedFlow<'_> {
255273 panic ! ( "{site}: local history page failed: {error}" )
256274 } ) ;
257275 let from_source = get_history_events_from_source (
258- state ,
276+ & state_without_history ,
259277 & history,
260278 context. flow_hash ,
261279 run_id,
@@ -515,6 +533,57 @@ fn strip_target_fields(
515533 stripped
516534}
517535
536+ /// A reader of `state` that fails every read of the History column family.
537+ struct WithoutHistoryColumn < ' state > {
538+ state : & ' state MemoryFlowState ,
539+ }
540+
541+ impl WithoutHistoryColumn < ' _ > {
542+ fn reject_history ( column_family : ColumnFamily ) -> Result < ( ) , StateReadError > {
543+ if column_family == ColumnFamily :: History {
544+ return Err ( StateReadError {
545+ column_family,
546+ detail : "the sourced read accessed local History rows" . to_owned ( ) ,
547+ } ) ;
548+ }
549+ Ok ( ( ) )
550+ }
551+ }
552+
553+ impl FlowStateReader for WithoutHistoryColumn < ' _ > {
554+ fn get (
555+ & self ,
556+ column_family : ColumnFamily ,
557+ key : & [ u8 ] ,
558+ ) -> Result < Option < Vec < u8 > > , StateReadError > {
559+ Self :: reject_history ( column_family) ?;
560+ self . state . get ( column_family, key)
561+ }
562+
563+ fn scan (
564+ & self ,
565+ column_family : ColumnFamily ,
566+ start : & [ u8 ] ,
567+ end : & [ u8 ] ,
568+ limit : usize ,
569+ ) -> Result < Vec < StateRow > , StateReadError > {
570+ Self :: reject_history ( column_family) ?;
571+ self . state . scan ( column_family, start, end, limit)
572+ }
573+
574+ fn seek_first_bounded (
575+ & self ,
576+ column_family : ColumnFamily ,
577+ start : & [ u8 ] ,
578+ end : & [ u8 ] ,
579+ max_skipped_deletions : u64 ,
580+ ) -> Result < BoundedRowSeek , StateReadError > {
581+ Self :: reject_history ( column_family) ?;
582+ self . state
583+ . seek_first_bounded ( column_family, start, end, max_skipped_deletions)
584+ }
585+ }
586+
518587/// A history source holding a run's records and checkpoints in memory, as a source built from
519588/// downloaded history would.
520589struct MemoryHistorySource {
0 commit comments