@@ -2633,45 +2633,80 @@ impl MetastoreService for PostgresqlMetastore {
26332633 ) ;
26342634
26352635 // Only delete splits that are marked for deletion
2636+ // Match the non-metrics delete_splits pattern: distinguish
2637+ // "not found" (warn + succeed) from "not deletable" (FailedPrecondition).
26362638 const DELETE_SPLITS_QUERY : & str = r#"
2637- DELETE FROM metrics_splits
2638- WHERE
2639- index_uid = $1
2640- AND split_id = ANY($2)
2641- AND split_state = 'MarkedForDeletion'
2642- RETURNING split_id
2639+ WITH input_splits AS (
2640+ SELECT input_splits.split_id, metrics_splits.split_state
2641+ FROM UNNEST($2::text[]) AS input_splits(split_id)
2642+ LEFT JOIN metrics_splits
2643+ ON metrics_splits.index_uid = $1
2644+ AND metrics_splits.split_id = input_splits.split_id
2645+ ),
2646+ deleted AS (
2647+ DELETE FROM metrics_splits
2648+ USING input_splits
2649+ WHERE
2650+ metrics_splits.index_uid = $1
2651+ AND metrics_splits.split_id = input_splits.split_id
2652+ AND NOT EXISTS (
2653+ SELECT 1 FROM input_splits
2654+ WHERE split_state IN ('Staged', 'Published')
2655+ )
2656+ RETURNING metrics_splits.split_id
2657+ )
2658+ SELECT
2659+ (SELECT COUNT(*) FROM input_splits WHERE split_state IS NOT NULL) as num_found,
2660+ (SELECT COUNT(*) FROM deleted) as num_deleted,
2661+ COALESCE(
2662+ (SELECT ARRAY_AGG(split_id) FROM input_splits
2663+ WHERE split_state IN ('Staged', 'Published')),
2664+ ARRAY[]::text[]
2665+ ) as not_deletable,
2666+ COALESCE(
2667+ (SELECT ARRAY_AGG(split_id) FROM input_splits
2668+ WHERE split_state IS NULL),
2669+ ARRAY[]::text[]
2670+ ) as not_found
26432671 "# ;
26442672
2645- let deleted_split_ids: Vec < String > = sqlx:: query_scalar ( DELETE_SPLITS_QUERY )
2673+ let ( num_found, num_deleted, not_deletable_ids, not_found_ids) : (
2674+ i64 ,
2675+ i64 ,
2676+ Vec < String > ,
2677+ Vec < String > ,
2678+ ) = sqlx:: query_as ( DELETE_SPLITS_QUERY )
26462679 . bind ( request. index_uid ( ) )
26472680 . bind ( & request. split_ids )
2648- . fetch_all ( & self . connection_pool )
2681+ . fetch_one ( & self . connection_pool )
26492682 . await
26502683 . map_err ( |sqlx_error| convert_sqlx_err ( & request. index_uid ( ) . index_id , sqlx_error) ) ?;
26512684
2652- // Log if some splits were not deleted (either non-existent or not
2653- // in MarkedForDeletion state). Delete is idempotent — we don't error
2654- // for missing splits.
2655- if deleted_split_ids . len ( ) != request . split_ids . len ( ) {
2656- let not_deleted : Vec < String > = request
2657- . split_ids
2658- . iter ( )
2659- . filter ( |id| !deleted_split_ids . contains ( id ) )
2660- . cloned ( )
2661- . collect ( ) ;
2685+ if !not_deletable_ids . is_empty ( ) {
2686+ let message = format ! (
2687+ " splits `{}` are not deletable" ,
2688+ not_deletable_ids . join ( ", " )
2689+ ) ;
2690+ let entity = EntityKind :: Splits {
2691+ split_ids : not_deletable_ids ,
2692+ } ;
2693+ return Err ( MetastoreError :: FailedPrecondition { entity , message } ) ;
2694+ }
26622695
2663- if !not_deleted . is_empty ( ) {
2664- warn ! (
2665- index_uid = %request. index_uid( ) ,
2666- not_deleted = ?not_deleted ,
2667- "some metrics splits were not deleted (non-existent or not marked for deletion)"
2668- ) ;
2669- }
2696+ if !not_found_ids . is_empty ( ) {
2697+ warn ! (
2698+ index_uid = %request. index_uid( ) ,
2699+ not_found = ?not_found_ids ,
2700+ "{} metrics splits were not found and could not be deleted" ,
2701+ not_found_ids . len ( )
2702+ ) ;
26702703 }
26712704
2705+ let _ = ( num_found, num_deleted) ; // used by the CTE logic
2706+
26722707 info ! (
26732708 index_uid = %request. index_uid( ) ,
2674- deleted_count = deleted_split_ids . len ( ) ,
2709+ num_deleted ,
26752710 "deleted metrics splits successfully"
26762711 ) ;
26772712 Ok ( EmptyResponse { } )
0 commit comments