@@ -298,6 +298,31 @@ ChunkedSegmentSealedImpl::LoadFieldData(const LoadFieldDataInfo& load_info,
298298 }
299299}
300300
301+ namespace {
302+
303+ // A column group or field-binlog group may contain multiple fields but has only
304+ // one translator warmup policy. Accumulate per-field policies into the most
305+ // aggressive group policy: sync > async > disable. Empty means no field has
306+ // provided an explicit warmup setting yet, so downstream logic may still fall
307+ // back to global config.
308+ void
309+ AccumulateWarmupPolicyForGroup (const std::string& policy,
310+ std::string& aggregated_warmup_policy) {
311+ if (policy.empty ()) {
312+ return ;
313+ }
314+
315+ if (policy == " sync" ) {
316+ aggregated_warmup_policy = " sync" ;
317+ } else if (policy == " async" && aggregated_warmup_policy != " sync" ) {
318+ aggregated_warmup_policy = " async" ;
319+ } else if (policy == " disable" && aggregated_warmup_policy.empty ()) {
320+ aggregated_warmup_policy = " disable" ;
321+ }
322+ }
323+
324+ } // namespace
325+
301326void
302327ChunkedSegmentSealedImpl::load_column_group_data_internal (
303328 const LoadFieldDataInfo& load_info, milvus::OpContext* op_ctx) {
@@ -357,6 +382,16 @@ ChunkedSegmentSealedImpl::load_column_group_data_internal(
357382 mmap_dir_path);
358383
359384 auto field_metas = schema_->get_field_metas (milvus_field_ids);
385+ std::string warmup_policy = info.warmup_policy ;
386+ if (warmup_policy.empty ()) {
387+ std::string aggregated_warmup_policy;
388+ for (auto field_id : milvus_field_ids) {
389+ AccumulateWarmupPolicyForGroup (
390+ resolve_field_data_warmup_policy (field_id),
391+ aggregated_warmup_policy);
392+ }
393+ warmup_policy = aggregated_warmup_policy;
394+ }
360395
361396 auto translator =
362397 std::make_unique<storagev2translator::GroupChunkTranslator>(
@@ -369,7 +404,7 @@ ChunkedSegmentSealedImpl::load_column_group_data_internal(
369404 mmap_config.GetMmapPopulate (),
370405 milvus_field_ids.size (),
371406 load_info.load_priority ,
372- info. warmup_policy );
407+ warmup_policy);
373408
374409 auto chunked_column_group =
375410 std::make_shared<ChunkedColumnGroup>(std::move (translator));
@@ -493,6 +528,8 @@ ChunkedSegmentSealedImpl::load_field_data_internal(
493528 storage::SortByPath (file_infos);
494529
495530 auto field_meta = schema_->operator [](field_id);
531+ auto warmup_policy =
532+ resolve_field_data_warmup_policy (field_id, info.warmup_policy );
496533 std::unique_ptr<Translator<milvus::Chunk>> translator =
497534 std::make_unique<storagev1translator::ChunkTranslator>(
498535 this ->get_segment_id (),
@@ -502,7 +539,7 @@ ChunkedSegmentSealedImpl::load_field_data_internal(
502539 info.enable_mmap ,
503540 mmap_config.GetMmapPopulate (),
504541 load_info.load_priority ,
505- info. warmup_policy );
542+ warmup_policy);
506543
507544 auto data_type = field_meta.get_data_type ();
508545 auto slot = cachinglayer::Manager::GetInstance ().CreateCacheSlot (
@@ -877,6 +914,69 @@ ChunkedSegmentSealedImpl::mask_with_delete(BitsetTypeView& bitset,
877914 deleted_record_.Query (bitset, ins_barrier, timestamp);
878915}
879916
917+ std::string
918+ ChunkedSegmentSealedImpl::resolve_field_data_warmup_policy (
919+ FieldId field_id, const std::string& explicit_warmup_policy) {
920+ if (SystemProperty::Instance ().IsSystem (field_id)) {
921+ return explicit_warmup_policy;
922+ }
923+
924+ // "Has index" here means an index is usable now:
925+ // - SegmentLoadInfo covers DataCoord-built index files loaded with segment
926+ // metadata.
927+ // - binlog_index_bitset_ covers interim indexes already generated from raw
928+ // binlog data.
929+ // Do not predict whether an interim index may be generated later. Before an
930+ // index exists, raw vector data is the search-critical representation and
931+ // follows vector-index warmup.
932+ bool has_index = false ;
933+ {
934+ std::shared_lock lck (mutex_);
935+ has_index = segment_load_info_.HasIndexInfo (field_id);
936+ if (!has_index) {
937+ has_index = get_bit (binlog_index_bitset_, field_id);
938+ }
939+ }
940+
941+ const auto & field_meta = schema_->operator [](field_id);
942+ auto is_vector = IsVectorDataType (field_meta.get_data_type ());
943+
944+ // explicit_warmup_policy is the field-data override carried by the current
945+ // load request. It is normally authoritative. The only exception is vector
946+ // raw data without a usable index: QueryCoord may have propagated
947+ // warmup.vectorField=disable into the field TypeParams, but in this state
948+ // the raw vector column is effectively the index/search path and must use
949+ // vector-index warmup instead.
950+ if (!explicit_warmup_policy.empty () && (!is_vector || has_index)) {
951+ return explicit_warmup_policy;
952+ }
953+
954+ if (is_vector && !has_index) {
955+ auto [has_index_warmup, index_warmup_policy] =
956+ schema_->CollectionWarmupPolicy (/* is_vector=*/ true ,
957+ /* is_index=*/ true );
958+ if (has_index_warmup) {
959+ return index_warmup_policy;
960+ }
961+
962+ switch (milvus::cachinglayer::Manager::GetInstance ()
963+ .getVectorIndexCacheWarmupPolicy ()) {
964+ case CacheWarmupPolicy::CacheWarmupPolicy_Sync:
965+ return " sync" ;
966+ case CacheWarmupPolicy::CacheWarmupPolicy_Async:
967+ return " async" ;
968+ case CacheWarmupPolicy::CacheWarmupPolicy_Disable:
969+ return " disable" ;
970+ default :
971+ return " " ;
972+ }
973+ }
974+
975+ auto [has_field_warmup, field_warmup_policy] =
976+ schema_->WarmupPolicy (field_id, is_vector, /* is_index=*/ false );
977+ return has_field_warmup ? field_warmup_policy : " " ;
978+ }
979+
880980void
881981ChunkedSegmentSealedImpl::vector_search (SearchInfo& search_info,
882982 const void * query_data,
@@ -3133,14 +3233,11 @@ ChunkedSegmentSealedImpl::LoadColumnGroup(
31333233 bool is_vector = false ;
31343234 bool has_mmap_setting = false ;
31353235 bool mmap_enabled = false ;
3136- bool has_warmup_setting = false ;
3137- bool warmup_sync = false ;
3236+ std::string aggregated_warmup_policy;
31383237 for (auto & [field_id, field_meta] : field_metas) {
31393238 if (IsVectorDataType (field_meta.get_data_type ())) {
31403239 is_vector = true ;
31413240 }
3142- std::shared_lock lck (mutex_);
3143- auto iter = index_has_raw_data_.find (field_id);
31443241
31453242 // if field has mmap setting, use it
31463243 // - mmap setting at collection level, then all field are the same
@@ -3150,23 +3247,14 @@ ChunkedSegmentSealedImpl::LoadColumnGroup(
31503247 has_mmap_setting = has_mmap_setting || field_has_setting;
31513248 mmap_enabled = mmap_enabled || field_mmap_enabled;
31523249
3153- // if field has warmup setting, use it
3154- // - warmup setting at collection level, uses appropriate key based on field type
3155- // - warmup setting at field level, use the most aggressive policy (sync > disable)
3156- // Note: this is for field data loading, not index (is_index = false)
3157- bool field_is_vector = IsVectorDataType (field_meta.get_data_type ());
3158- auto [field_has_warmup, field_warmup_policy] = schema_->WarmupPolicy (
3159- field_id, field_is_vector, /* is_index=*/ false );
3160- if (field_has_warmup) {
3161- has_warmup_setting = true ;
3162- warmup_sync = warmup_sync || (field_warmup_policy == " sync" );
3163- }
3250+ AccumulateWarmupPolicyForGroup (
3251+ resolve_field_data_warmup_policy (field_id),
3252+ aggregated_warmup_policy);
31643253 }
31653254
31663255 // Determine warmup policy: use per-field settings if any,
31673256 // otherwise pass empty string to fall back to global config
3168- std::string warmup_policy =
3169- has_warmup_setting ? (warmup_sync ? " sync" : " disable" ) : " " ;
3257+ std::string warmup_policy = aggregated_warmup_policy;
31703258
31713259 auto & mmap_config = storage::MmapManager::GetInstance ().GetMmapConfig ();
31723260 bool global_use_mmap = is_vector ? mmap_config.GetVectorFieldEnableMmap ()
@@ -3347,8 +3435,7 @@ ChunkedSegmentSealedImpl::LoadBatchFieldData(
33473435 bool is_vector = false ;
33483436 bool has_nullable_vector = false ;
33493437
3350- bool has_warmup_setting = false ;
3351- bool warmup_sync = false ;
3438+ std::string aggregated_warmup_policy;
33523439 for (const auto & child_field_id : field_ids) {
33533440 auto & field_meta = schema_->operator [](child_field_id);
33543441 if (IsVectorDataType (field_meta.get_data_type ())) {
@@ -3372,15 +3459,9 @@ ChunkedSegmentSealedImpl::LoadBatchFieldData(
33723459 index_has_raw_data = false ;
33733460 }
33743461
3375- auto [field_has_warmup, field_warmup_policy] =
3376- schema_->WarmupPolicy (
3377- child_field_id,
3378- IsVectorDataType (field_meta.get_data_type ()),
3379- /* is_index=*/ false );
3380- if (field_has_warmup) {
3381- has_warmup_setting = true ;
3382- warmup_sync = warmup_sync || (field_warmup_policy == " sync" );
3383- }
3462+ AccumulateWarmupPolicyForGroup (
3463+ resolve_field_data_warmup_policy (child_field_id),
3464+ aggregated_warmup_policy);
33843465 }
33853466
33863467 auto group_id = field_binlog.fieldid ();
@@ -3423,8 +3504,7 @@ ChunkedSegmentSealedImpl::LoadBatchFieldData(
34233504
34243505 // Determine group warmup policy: use per-field settings if any,
34253506 // otherwise fall back to global warmup policy
3426- field_binlog_info.warmup_policy =
3427- has_warmup_setting ? (warmup_sync ? " sync" : " disable" ) : " " ;
3507+ field_binlog_info.warmup_policy = aggregated_warmup_policy;
34283508
34293509 // Store in map
34303510 load_field_data_info.field_infos [group_id] = field_binlog_info;
0 commit comments