Middle+
// We have a method for retrieving product information, this method is called very frequently // 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:
- Parallel addition to the
productsslice without synchronization — this can lead to data races and potential slice corruption. - Launching goroutines for each ID without controlling the number of concurrently running goroutines can overload the database.
- The method returns the slice immediately, without waiting for goroutines to finish, so the result will always be empty.
How to fix:
- Use a single SQL query with the
INoperator to fetch all products in one call, which is more efficient and simpler. - If parallel execution is needed, use
sync.WaitGroupand a mutex for safe addition to the slice.
Example of a fixed version with a single query:
func (r *ProductRepository) GetProductDetails(ctx context.Context, productIDs []int) ([]Product, error) {
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, nil
}
This approach reduces the load on the database and eliminates issues with concurrent access.