[hw-5] concurrency, graceful shutdown, concurrent tests

This commit is contained in:
Никита Шубин
2025-07-06 20:52:27 +00:00
parent dbf8aaedcf
commit 84201fe495
23 changed files with 742 additions and 157 deletions

View File

@@ -4,9 +4,6 @@ import (
"context"
"fmt"
"slices"
"sync"
"github.com/rs/zerolog/log"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
@@ -22,6 +19,7 @@ type Repository interface {
type ProductService interface {
GetProductBySku(ctx context.Context, sku entity.Sku) (*model.Product, error)
GetProducts(ctx context.Context, skus []entity.Sku) ([]*model.Product, error)
}
type LomsService interface {
@@ -77,9 +75,6 @@ func (s *CartService) AddItem(ctx context.Context, userID entity.UID, item *mode
// GetUserCart gets all user cart's item ids, gets the item description from the product-service
// and return a list of the collected items.
// In case of failed request to product-service, return nothing and error.
//
// TODO: add worker group, BUT it's OK for now,
// assuming user does not have hundreds of different items in his cart.
func (s *CartService) GetItemsByUserID(ctx context.Context, userID entity.UID) (*model.Cart, error) {
if userID <= 0 {
return nil, fmt.Errorf("userID invalid")
@@ -99,65 +94,31 @@ func (s *CartService) GetItemsByUserID(ctx context.Context, userID entity.UID) (
resultCart := &model.Cart{
UserID: userID,
Items: make([]*model.Item, len(cart.Items)),
Items: make([]*model.Item, 0, len(cart.Items)),
TotalPrice: 0,
}
errCh := make(chan error, 1)
var (
wg sync.WaitGroup
sumMutex sync.Mutex
)
for idx, sku := range cart.Items {
wg.Add(1)
go func(sku entity.Sku, count uint32, idx int) {
defer wg.Done()
product, err := s.productService.GetProductBySku(ctx, sku)
if err != nil {
select {
case errCh <- fmt.Errorf("productService.GetProductBySku: %w", err):
case <-ctx.Done():
}
log.Error().Err(err).Msg("productService.GetProductBySku")
return
}
resultCart.Items[idx] = &model.Item{
Product: product,
Count: count,
}
sumMutex.Lock()
resultCart.TotalPrice += uint32(product.Price) * count
sumMutex.Unlock()
}(sku, cart.ItemCount[sku], idx)
products, err := s.productService.GetProducts(ctx, cart.Items)
if err != nil {
return nil, err
}
doneCh := make(chan struct{})
go func() {
wg.Wait()
for _, product := range products {
cnt := cart.ItemCount[product.Sku]
close(doneCh)
}()
select {
case err := <-errCh:
cancel()
return nil, err
case <-doneCh:
slices.SortStableFunc(resultCart.Items, func(a, b *model.Item) int {
return int(a.Product.Sku - b.Product.Sku)
resultCart.Items = append(resultCart.Items, &model.Item{
Product: product,
Count: cnt,
})
return resultCart, nil
resultCart.TotalPrice += cnt * uint32(product.Price)
}
slices.SortStableFunc(resultCart.Items, func(a, b *model.Item) int {
return int(a.Product.Sku - b.Product.Sku)
})
return resultCart, nil
}
func (s *CartService) DeleteItem(ctx context.Context, userID entity.UID, sku entity.Sku) error {