Sobes.tech
Principal

// We have a method for retrieving product information, this method is called very often // are there any problems in this code and how to fix them? PostgreSQL database func (r *ProductRepository) GetProductDetails(ctx context.Context, productIDs []int) []Product { products := make([]Product, 0, len(productIDs)) for _, id := range productIDs { go func(id int) { var p Product query := "SELECT name, price, description FROM products WHERE id = $1" err := r.db.QueryRowContext(ctx, query, id).Scan(&p.Name, &p.Price, &p.Desc) if err != nil { r.logger.Error("error get product", "err", err) return } products = append(products, p) }(id) } return products }

sobes.tech AI

Answer from AI

The provided code has several issues:

  1. Goroutines and concurrent access to the products slice — multiple goroutines call append to the same slice simultaneously without synchronization, leading to data races and potential panics.

  2. Lack of waiting for goroutines to finish — the function returns products immediately after starting goroutines, without waiting for their completion, so the result will always be empty or incomplete.

  3. Multiple individual database queries — a separate query is executed for each productID, which is inefficient.

How to fix:

  • Use a single query with the WHERE id IN (...) operator to fetch all products in one call.

  • If parallelism is needed, use synchronization (e.g., sync.WaitGroup and mutex) for safe access to the slice.

Example of fixed code with a single query:

func (r *ProductRepository) GetProductDetails(ctx context.Context, productIDs []int) ([]Product, error) {
    if len(productIDs) == 0 {
        return nil, nil
    }

    query := `SELECT id, name, price, description FROM products WHERE id = ANY($1)`
    rows, err := r.db.QueryContext(ctx, query, pq.Array(productIDs))
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var products []Product
    for rows.Next() {
        var p Product
        if err := rows.Scan(&p.ID, &p.Name, &p.Price, &p.Desc); err != nil {
            return nil, err
        }
        products = append(products, p)
    }

    return products, rows.Err()
}

This approach reduces database load and eliminates issues with concurrent access.