Skip to content

Commit 725aa5b

Browse files
committed
Fix data race, close #7287 #7110 #7539 #7108
1 parent 6cc2c01 commit 725aa5b

4 files changed

Lines changed: 151 additions & 123 deletions

File tree

schema/relationship.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ func (schema *Schema) parseRelation(field *Field) *Relationship {
7575
}
7676
)
7777

78-
cacheStore := schema.cacheStore
79-
80-
if relation.FieldSchema, err = getOrParse(fieldValue, cacheStore, schema.namer); err != nil {
78+
if relation.FieldSchema, err = getOrParse(fieldValue, schema.cacheStore, schema.namer); err != nil {
8179
schema.err = fmt.Errorf("failed to parse field: %s, error: %w", field.Name, err)
8280
return nil
8381
}
@@ -147,6 +145,9 @@ func hasPolymorphicRelation(tagSettings map[string]string) bool {
147145
}
148146

149147
func (schema *Schema) setRelation(relation *Relationship) {
148+
schema.Relationships.Mux.Lock()
149+
defer schema.Relationships.Mux.Unlock()
150+
150151
// set non-embedded relation
151152
if rel := schema.Relationships.Relations[relation.Name]; rel != nil {
152153
if len(rel.Field.BindNames) > 1 {
@@ -590,13 +591,21 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl gu
590591
// build references
591592
for idx, foreignField := range foreignFields {
592593
// use same data type for foreign keys
594+
schema.Relationships.Mux.Lock()
595+
if schema != foreignField.Schema {
596+
foreignField.Schema.Relationships.Mux.Lock()
597+
}
593598
if copyableDataType(primaryFields[idx].DataType) {
594599
foreignField.DataType = primaryFields[idx].DataType
595600
}
596601
foreignField.GORMDataType = primaryFields[idx].GORMDataType
597602
if foreignField.Size == 0 {
598603
foreignField.Size = primaryFields[idx].Size
599604
}
605+
schema.Relationships.Mux.Unlock()
606+
if schema != foreignField.Schema {
607+
foreignField.Schema.Relationships.Mux.Unlock()
608+
}
600609

601610
relation.References = append(relation.References, &Reference{
602611
PrimaryKey: primaryFields[idx],

schema/relationship_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package schema_test
33
import (
44
"sync"
55
"testing"
6+
"time"
67

78
"gorm.io/gorm"
89
"gorm.io/gorm/schema"
10+
"gorm.io/gorm/utils/tests"
911
)
1012

1113
func checkStructRelation(t *testing.T, data interface{}, relations ...Relation) {
@@ -996,3 +998,46 @@ func TestParseConstraintNameWithSchemaQualifiedLongTableName(t *testing.T) {
996998
)
997999
}
9981000
}
1001+
1002+
type InfoRelation struct {
1003+
ID int
1004+
Code string
1005+
Info1 []*Info1 `gorm:"foreignkey:Code;references:Code"`
1006+
Info2 []*Info2 `gorm:"foreignkey:Code;references:Code"`
1007+
}
1008+
1009+
type Info1 struct {
1010+
CreatedAt time.Time
1011+
UpdatedAt time.Time
1012+
Code string
1013+
Relation []*InfoRelation `gorm:"foreignkey:Code;references:Code"`
1014+
}
1015+
1016+
type Info2 struct {
1017+
CreatedAt time.Time
1018+
UpdatedAt time.Time
1019+
Code string
1020+
Relation []*InfoRelation `gorm:"foreignkey:Code;references:Code"`
1021+
}
1022+
1023+
func TestDataRace(t *testing.T) {
1024+
syncMap := &sync.Map{}
1025+
for i := 0; i < 10; i++ {
1026+
go func() {
1027+
schema.Parse(&Info1{}, syncMap, schema.NamingStrategy{IdentifierMaxLength: 64})
1028+
}()
1029+
1030+
go func() {
1031+
schema.Parse(&Info2{}, syncMap, schema.NamingStrategy{IdentifierMaxLength: 64})
1032+
}()
1033+
1034+
go func() {
1035+
var result User
1036+
schema.Parse(&result, syncMap, schema.NamingStrategy{IdentifierMaxLength: 64})
1037+
}()
1038+
go func() {
1039+
var result tests.Account
1040+
schema.Parse(&result, syncMap, schema.NamingStrategy{IdentifierMaxLength: 64})
1041+
}()
1042+
}
1043+
}

0 commit comments

Comments
 (0)