package db import ( "context" "fmt" "sync" "time" "github.com/jmoiron/sqlx" ) type AllowedIds interface { ~uint | ~string } type InsertId[T AllowedIds] struct { id T fieldName string tableName string } type SyncDbOperations struct { errChan chan error wg sync.WaitGroup } func (s *SyncDbOperations) Wait() error { return WaitSync(&s.wg, s.errChan) } func WaitSync(wg *sync.WaitGroup, errChan chan error) error { wgDone := make(chan struct{}) go func() { wg.Wait() close(wgDone) }() select { case <-wgDone: return nil case err := <-errChan: close(wgDone) return err } } type Insertable[T AllowedIds] interface { Insert(tx *sqlx.Tx, relId *InsertId[T]) error InsertCtx(ctx context.Context, tx *sqlx.Tx, relId *InsertId[T]) error } func InsertWithCtx[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data I, relId *InsertId[T]) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() return data.InsertCtx(ctx, tx, relId) } func InsertSlice[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data []I, relId *InsertId[T]) error { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() for i := range data { err := data[i].InsertCtx(ctx, tx, relId) if err != nil { return err } } return nil } func InsertSliceParallel[T AllowedIds, I Insertable[T]](syncDb *SyncDbOperations, tx *sqlx.Tx, data []I, relId *InsertId[T]) { syncDb.wg.Add(1) wg := sync.WaitGroup{} errChan := make(chan error) defer close(errChan) for i := range data { go func() { wg.Add(1) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() errChan <- data[i].InsertCtx(ctx, tx, relId) wg.Done() }() } err := WaitSync(&wg, errChan) if err != nil { syncDb.errChan <- err } syncDb.wg.Done() } func InsertSimpleSliceToTable[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data []I, relId *InsertId[T]) error { query := fmt.Sprintf(` INSERT INTO %s (%s, value) VALUES (:id, :value)`, relId.tableName, relId.fieldName) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() for i := range data { _, err := tx.ExecContext(ctx, query, relId.id, data[i]) if err != nil { return err } } return nil }