Partager via


Diagnostiquer et résoudre les problèmes de requêtes lentes dans le kit de développement logiciel (SDK) .NET Azure Cosmos DB

S’APPLIQUE À : NoSQL

Dans Azure Cosmos DB, vous remarquerez peut-être des requêtes lentes. Les retards peuvent avoir de multiples raisons, telles que la limitation des requêtes ou la façon dont votre application est conçue. Cet article explique les différentes causes racines du problème.

Taux de requêtes trop élevé

La limitation des requêtes est la cause la plus courante pour les requêtes lentes. Azure Cosmos DB limite les requêtes si elles dépassent les unités de requêtes allouées à la base de données ou au conteneur. Le kit de développement logiciel (SDK) intègre une logique permettant de retenter ces requêtes. L’article de résolution des problèmes Taux de requêtes trop élevé explique comment vérifier si les requêtes ont été limitées. L’article explique également comment mettre à l’échelle votre compte afin d’éviter ces problèmes à l’avenir.

Conception des applications

Lorsque vous concevez votre application, suivez les meilleures pratiques du kit de développement logiciel (SDK) .NET pour obtenir de meilleures performances. Si votre application ne suit pas les meilleures pratiques du kit de développement logiciel (SDK), vous obtiendrez des requêtes lentes ou défaillantes.

Tenez compte des éléments suivants lors du développement de votre application :

Opérations sur les métadonnées

Si vous devez vérifier qu’une base de données ou un conteneur existe, ne le faites pas en appelant Create...IfNotExistsAsync ou Read...Async avant d’effectuer une opération d’élément. La validation ne doit être effectuée qu’au démarrage de l’application lorsque cela est nécessaire, si vous pensez qu’elle doit être supprimée. Ces opérations de métadonnées génèrent une latence supplémentaire, n’ont pas de contrat de niveau de service (SLA) et ont leurs propres limitations distinctes. Elles ne sont pas mises à l’échelle comme des opérations de données.

Lenteur des requêtes en mode bloc

Le mode bloc est un mode optimisé pour le débit destiné aux opérations de volume de données élevé, et non un mode optimisé pour la latence. Il est destiné à saturer le débit disponible. Si vous rencontrez des requêtes lentes lors de l’utilisation du mode en bloc, assurez-vous que :

  • Votre application est compilée dans la configuration Mise en production.
  • Vous ne mesurez pas la latence pendant le débogage de l’application (aucun débogueur attaché).
  • Le volume d’opérations est élevé, n’utilisez pas de bloc s’il y a moins de 1 000 opérations. La configuration de votre débit détermine le nombre d’opérations par seconde que vous pouvez traiter, et votre objectif avec le mode bloc serait d’en utiliser le plus possible.
  • Surveiller le conteneur pour les scénarios de limitation de requêtes. Si le conteneur est fortement limité, cela signifie que le volume de données est plus important que votre débit provisionné, vous devez effectuer un scale-up du conteneur ou réduire le volume de données (peut-être créer des lots de données plus petits à la fois).
  • Vous utilisez correctement le modèle async/await pour traiter toutes les tâches simultanées et vous ne bloquer pas des opérations asynchrones.

Capturer les diagnostics

Toutes les réponses du kit de développement logiciel (SDK) y compris CosmosException possède une propriété Diagnostics. Cette propriété enregistre toutes les informations relatives à la requête unique, notamment en cas de nouvelles tentatives ou d’échecs temporaires.

Les diagnostics sont retournés sous forme de chaîne. La chaîne change avec chaque version, car elle est améliorée pour résoudre les différents scénarios. Avec chaque version du SDK, le formatage de la chaîne comporte des changements cassants. N’analysez pas la chaîne pour éviter les changements cassants. L’exemple de code suivant montre comment lire les journaux de diagnostic à l’aide du kit de développement logiciel (SDK) .NET :

try
{
    ItemResponse<Book> response = await this.Container.CreateItemAsync<Book>(item: testItem);
    if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan)
    {
        // Log the response.Diagnostics.ToString() and add any additional info necessary to correlate to other logs 
    }
}
catch (CosmosException cosmosException)
{
    // Log the full exception including the stack trace with: cosmosException.ToString()
    
    // The Diagnostics can be logged separately if required with: cosmosException.Diagnostics.ToString()
}

// When using Stream APIs
ResponseMessage response = await this.Container.CreateItemStreamAsync(partitionKey, stream);
if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan || !response.IsSuccessStatusCode)
{
    // Log the diagnostics and add any additional info necessary to correlate to other logs with: response.Diagnostics.ToString()
}

Diagnostics dans la version 3.19 et versions ultérieures

La structure JSON présente des changements cassants à chaque version du kit de développement logiciel (SDK). Cela rend l’analyse risquée. Le fichier JSON représente une structure arborescente de la requête qui passe par le kit de développement logiciel (SDK). Les sections suivantes couvrent quelques points clés à examiner.

Historique de processeur

Une utilisation élevée du processeur est la cause la plus courante de requêtes lentes. Pour une latence optimale, l'utilisation du processeur doit être d'environ 40 %. Utilisez 10 secondes comme intervalle pour superviser l'utilisation maximale (non moyenne) du processeur. Les pics d’utilisation du processeur sont plus courants avec les requêtes entre partitions où les requêtes peuvent établir plusieurs connexions pour une seule requête.

Les délais d’attente incluent des diagnostics, qui contiennent les éléments suivants, par exemple :

"systemHistory": [
{
"dateUtc": "2021-11-17T23:38:28.3115496Z",
"cpu": 16.731,
"memory": 9024120.000,
"threadInfo": {
"isThreadStarving": "False",
....
}

},
{
"dateUtc": "2021-11-17T23:38:38.3115496Z",
"cpu": 16.731,
"memory": 9024120.000,
"threadInfo": {
"isThreadStarving": "False",
....
}

},
...
]
  • Si les valeurs cpu dépassent 70 %, le délai d’attente est probablement provoqué par un épuisement du processeur. Dans ce cas, la solution consiste à examiner la source de l’utilisation élevée du processeur et à la réduire, ou à augmenter les ressources de la machine.
  • Si les nœuds threadInfo/isThreadStarving ont des valeurs True, la cause en est le manque de threads. Dans ce cas, la solution consiste à étudier la ou les sources de la privation de thread (threads potentiellement verrouillés) ou à mettre les ressources de la ou des machines à une échelle supérieure.
  • Si le délai dateUtc entre les mesures n’est pas d’environ 10 secondes, cela indique également de la contention dans le pool de threads. Le processeur est mesuré en tant que tâche indépendante mise en file d’attente dans le pool de threads toutes les 10 secondes. Si le délai entre les mesures est plus long, cela indique que les tâches asynchrones ne peuvent pas être traitées en temps voulu. Le scénario le plus courant est lorsque votre code d’application bloque les appels sur le code asynchrone.

Solution

L'application cliente qui utilise le SDK doit faire l'objet d'un scale-up ou d'un scale-out.

HttpResponseStats

HttpResponseStats sont des requêtes envoyées à la passerelle. Même en mode direct, le kit de développement logiciel (SDK) obtient toutes les informations des métadonnées à partir de la passerelle.

Si la requête est lente, vérifiez d’abord qu’aucune des suggestions précédentes ne produit les résultats souhaités. Si elle est toujours lente, les différents modèles pointent vers différents problèmes. Le tableau suivant fournit plus de détails.

Nombre de demandes Scénario Description
Unique pour tout Délai d’attente de la demande ou HttpRequestExceptions Pointe vers l’épuisement du port SNAT ou un manque de ressources sur la machine pour traiter la requête à temps.
Pourcentage unique ou faible (le contrat de niveau de service est respecté) Tous Un faible ou unique pourcentage de requêtes lentes peut être causé par plusieurs problèmes transitoires différents et doit être envisagé.
Tous Tous Pointe vers un problème lié à l’infrastructure ou à la mise en réseau.
Contrat SLA non respecté Aucune modification de l’application et contrat de niveau de service annulé. Pointe vers un problème lié au service Azure Cosmos DB.
"HttpResponseStats": [
    {
        "StartTimeUTC": "2021-06-15T13:53:09.7961124Z",
        "EndTimeUTC": "2021-06-15T13:53:09.7961127Z",
        "RequestUri": "https://127.0.0.1:8081/dbs/347a8e44-a550-493e-88ee-29a19c070ecc/colls/4f72e752-fa91-455a-82c1-bf253a5a3c4e",
        "ResourceType": "Collection",
        "HttpMethod": "GET",
        "ActivityId": "e16e98ec-f2e3-430c-b9e9-7d99e58a4f72",
        "StatusCode": "OK"
    }
]

StoreResult

StoreResult représente une requête unique à Azure Cosmos DB à l’aide du mode direct avec le protocole TCP.

Si elle est toujours lente, les différents modèles pointent vers différents problèmes. Le tableau suivant fournit plus de détails.

Nombre de demandes Scénario Description
Unique pour tout StoreResult contient TransportException Pointe vers l’épuisement du port SNAT ou un manque de ressources sur la machine pour traiter la requête à temps.
Pourcentage unique ou faible (le contrat de niveau de service est respecté) Tous Un faible ou unique pourcentage de requêtes lentes peut être causé par plusieurs problèmes transitoires différents et doit être envisagé.
Tous Tous Un problème lié à l’infrastructure ou à la mise en réseau.
Contrat SLA non respecté Les requêtes contiennent plusieurs codes d’erreur de défaillance tels que 410 Pointe vers un problème concernant le service Azure Cosmos DB ou l’ordinateur client.
Contrat SLA non respecté StorePhysicalAddress sont les mêmes, sans code d’état de défaillance. Problème potentiel avec Azure Cosmos DB.
Contrat SLA non respecté StorePhysicalAddress possède le même ID de partition mais des ID de réplica différents sans code d’état de défaillance. Problème potentiel avec Azure Cosmos DB.
Contrat SLA non respecté StorePhysicalAddress est aléatoire, sans code d’état de défaillance. Pointe vers un problème avec la machine.

Pour les résultats de plusieurs magasins pour une requête unique, tenez compte des éléments suivants :

  • La cohérence forte et de l’obsolescence limitée possède toujours au moins deux résultats de magasin.
  • Vérifiez le code d’état de chaque StoreResult. Le kit de développement logiciel (SDK) réessaie automatiquement sur plusieurs défaillances transitoires différentes. Le kit de développement logiciel (SDK) est constamment amélioré pour couvrir davantage de scénarios.

RequestTimeline

Affichez l’heure des différentes étapes de l’envoi et de la réception d’une requête dans la couche transport.

  • ChannelAcquisitionStarted : heure à laquelle obtenir ou créer une nouvelle connexion. Les connexions peuvent être créées pour de nombreuses raisons, notamment : La connexion précédente a été fermée en raison d’une inactivité au moyen de CosmosClientOptions.IdleTcpConnectionTimeout, le volume de requêtes simultanées dépasse le cosmosClientOptions.MaxRequestsPerTcpConnection, la connexion a été fermée en raison d’une erreur réseau, ou l’application ne respecte pas le modèle Singleton et de nouvelles instances sont constamment créées. Une fois une connexion établie, elle est réutilisée pour les requêtes suivantes. Cela ne doit donc pas avoir d’impact sur la latence P99, sauf si les problèmes mentionnés précédemment surviennent.
  • Pipelined : Le temps passé à écrire la requête dans le socket TCP. Les requêtes peuvent uniquement être écrites sur les sockets TCP un par un. Une valeur élevée indique un goulot d’étranglement sur les sockets TCP, ce qui est généralement associé à des threads verrouillés par le code de l’application ou à des requêtes de grande taille.
  • Transit time : Le temps passé sur le réseau après que la requête a été écrite sur le socket TCP. Comparez ce nombre à BELatencyInMs. Si BELatencyInMs a une valeur faible, le temps a été passé sur le réseau et non sur le service Azure Cosmos DB. Si la requête a échoué avec un délai d’attente, cela indique la durée pendant laquelle le client a attendu sans obtenir de réponse, et que la source en est la latence du réseau.
  • Received : Le délai écoulé entre la réception de la réponse et son traitement par le Kit de développement logiciel (SDK). Une valeur élevée est généralement provoquée par un manque de threads ou des threads verrouillés.

ServiceEndpointStatistics

Informations sur un serveur principal particulier. Le Kit de développement logiciel (SDK) peut ouvrir plusieurs connexions à un seul serveur principal en fonction du nombre de demandes en attente et de MaxConcurrentRequestsPerConnection.

  • inflightRequests Nombre de demandes en attente adressées à un serveur principal (éventuellement à partir de partitions différentes). Un nombre élevé peut entraîner une augmentation du trafic et des latences.
  • openConnections Nombre total de connexions ouvertes à un seul serveur principal. Cela peut être utile pour indiquer un épuisement des ports SNAT si ce nombre est très élevé.

ConnectionStatistics

Informations sur la connexion particulière (nouvelle ou ancienne) à laquelle la demande est attribuée.

  • waitforConnectionInit : La demande actuelle attendait la fin de l’initialisation de la nouvelle connexion. Cela entraînera des latences plus élevées.
  • callsPendingReceive : Nombre d’appels en attente de réception avant l’envoi de cet appel. Un nombre élevé peut indiquer qu’il y avait beaucoup d’appels avant cet appel, et que ce dernier peut entraîner des latences plus élevées. Si ce nombre est élevé, il indique un problème de blocage en tête de ligne qui peut être dû à une autre demande, comme une requête ou une opération de flux dont le traitement prend beaucoup de temps. Essayez de réduire la valeur de cosmosClientOptions.MaxRequestsPerTcpConnection pour augmenter le nombre de canaux.
  • LastSentTime : Heure de la dernière requête envoyée à ce serveur. Cette valeur avec celle de LastReceivedTime peut être utilisée pour voir des problèmes de connectivité ou de point de terminaison. Par exemple, s’il y a beaucoup d’expirations de délai de réception, la valeur de l’heure d’envoi sera beaucoup plus élevée que celle de l’heure de réception.
  • lastReceive : Heure de la dernière demande reçue de ce serveur
  • lastSendAttempt : Heure de la dernière tentative d’envoi

Tailles de demande et de réponse

  • requestSizeInBytes : Taille totale de la demande envoyée à Azure Cosmos DB
  • responseMetadataSizeInBytes : Taille des en-têtes retournés à partir d’Azure Cosmos DB
  • responseBodySizeInBytes : Taille du contenu retourné à partir d’Azure Cosmos DB
"StoreResult": {
    "ActivityId": "bab6ade1-b8de-407f-b89d-fa2138a91284",
    "StatusCode": "Ok",
    "SubStatusCode": "Unknown",
    "LSN": 453362,
    "PartitionKeyRangeId": "1",
    "GlobalCommittedLSN": 0,
    "ItemLSN": 453358,
    "UsingLocalLSN": true,
    "QuorumAckedLSN": -1,
    "SessionToken": "-1#453362",
    "CurrentWriteQuorum": -1,
    "CurrentReplicaSetSize": -1,
    "NumberOfReadRegions": 0,
    "IsValid": true,
    "StorePhysicalAddress": "rntbd://127.0.0.1:10253/apps/DocDbApp/services/DocDbServer92/partitions/a4cb49a8-38c8-11e6-8106-8cdcd42c33be/replicas/1s/",
    "RequestCharge": 1,
    "RetryAfterInMs": null,
    "BELatencyInMs": "0.304",
    "transportRequestTimeline": {
        "requestTimeline": [
            {
                "event": "Created",
                "startTimeUtc": "2022-05-25T12:03:36.3081190Z",
                "durationInMs": 0.0024
            },
            {
                "event": "ChannelAcquisitionStarted",
                "startTimeUtc": "2022-05-25T12:03:36.3081214Z",
                "durationInMs": 0.0132
            },
            {
                "event": "Pipelined",
                "startTimeUtc": "2022-05-25T12:03:36.3081346Z",
                "durationInMs": 0.0865
            },
            {
                "event": "Transit Time",
                "startTimeUtc": "2022-05-25T12:03:36.3082211Z",
                "durationInMs": 1.3324
            },
            {
                "event": "Received",
                "startTimeUtc": "2022-05-25T12:03:36.3095535Z",
                "durationInMs": 12.6128
            },
            {
                "event": "Completed",
                "startTimeUtc": "2022-05-25T12:03:36.8621663Z",
                "durationInMs": 0
            }
        ],
        "serviceEndpointStats": {
            "inflightRequests": 1,
            "openConnections": 1
        },
        "connectionStats": {
            "waitforConnectionInit": "False",
            "callsPendingReceive": 0,
            "lastSendAttempt": "2022-05-25T12:03:34.0222760Z",
            "lastSend": "2022-05-25T12:03:34.0223280Z",
            "lastReceive": "2022-05-25T12:03:34.0257728Z"
        },
        "requestSizeInBytes": 447,
        "responseMetadataSizeInBytes": 438,
        "responseBodySizeInBytes": 604
    },
    "TransportException": null
}

Le taux d'échec n'est pas conforme au contrat de niveau de service Azure Cosmos DB

Contacter le support Azure.

Étapes suivantes