sthuang a85e36bad2
fix: create collection task check failed after restart (#40982)
The fields and partitions information are stored and fetched with
different prefixes in the metadata. In the CreateCollectionTask, the
RootCoord checks the existing collection information against the
metadata. This check fails if the order of the fields or partitions info
differs, leading to an error after restarting Milvus. To resolve this,
we should use a map in the check logic to ensure consistency.

related: https://github.com/milvus-io/milvus/issues/40955

---------

Signed-off-by: shaoting-huang <shaoting.huang@zilliz.com>
2025-04-05 06:58:22 +08:00

172 lines
4.7 KiB
Go

package model
import (
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
"github.com/milvus-io/milvus/pkg/v2/common"
)
type Field struct {
FieldID int64
Name string
IsPrimaryKey bool
Description string
DataType schemapb.DataType
TypeParams []*commonpb.KeyValuePair
IndexParams []*commonpb.KeyValuePair
AutoID bool
State schemapb.FieldState
IsDynamic bool
IsPartitionKey bool // partition key mode, multi logic partitions share a physical partition
IsClusteringKey bool
IsFunctionOutput bool
DefaultValue *schemapb.ValueField
ElementType schemapb.DataType
Nullable bool
}
func (f *Field) Available() bool {
return f.State == schemapb.FieldState_FieldCreated
}
func (f *Field) Clone() *Field {
return &Field{
FieldID: f.FieldID,
Name: f.Name,
IsPrimaryKey: f.IsPrimaryKey,
Description: f.Description,
DataType: f.DataType,
TypeParams: common.CloneKeyValuePairs(f.TypeParams),
IndexParams: common.CloneKeyValuePairs(f.IndexParams),
AutoID: f.AutoID,
State: f.State,
IsDynamic: f.IsDynamic,
IsPartitionKey: f.IsPartitionKey,
IsClusteringKey: f.IsClusteringKey,
IsFunctionOutput: f.IsFunctionOutput,
DefaultValue: f.DefaultValue,
ElementType: f.ElementType,
Nullable: f.Nullable,
}
}
func CloneFields(fields []*Field) []*Field {
clone := make([]*Field, 0, len(fields))
for _, field := range fields {
clone = append(clone, field.Clone())
}
return clone
}
func checkParamsEqual(paramsA, paramsB []*commonpb.KeyValuePair) bool {
var A common.KeyValuePairs = paramsA
return A.Equal(paramsB)
}
func (f *Field) Equal(other Field) bool {
return f.FieldID == other.FieldID &&
f.Name == other.Name &&
f.IsPrimaryKey == other.IsPrimaryKey &&
f.Description == other.Description &&
f.DataType == other.DataType &&
checkParamsEqual(f.TypeParams, other.TypeParams) &&
checkParamsEqual(f.IndexParams, other.IndexParams) &&
f.AutoID == other.AutoID &&
f.IsPartitionKey == other.IsPartitionKey &&
f.IsDynamic == other.IsDynamic &&
f.IsClusteringKey == other.IsClusteringKey &&
f.DefaultValue == other.DefaultValue &&
f.ElementType == other.ElementType &&
f.IsFunctionOutput == other.IsFunctionOutput &&
f.Nullable == other.Nullable
}
func CheckFieldsEqual(fieldsA, fieldsB []*Field) bool {
if len(fieldsA) != len(fieldsB) {
return false
}
mapA := make(map[int64]*Field)
for _, f := range fieldsA {
mapA[f.FieldID] = f
}
for _, f := range fieldsB {
if other, exists := mapA[f.FieldID]; !exists || !f.Equal(*other) {
return false
}
}
return true
}
func MarshalFieldModel(field *Field) *schemapb.FieldSchema {
if field == nil {
return nil
}
return &schemapb.FieldSchema{
FieldID: field.FieldID,
Name: field.Name,
IsPrimaryKey: field.IsPrimaryKey,
Description: field.Description,
DataType: field.DataType,
TypeParams: field.TypeParams,
IndexParams: field.IndexParams,
AutoID: field.AutoID,
IsDynamic: field.IsDynamic,
IsPartitionKey: field.IsPartitionKey,
IsClusteringKey: field.IsClusteringKey,
IsFunctionOutput: field.IsFunctionOutput,
DefaultValue: field.DefaultValue,
ElementType: field.ElementType,
Nullable: field.Nullable,
}
}
func MarshalFieldModels(fields []*Field) []*schemapb.FieldSchema {
if fields == nil {
return nil
}
fieldSchemas := make([]*schemapb.FieldSchema, len(fields))
for idx, field := range fields {
fieldSchemas[idx] = MarshalFieldModel(field)
}
return fieldSchemas
}
func UnmarshalFieldModel(fieldSchema *schemapb.FieldSchema) *Field {
if fieldSchema == nil {
return nil
}
return &Field{
FieldID: fieldSchema.FieldID,
Name: fieldSchema.Name,
IsPrimaryKey: fieldSchema.IsPrimaryKey,
Description: fieldSchema.Description,
DataType: fieldSchema.DataType,
TypeParams: fieldSchema.TypeParams,
IndexParams: fieldSchema.IndexParams,
AutoID: fieldSchema.AutoID,
IsDynamic: fieldSchema.IsDynamic,
IsPartitionKey: fieldSchema.IsPartitionKey,
IsClusteringKey: fieldSchema.IsClusteringKey,
IsFunctionOutput: fieldSchema.IsFunctionOutput,
DefaultValue: fieldSchema.DefaultValue,
ElementType: fieldSchema.ElementType,
Nullable: fieldSchema.Nullable,
}
}
func UnmarshalFieldModels(fieldSchemas []*schemapb.FieldSchema) []*Field {
if fieldSchemas == nil {
return nil
}
fields := make([]*Field, len(fieldSchemas))
for idx, fieldSchema := range fieldSchemas {
fields[idx] = UnmarshalFieldModel(fieldSchema)
}
return fields
}