Czy podczas otwierania MySqlConnection nie zgłoszono wyjątku?

14

Zaczynam od asynchronizacji i zadań, a mój kod przestał przetwarzać. Zdarza się to, gdy mam przychodzący pakiet sieciowy i próbuję komunikować się z bazą danych wewnątrz modułu obsługi pakietów.

public class ClientConnectedPacket : IClientPacket
{
    private readonly EntityFactory _entityFactory;

    public ClientConnectedPacket(EntityFactory entityFactory)
    {
        _entityFactory= entityFactory;
    }

    public async Task Handle(NetworkClient client, ClientPacketReader reader)
    {
        client.Entity = await _entityFactory.CreateInstanceAsync( reader.GetValueByKey("unique_device_id"));

        // this Console.WriteLine never gets reached
        Console.WriteLine($"Client [{reader.GetValueByKey("unique_device_id")}] has connected");
    }
}

Metoda Handle jest wywoływana z zadania asynchronicznego

if (_packetRepository.TryGetPacketByName(packetName, out var packet))
{
    await packet.Handle(this, new ClientPacketReader(packetName, packetData));
}
else
{
    Console.WriteLine("Unknown packet: " + packetName);
}

Oto metoda, która moim zdaniem powoduje problem

public async Task<Entity> CreateInstanceAsync(string uniqueId)
{
    await using (var dbConnection = _databaseProvider.GetConnection())
    { 
        dbConnection.SetQuery("SELECT COUNT(NULL) FROM `entities` WHERE `unique_id` = @uniqueId");
        dbConnection.AddParameter("uniqueId", uniqueId);

        var row = await dbConnection.ExecuteRowAsync();

        if (row != null)
        {
            return new Entity(uniqueId, false);
        }
    }

    return new Entity(uniqueId,true);
}

Metoda GetConnection programu DatabaseProvider:

public DatabaseConnection GetConnection()
{
    var connection = new MySqlConnection(_connectionString);
    var command = connection.CreateCommand();

    return new DatabaseConnection(_logFactory.GetLogger(), connection, command);
}

Konstruktor DatabaseConnection:

public DatabaseConnection(ILogger logger, MySqlConnection connection, MySqlCommand command)
{
    _logger = logger;

    _connection = connection;    
    _command = command;

    _connection.Open();
}

Kiedy komentuję tę linię, dociera do Console.WriteLine

_connection.Open();
AAA
źródło
3
Pamiętaj, że Oracle MySQL Connector / NET (alias MySql.Data) tak naprawdę nie implementuje żadnych operacji asynchronicznych: stackoverflow.com/a/40065501/23633 Jeśli chcesz używać operacji asynchronicznych z MySQL, powinieneś przełączyć się na nuget.org/ Package / MySqlConnector
Bradley Grainger
Wygląda na to, że masz zakleszczenie w kodzie. Poleciłbym włamanie się do debuggera Visual Studio, gdy to nastąpi, i otwarcie okna stosów równoległych. Jeśli to pokazuje jeden lub więcej wątków ze stosami wywołań, które blokują Zadanie (lub trochę prymitywnej synchronizacji), to mamy nadzieję, że będziesz w stanie powiedzieć, co się dzieje (lub edytować więcej szczegółów w pytaniu, aby nam pomóc). Jeśli żaden z wątków nie jest zablokowany w zadaniu, prawdopodobnie masz zadanie, które nigdy się nie kończy i nigdy nie aktywuje kodu, który awaitna nim działa. O wiele trudniej jest zdiagnozować.
Bradley Grainger
Czy możesz dodać implementację DatabaseConnection.DisposeAsync?
eisenpony
Jeśli zaczekasz na powiedzmy 5 minut, czy w końcu spowoduje to wyjątek? Connection.opennie wraca podczas próby połączenia, ale w końcu zrezygnuje po upływie limitu czasu. Lub możesz zmienić wartość limitu czasu obiektu połączenia na mniejszą liczbę większą niż 0 i sprawdzić, czy istnieje wyjątek.
weichch

Odpowiedzi:

1

Uruchomiłem projekt POC spinning 100 równoległych zadań zarówno z MySql.Data 8.0.19 i MySqlConnector 0.63.2 w aplikacji konsoli .NET Core 3.1. Tworzę, otwieram i rozmieszczam połączenie w kontekście każdego zadania. Obaj dostawcy biegną do końca bez błędów.

Specyfika polega na tym, że zapytania MySql.Data działają synchronicznie, chociaż biblioteka zapewnia podpis metod asynchronicznych, np. ExecuteReaderAsync () lub ExecuteScalarAsync (), podczas gdy MySqlConnector działa naprawdę asynchronicznie .

Możesz napotkać:

  • sytuacja zakleszczenia nie jest ściśle związana z dostawcą mysql
  • niewłaściwa obsługa wyjątków w zadaniach (możesz sprawdzić wyjątek agregacji powiązany z zadaniem, a także monitorować dzienniki db mysql)
  • wykonanie jest nadal blokowane (nie zwraca wyniku), jeśli założono, że nie działa, jeśli uruchomiono dużą liczbę równoległych zadań z MySql.Data, ponieważ wykonuje się synchronicznie
kalitsov
źródło
0

Wielowątkowość z MySQL musi korzystać z niezależnych połączeń. Biorąc to pod uwagę, wielowątkowość nie jest pytaniem MySQL, ale problemem dla języka klienta, C # w twoim pytaniu.

Oznacza to, że buduj wątki bez względu na MySQL, a następnie utwórz połączenie w każdym wątku, który musi wykonywać zapytania. To będzie na twoich barkach, jeśli będziesz musiał przesyłać dane między wątkami.

Zazwyczaj uważam, że optymalizacja zapytań eliminuje pokusę wielowątkowości moich aplikacji.

Rick James
źródło