Aucune exception n'est levée lors de l'ouverture de MySqlConnection?

14

Je commence tout juste avec async et Task et mon code a cessé de traiter. Cela se produit lorsque j'ai un paquet réseau entrant et que j'essaie de communiquer avec la base de données à l'intérieur du gestionnaire de paquets.

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");
    }
}

La méthode Handle est appelée à partir d'une tâche asynchrone

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

Voici la méthode qui, je pense, est à l'origine du problème

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);
}

Méthode GetConnection de DatabaseProvider:

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

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

Constructeur de DatabaseConnection:

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

    _connection = connection;    
    _command = command;

    _connection.Open();
}

Lorsque je commente cette ligne, elle atteint le Console.WriteLine

_connection.Open();
AAA
la source
3
Sachez que le connecteur / NET d'Oracle MySQL (alias MySql.Data) n'implémente pas réellement d'opérations asynchrones: stackoverflow.com/a/40065501/23633 Si vous souhaitez utiliser des opérations asynchrones avec MySQL, vous devez passer à nuget.org/ packages / MySqlConnector
Bradley Grainger
Il semble que vous ayez un blocage dans votre code. Je recommanderais de pénétrer dans le débogueur Visual Studio lorsque cela se produit et d'ouvrir la fenêtre Parallel Stacks. Si cela montre un ou plusieurs threads avec des piles d'appels qui bloquent une tâche (ou une primitive de synchronisation), nous espérons que vous pourrez dire ce qui ne va pas (ou modifier plus de détails dans la question pour nous aider). Si aucun des threads n'est bloqué sur une tâche, vous avez probablement une tâche qui n'est jamais terminée et qui n'active jamais le code qui awaits'y trouve. C'est beaucoup plus difficile à diagnostiquer.
Bradley Grainger
Pouvez-vous ajouter l'implémentation de DatabaseConnection.DisposeAsync?
eisenpony
Si vous attendez 5 minutes disons-nous finalement une exception? Connection.openne revient pas pendant qu'il essaie de se connecter, mais abandonnera finalement après un délai d'attente défini. Ou vous pouvez changer la valeur du délai d'expiration sur l'objet de connexion en un nombre inférieur à 0 et voir s'il y a exception.
weichch

Réponses:

1

J'ai exécuté un projet POC en faisant tourner 100 tâches parallèles avec MySql.Data 8.0.19 et MySqlConnector 0.63.2 sur l'application console .NET Core 3.1. Je crée, ouvre et supprime la connexion dans le contexte de chaque tâche. Les deux fournisseurs s'exécutent sans erreur.

Les spécificités sont que les requêtes MySql.Data s'exécutent de manière synchrone bien que la bibliothèque fournisse la signature des méthodes asynchrones, par exemple ExecuteReaderAsync () ou ExecuteScalarAsync (), tandis que MySqlConnector s'exécute de manière vraiment asynchrone .

Vous pourriez rencontrer:

  • une situation de blocage non spécifiquement liée au fournisseur mysql
  • ne gère pas correctement les exceptions dans vos tâches (vous pouvez inspecter l'exception d'agrégation associée à la tâche et surveiller également les journaux de base de données mysql)
  • votre exécution est toujours bloquée (ne renvoie pas de résultat) lorsque vous supposez que cela ne fonctionne pas, si vous exécutez un grand nombre de tâches parallèles avec MySql.Data lors de son exécution synchrone
Kalitsov
la source
0

Le multi-threading avec MySQL doit utiliser des connexions indépendantes. Étant donné que le multithreading n'est pas une question MySQL mais un problème pour le langage client, C # dans votre question.

Autrement dit, créez vos threads sans tenir compte de MySQL, puis créez une connexion dans chaque thread qui doit effectuer des requêtes. Ce sera sur vos épaules si vous avez besoin de passer des données entre les threads.

Je trouve généralement que l'optimisation des requêtes élimine la tentation de multi-thread mes applications.

Rick James
la source