Postgres(GORM): violates foreign key constraint

У меня есть таблица отелей к которым подвязаны amenities, facilities. Что я хочу получить, мне нужно сделать синхронизацию таблицы, то есть когда приходит 2 запроса в одно и то же время, то чтобы не было дубликатов в таблице. Поле Code в таблице TempHotel является уникальным (это просто для примера там стоит AAAA), но вот когда приходит 2 запроса на одинаковые данные, чтобы избежать этих дубликатов, нужно применить ON CONFLICT DO NOTHING, но тогда ломаются ассоциации и я не знаю что с этим сделать.

При этом всём мне нужно добиться максимальной производительности, данные которые вносятся очень большие, нам мои данные отелей батч пакет на 64 вставки работает быстрее всего. Но если делать какие-то хуки и т.д, то из 5 секунд на вставку превращается в 2 минуты, то есть если добавить что-то типа такого

func (e *TempFacility) BeforeCreate(tx *gorm.DB) (err error) {
    tx.Statement.AddClause(clause.OnConflict{
        Columns:   []clause.Column{{Name: "code"}},
        DoUpdates: clause.AssignmentColumns([]string{"code"}),
    })
    return nil
}

Вот код который вы можете сразу запустить и проверить:

package main

import (
    "fmt"
    "math/rand"
    "time"

    "gorm.io/driver/postgres"
    "gorm.io/gorm"
    "gorm.io/gorm/clause"
)

const (
    host     = "localhost"
    user     = "root"
    password = "root"
    database = "primary"
)

type TempHotel struct {
    ID   int    `gorm:"primaryKey"`
    Code string `gorm:"uniqueIndex"`

    // Associations
    Amenities  []TempAmenity  `gorm:"foreignKey:HotelID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
    Facilities []TempFacility `gorm:"foreignKey:HotelID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}

type TempAmenity struct {
    ID      int `gorm:"primaryKey"`
    HotelID int `gorm:"index"`

    // Data
    Code string
}

type TempFacility struct {
    ID      int `gorm:"primaryKey"`
    HotelID int `gorm:"index"`

    // Data
    Code string
}

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}

func init() {
    rand.Seed(time.Now().UnixNano())
}

func main() {
    var err error
    dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=5432 sslmode=disable",
        host,
        user,
        password,
        database,
    )
    db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{PrepareStmt: true})
    if err != nil {
        panic(err)
    }

    if err := db.AutoMigrate(
        &TempHotel{},
        &TempAmenity{},
        &TempFacility{},
    ); err != nil {
        panic(err)
    }

    hotels := make([]TempHotel, 128)
    for i := 0; i < 128; i++ {
        hotels[i] = TempHotel{
            Code: "AAAA",
            Amenities: []TempAmenity{
                {
                    Code: RandStringRunes(5),
                },
            },
            Facilities: []TempFacility{
                {
                    Code: RandStringRunes(5),
                },
                {
                    Code: RandStringRunes(5),
                },
            },
        }
    }

    const batchSize = 64
    err = db.Clauses(&clause.OnConflict{DoNothing: true}).CreateInBatches(hotels, batchSize).Error
    if err != nil {
        panic(err)
    }
}

Выдает ошибку

ERROR: insert or update on table "temp_amenities" violates foreign key constraint "fk_temp_hotels_amenities" (SQLSTATE 23503); ERROR: insert or update on table "temp_amenities" violates foreign key constraint "fk_temp_hotels_amenities" (SQLSTATE 23503)

Ответы (0 шт):