Есть процесс клиентской поддержки. В рамках него есть процесс назначения тикетов поддержки на оператора. У оператора может быть не более 5 тикетов одновременно.

В текущей реализации есть риск гонки при параллельном назначении тикетов операторам, особенно если несколько потоков одновременно пытаются назначить тикеты.

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

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

В текущей реализации есть риск гонки при параллельном назначении тикетов операторам, особенно если несколько потоков одновременно пытаются назначить тикеты. Это может привести к превышению лимита в 5 тикетов на оператора.

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

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

Условие

Есть процесс клиентской поддержки. В рамках него есть процесс назначения тикетов поддержки на оператора. У оператора может быть не более 5 тикетов одновременно.

TABLE Tickets ( Id int PK, -- прочие колонки )

TABLE Operators ( Id int PK, -- прочие колонки )

-- маппинг оператор-тикет для активных тикетов. -- При закрытии тикета маппинг удаляется. TABLE OperatorsTicket ( TicketId int PK, OperatorId int NULL )

// в рамках транзакции public void TryAssignOperatorToTicket(int ticketId) { var avalibleOperator = _db.GetOperatorsWithTicketsCount(maxTickets: 4) .OrderBy(o => o.TicketsCount).FirstOrDefault();

if(avalibleOperator is not null)
    _db.AssignTicketToOperator(ticketId, avalibleOperator.Id);
else
    _db.AssignTicketToOperator(ticketId, null);

}

Ответ

В текущей реализации есть риск гонки при параллельном назначении тикетов операторам, особенно если несколько потоков одновременно пытаются назначить тикеты. Это может привести к превышению лимита в 5 тикетов на оператора.

Чтобы избежать этого, можно:

  1. Использовать блокировку на уровне базы данных — например, SELECT ... FOR UPDATE при выборке операторов с количеством тикетов.
  2. Внести ограничение на уровне базы данных, например, триггер или ограничение CHECK, чтобы не допустить более 5 активных тикетов на оператора.
  3. Внести дополнительное поле в таблицу OperatorsTicket, чтобы отслеживать статус тикета (активен/закрыт), и учитывать только активные.

Пример улучшенного подхода с использованием транзакции и блокировки:

public void TryAssignOperatorToTicket(int ticketId)
{
    using var transaction = _db.BeginTransaction();

    // Блокируем операторов с количеством тикетов меньше 5
    var availableOperators = _db.Query<Operator>(@"
        SELECT o.Id, COUNT(ot.TicketId) as TicketsCount
        FROM Operators o
        LEFT JOIN OperatorsTicket ot ON o.Id = ot.OperatorId
        GROUP BY o.Id
        HAVING COUNT(ot.TicketId) < 5
        ORDER BY TicketsCount
        FOR UPDATE
    ");

    var availableOperator = availableOperators.FirstOrDefault();

    if (availableOperator != null)
        _db.AssignTicketToOperator(ticketId, availableOperator.Id);
    else
        _db.AssignTicketToOperator(ticketId, null);

    transaction.Commit();
}

Такой подход гарантирует, что в момент выбора операторов они будут заблокированы для других транзакций, что предотвращает превышение лимита.

Практика в реальном времени

Подготовьтесь к следующему собеседованию

Interview Boost учитывает вакансию, резюме и технологии и помогает сформулировать ответ прямо во время интервью.

Начать подготовку