< Summary

Information
Class: Services.HangfireService.HangfireQueueRepair
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/HangfireService/HangfireQueueRepair.cs
Line coverage
1%
Covered lines: 1
Uncovered lines: 53
Coverable lines: 54
Total lines: 94
Line coverage: 1.8%
Branch coverage
0%
Covered branches: 0
Total branches: 8
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
IsLockWaitTimeout(...)100%11100%
RepairDefaultQueuesAsync()0%2040%
HangfireTablesExistAsync()0%620%
ExecuteNonQueryAsync()0%620%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/HangfireService/HangfireQueueRepair.cs

#LineLine coverage
 1using MySqlConnector;
 2
 3namespace Services.HangfireService;
 4
 5/// <summary>
 6/// Перенос зависших задач из очереди default (воркеры её не слушают).
 7/// Выполняется в БД Hangfire (ST2_Hangfire), не в ST2.
 8/// </summary>
 9public static class HangfireQueueRepair
 10{
 11    /// <summary>
 12    /// Блокировка таблиц внутреннего хранилища Hangfire кратковременна: штатный планировщик
 13    /// повторяет свои операции, а миграция очереди повторится при следующем старте.
 14    /// </summary>
 15    public static bool IsLockWaitTimeout(Exception exception) =>
 216        exception.ToString().Contains("Lock wait timeout exceeded", StringComparison.OrdinalIgnoreCase);
 17
 18    public static async Task RepairDefaultQueuesAsync(
 19        HangfireStorageConnection hangfireStorage,
 20        CancellationToken cancellationToken = default)
 21    {
 022        await using var connection = new MySqlConnection(hangfireStorage.ConnectionString);
 023        await connection.OpenAsync(cancellationToken);
 24
 025        if (!await HangfireTablesExistAsync(connection, cancellationToken))
 26        {
 27            return;
 28        }
 29
 030        await ExecuteNonQueryAsync(
 031            connection,
 032            """
 033            UPDATE HangfireHash
 034            SET Value = @queue
 035            WHERE Field = 'Queue'
 036              AND Value = 'default'
 037            """,
 038            new MySqlParameter("@queue", HangfireQueueNames.Main),
 039            cancellationToken);
 40
 041        await ExecuteNonQueryAsync(
 042            connection,
 043            """
 044            UPDATE HangfireJobQueue q
 045            INNER JOIN HangfireJob j ON j.Id = q.JobId
 046            SET q.Queue = @queue
 047            WHERE q.Queue = 'default'
 048              AND (
 049                  j.InvocationData LIKE '%IOzonWebhookService%'
 050                  OR j.InvocationData LIKE '%OzonWebhookService%'
 051              )
 052            """,
 053            new MySqlParameter("@queue", HangfireQueueNames.OzonWebhook),
 054            cancellationToken);
 55
 056        await ExecuteNonQueryAsync(
 057            connection,
 058            """
 059            UPDATE HangfireJobQueue q
 060            INNER JOIN HangfireJob j ON j.Id = q.JobId
 061            SET q.Queue = @queue
 062            WHERE q.Queue = 'default'
 063            """,
 064            new MySqlParameter("@queue", HangfireQueueNames.Main),
 065            cancellationToken);
 066    }
 67
 68    private static async Task<bool> HangfireTablesExistAsync(
 69        MySqlConnection connection,
 70        CancellationToken cancellationToken)
 71    {
 072        await using var cmd = connection.CreateCommand();
 073        cmd.CommandText = """
 074            SELECT COUNT(*)
 075            FROM information_schema.tables
 076            WHERE table_schema = DATABASE()
 077              AND table_name = 'HangfireHash'
 078            """;
 079        var count = Convert.ToInt32(await cmd.ExecuteScalarAsync(cancellationToken));
 080        return count > 0;
 081    }
 82
 83    private static async Task ExecuteNonQueryAsync(
 84        MySqlConnection connection,
 85        string sql,
 86        MySqlParameter parameter,
 87        CancellationToken cancellationToken)
 88    {
 089        await using var cmd = connection.CreateCommand();
 090        cmd.CommandText = sql;
 091        cmd.Parameters.Add(parameter);
 092        await cmd.ExecuteNonQueryAsync(cancellationToken);
 093    }
 94}