@@ -100,11 +100,9 @@ impl SketchStoreSink {
100100 }
101101 }
102102
103- /// Best-effort write to `SketchStore` for one PrecomputedOutput.
104- /// Logs and skips on missing agg_config or other transient
105- /// inconsistencies — a SketchStore miss is recoverable in
106- /// practice because the control plane will re-emit the agg_config
107- /// on its next reconcile pass.
103+ /// Write one PrecomputedOutput; false reports a rejected output to the caller.
104+ /// Missing configuration or incompatible state must surface as failure:
105+ /// a finite-input completion barrier cannot acknowledge dropped outputs.
108106 ///
109107 /// PR-6 follow-up: resolves the source `AggregationConfig` via
110108 /// `PolicyRegistry::get(output.policy_fp)`. The legacy
@@ -160,8 +158,18 @@ impl OutputSink for SketchStoreSink {
160158 return Ok ( ( ) ) ;
161159 }
162160 let _span = debug_span ! ( "sketch_index_insert" , batch_size = outputs. len( ) ) . entered ( ) ;
161+ let mut failed = 0 ;
163162 for ( output, accumulator) in & outputs {
164- self . append_to_index ( output, accumulator. as_ref ( ) ) ;
163+ if !self . append_to_index ( output, accumulator. as_ref ( ) ) {
164+ failed += 1 ;
165+ }
166+ }
167+ if failed > 0 {
168+ return Err ( format ! (
169+ "SketchStore rejected {failed} of {} completed outputs" ,
170+ outputs. len( )
171+ )
172+ . into ( ) ) ;
165173 }
166174 Ok ( ( ) )
167175 }
@@ -375,9 +383,9 @@ mod tests {
375383 }
376384
377385 #[ test]
378- fn sketch_index_sink_skips_unknown_agg_id_gracefully ( ) {
386+ fn sketch_index_sink_reports_unknown_policy_as_failure ( ) {
379387 // Streaming config does NOT contain agg_id=99 — the sink
380- // skips it (warn log) rather than panicking .
388+ // reports a recoverable error rather than acknowledging a lost write .
381389 let streaming = StreamingConfig :: new ( HashMap :: new ( ) ) ;
382390 let hot_reload = HotReloadStreamingConfig :: new ( streaming. clone ( ) ) ;
383391 let sketch_index = Arc :: new ( SketchStore :: new ( ) ) ;
@@ -389,12 +397,13 @@ mod tests {
389397
390398 let output = PrecomputedOutput :: new ( 1000 , 2000 , None , asap_types:: PolicyFingerprint ( 99 ) ) ;
391399 let acc: Box < dyn AggregateCore > = Box :: new ( SumAccumulator :: with_sum ( 1.0 ) ) ;
392- sink. emit_batch ( vec ! [ ( output, acc) ] ) . expect ( "emit ok" ) ;
400+ sink. emit_batch ( vec ! [ ( output, acc) ] )
401+ . expect_err ( "unpersisted output must not be acknowledged" ) ;
393402 assert_eq ! ( sketch_index. instance_count( ) , 0 ) ;
394403 }
395404
396405 /// CQ-6 — a registry-miss (policy_fp not in the running streaming
397- /// config) is a silent write-skip ; with an `IngestObservability`
406+ /// config) reports a failed write; with an `IngestObservability`
398407 /// handle wired in, the `dropped_policy_miss` counter must tick.
399408 #[ test]
400409 fn sink_increments_policy_miss_counter_on_registry_miss ( ) {
@@ -412,7 +421,8 @@ mod tests {
412421 // policy_fp=42 is absent from the empty registry → registry miss.
413422 let output = PrecomputedOutput :: new ( 1000 , 2000 , None , asap_types:: PolicyFingerprint ( 42 ) ) ;
414423 let acc: Box < dyn AggregateCore > = Box :: new ( SumAccumulator :: with_sum ( 1.0 ) ) ;
415- sink. emit_batch ( vec ! [ ( output, acc) ] ) . expect ( "emit ok" ) ;
424+ sink. emit_batch ( vec ! [ ( output, acc) ] )
425+ . expect_err ( "unpersisted output must not be acknowledged" ) ;
416426
417427 assert_eq ! (
418428 sketch_index. instance_count( ) ,
@@ -430,7 +440,8 @@ mod tests {
430440 // miss — it must not bump the counter.
431441 let unset = PrecomputedOutput :: new ( 1000 , 2000 , None , asap_types:: PolicyFingerprint :: UNSET ) ;
432442 let acc2: Box < dyn AggregateCore > = Box :: new ( SumAccumulator :: with_sum ( 1.0 ) ) ;
433- sink. emit_batch ( vec ! [ ( unset, acc2) ] ) . expect ( "emit ok" ) ;
443+ sink. emit_batch ( vec ! [ ( unset, acc2) ] )
444+ . expect_err ( "unpersisted output must not be acknowledged" ) ;
434445 assert_eq ! (
435446 obs. dropped_policy_miss
436447 . load( std:: sync:: atomic:: Ordering :: Relaxed ) ,
0 commit comments