107469c328
continuous-integration/drone/push Build is passing
- Introduced a caching mechanism for translation responses in the tender recommendation service, improving efficiency by reducing redundant translation requests. - Refactored the `enrichWithTranslation` method to support an optional translation cache, allowing for faster retrieval of stored translations. - Implemented parallel processing for building recommended tender responses, utilizing goroutines to enhance performance and responsiveness. - Added unit tests to validate the new caching behavior and ensure the correct application of translations in various scenarios. This update significantly improves the performance and responsiveness of the tender recommendation service by integrating caching and parallel processing, enhancing the overall user experience.
130 lines
3.2 KiB
Go
130 lines
3.2 KiB
Go
package tender
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
"tm/pkg/ai_summarizer"
|
|
)
|
|
|
|
const recommendationTranslationConcurrency = 8
|
|
|
|
type rankedRecommendedTender struct {
|
|
tender Tender
|
|
rank int
|
|
analysis string
|
|
tenderRef string
|
|
}
|
|
|
|
type recommendationTranslationCache struct {
|
|
mu sync.Mutex
|
|
items map[string]ai_summarizer.StoredTranslation
|
|
}
|
|
|
|
func newRecommendationTranslationCache() *recommendationTranslationCache {
|
|
return &recommendationTranslationCache{
|
|
items: make(map[string]ai_summarizer.StoredTranslation),
|
|
}
|
|
}
|
|
|
|
func recommendationTranslationCacheKey(tenderID, language string) string {
|
|
return strings.TrimSpace(tenderID) + "|" + strings.TrimSpace(language)
|
|
}
|
|
|
|
func (c *recommendationTranslationCache) get(tenderID, language string) (ai_summarizer.StoredTranslation, bool) {
|
|
if c == nil {
|
|
return ai_summarizer.StoredTranslation{}, false
|
|
}
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
stored, ok := c.items[recommendationTranslationCacheKey(tenderID, language)]
|
|
return stored, ok
|
|
}
|
|
|
|
func (c *recommendationTranslationCache) set(tenderID, language string, stored ai_summarizer.StoredTranslation) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
c.items[recommendationTranslationCacheKey(tenderID, language)] = stored
|
|
}
|
|
|
|
func (s *tenderService) buildRecommendedTenderResponsesParallel(
|
|
ctx context.Context,
|
|
items []rankedRecommendedTender,
|
|
language string,
|
|
) []TenderResponse {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
|
|
out := make([]TenderResponse, len(items))
|
|
if len(items) == 1 {
|
|
out[0] = s.buildRecommendedTenderResponse(ctx, items[0].tender, items[0].rank, items[0].analysis, items[0].tenderRef, language, newRecommendationTranslationCache())
|
|
return out
|
|
}
|
|
|
|
translationCache := newRecommendationTranslationCache()
|
|
group, groupCtx := errgroup.WithContext(ctx)
|
|
group.SetLimit(recommendationTranslationConcurrency)
|
|
|
|
for i := range items {
|
|
i := i
|
|
item := items[i]
|
|
group.Go(func() error {
|
|
out[i] = s.buildRecommendedTenderResponse(
|
|
groupCtx,
|
|
item.tender,
|
|
item.rank,
|
|
item.analysis,
|
|
item.tenderRef,
|
|
language,
|
|
translationCache,
|
|
)
|
|
return nil
|
|
})
|
|
}
|
|
|
|
if err := group.Wait(); err != nil {
|
|
s.logger.Warn("Parallel recommendation response build degraded to sequential", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"count": len(items),
|
|
})
|
|
for i, item := range items {
|
|
out[i] = s.buildRecommendedTenderResponse(ctx, item.tender, item.rank, item.analysis, item.tenderRef, language, translationCache)
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func (s *tenderService) loadRecommendationExclusions(
|
|
ctx context.Context,
|
|
form *SearchForm,
|
|
companyID string,
|
|
companyIDs []string,
|
|
) (map[string]struct{}, map[string]map[string]struct{}, error) {
|
|
if !form.ExcludeRejectedTenders {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
if len(companyIDs) > 0 {
|
|
excludedByCompany, err := s.buildRejectedTenderIDSetsForCompanies(ctx, companyIDs)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to build recommendation exclusions: %w", err)
|
|
}
|
|
return nil, excludedByCompany, nil
|
|
}
|
|
|
|
excluded, err := s.buildRejectedTenderIDSet(ctx, companyID)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to build recommendation exclusions: %w", err)
|
|
}
|
|
return excluded, nil, nil
|
|
}
|