func (c *Client) Send(ctx context.Context, messages []Message, maxWorkers int) error {

Нужны WithCancel, проверка maxWorkers и ошибок создания запроса, отменяемое ожидание слота и возврат error. Сейчас cancel не определён, return отсутствует, а горутин создаётся по числу сообщений.

Короткий ответ

Что ответить на собеседовании

Нужны WithCancel, проверка maxWorkers и ошибок создания запроса, отменяемое ожидание слота и возврат error. Сейчас cancel не определён, return отсутствует, а горутин создаётся по числу сообщений.

Подробный разбор

Ответ с пояснениями

Условие

func (c *Client) Send(ctx context.Context, messages []Message, maxWorkers int) error { sem := make(chan struct{}, maxWorkers)

var wg sync.WaitGroup
var resultErr error
var once sync.Once

for _, msg := range messages {
	wg.Add(1)

	go func(msg Message) {
		defer wg.Done()

		sem <- struct{}{}
		defer func() {
			<-sem
		}()

		body, err := json.Marshal(msg)
		if err != nil {
			once.Do(func() { resultErr = err; cancel() })
			return
		}

		req, _ := http.NewRequestWithContext(
			ctx,
			http.MethodPost,
			c.url,
			bytes.NewReader(body),
		)

		req.Header.Set("Content-Type", "application/json")

		resp, err := c.httpClient.Do(req)
		if err != nil {
			once.Do(func() { resultErr = err; cancel()})
			return
		}
		defer resp.Body.Close()

		if resp.StatusCode >= 500 {
			once.Do(func() {
				resultErr = fmt.Errorf("server error")
				cancel()})
			return
		}

		c.sentCount.Add(1)

		time.Sleep(time.Second)
	}(msg)
}

wg.Wait()

}

Ответ

Нужны WithCancel, проверка maxWorkers и ошибок создания запроса, отменяемое ожидание слота и возврат error. Сейчас cancel не определён, return отсутствует, а горутин создаётся по числу сообщений.

Минимальная схема с ограничением числа активных горутин; успехом здесь считаются HTTP 2xx:

func (c *Client) Send(ctx context.Context, messages []Message, maxWorkers int) error {
    if maxWorkers <= 0 { return fmt.Errorf("maxWorkers must be positive") }
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()
    sem := make(chan struct{}, maxWorkers)
    var wg sync.WaitGroup
    var once sync.Once
    var resultErr error
    fail := func(err error) {
        once.Do(func() { resultErr = err; cancel() })
    }
loop:
    for _, msg := range messages {
        select {
        case <-ctx.Done(): break loop
        case sem <- struct{}{}:
        }
        if ctx.Err() != nil { <-sem; break }
        wg.Add(1)
        go func(msg Message) {
            defer wg.Done()
            defer func() { <-sem }()
            body, err := json.Marshal(msg)
            if err != nil { fail(err); return }
            req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.url, bytes.NewReader(body))
            if err != nil { fail(err); return }
            req.Header.Set("Content-Type", "application/json")
            resp, err := c.httpClient.Do(req)
            if err != nil { fail(err); return }
            defer resp.Body.Close()
            if resp.StatusCode < 200 || resp.StatusCode >= 300 {
                fail(fmt.Errorf("unexpected HTTP status: %d", resp.StatusCode))
                return
            }
            c.sentCount.Add(1)
        }(msg)
    }
    wg.Wait()
    if resultErr != nil { return resultErr }
    return ctx.Err()
}

Используются bytes, context, encoding/json, fmt, net/http, sync. Client, Message и атомарный sentCount остаются из условия. Для реального клиента задайте timeout; конкурентность не заменяет rate limit. Sleep удалён: пауза в каждой горутине не задаёт общий лимит запросов в секунду.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее