Параллельное выполнение gorm запросов в golang
У меня есть 2 сущности:
type Service struct {
ID string
Name string
}
type ConnectedService struct {
ID int
ServiceID string
Service Service
}
Я хочу, используя gorm.io/gorm v2, получить доступ к связанной сущности Service, получая ConnectedService.
Проблема в том, что если выполнять параллельно (в go-рутинах) несколько запросов, на получение ConnectedService, то вылетает ошибка, если делать последовательно, ошибки нет:
ERROR: invalid reference to FROM-clause entry for table "connected_services" (SQLSTATE 42P01)
[4.020ms] [rows:0] SELECT "connected_services"."id","connected_services"."service_id" FROM "connected_services" Service WHERE "connected_services"."id" = 3 ORDER BY "connected_services"."id" LIMIT 1
FATA[0000] ERROR: invalid reference to FROM-clause entry for table "connected_services" (SQLSTATE 42P01)
Это связанно с тем, что gorm v2 использует: Method Chain Safety/Goroutine Safety. Вот что пишут на оф. сайте:
To reduce GC allocs, GORM V2 will share Statement when using method chains, and will only create new Statement instances for new initialized *gorm.DB or after a New Session Method, to reuse a *gorm.DB, you need to make sure it just after a New Session Method, for example:
Я пробовал делать запросы с разными контекстами и ничего не получается.
Так же, если убрать .Joins("Service"), то работает и синхронно, и асинхронно.
Запросы выполняю так:
type Repository struct { db *gorm.DB }
func (r *Repository) GetConnectedService(id int) (*ConnectedService, error) {
cs := &ConnectedService{}
if err := r.db.WithContext(context.Background()).Joins("Service").First(cs, id).Error; err != nil {
return nil, err
}
return cs, nil
}
Инициализация дб и репозитория:
func main() {
dsn := fmt.Sprintf("host=localhost port=5433 user=guest password=guest dbname=mydb sslmode=disable")
db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{})
repo := &Repository{db: db}
. . .
}
Вызов делаю так:
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func main() {
. . .
for i := 1; i < 10; i++ {
go func(i int) {
cs, err := repo.GetConnectedService(i)
if err != nil {
logrus.Errorf("get connected service: %s", err.Error())
return
}
logrus.Printf("%+v", cs)
}(i)
}
. . .
}
Without <.Joins("Service")> everything works fine, but as soon as we add this, an error crashes.