

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Bonnes pratiques : optimisation de Neptune
<a name="best-practices"></a>

Voici quelques recommandations d'ordre général pour utiliser Amazon Neptune. Utilisez ces informations comme référence pour trouver rapidement des recommandations sur l'utilisation d'Amazon Neptune et l'optimisation des performances.

**Contents**
+ [Directives opérationnelles de base Amazon Neptune](best-practices-general-basic.md)
  + [Bonnes pratiques de sécurité pour Amazon Neptune](best-practices-general-security.md)
  + [Éviter différentes classes d'instances dans un cluster](best-practices-general-basic.md#best-practices-loader-heterogeneous-instances)
  + [Éviter les redémarrages répétés pendant le chargement en bloc](best-practices-general-basic.md#best-practices-loader-repeated-restarts)
  + [Activation de l'index OSGP si le nombre de prédicats est élevé](best-practices-general-basic.md#best-practices-general-predicates)
  + [Éviter les transactions de longue durée dans la mesure du possible](best-practices-general-basic.md#best-practices-general-long-running-transactions)
  + [Bonnes pratiques pour l'utilisation des métriques Neptune](best-practices-general-metrics.md)
  + [Bonnes pratiques pour le réglage des requêtes Neptune](best-practices-general-basic.md#best-practices-general-tuning)
  + [Équilibrage de charge entre les réplicas en lecture](best-practices-general-basic.md#best-practices-general-loadbalance)
  + [Chargement plus rapide à l'aide d'une instance temporaire de plus grande taille](best-practices-general-basic.md#best-practices-loader-tempinstance)
  + [Redimensionnement de l'instance d'enregistreur en basculant vers un réplica en lecture](best-practices-general-basic.md#best-practices-resize-instance)
  + [Nouvelle tentative de chargement après une erreur d'interruption de tâche de lecture anticipée des données](best-practices-general-basic.md#load-api-reference-status-interrupted)
+ [Bonnes pratiques d'ordre général pour l'utilisation de Gremlin avec Neptune](best-practices-gremlin.md)
  + [Configuration Heartbeat pour Neptune Serverless](best-practices-gremlin-heartbeat-serverless.md)
  + [Structurer les requêtes upsert pour tirer parti du moteur DFE](best-practices-gremlin.md#best-practices-gremlin-upserts)
  + [Testez le code Gremlin dans le contexte dans lequel vous allez le déployer](best-practices-gremlin-console-glv-differences.md)
  + [Création d'écritures Gremlin multithreads efficaces](best-practices-gremlin-multithreaded-writes.md)
  + [Élagage des enregistrements avec la propriété de date/heure de création](best-practices-gremlin-prune.md)
  + [Utilisation de la méthode `datetime( )` pour les données temporelles Groovy](best-practices-gremlin-datetime.md)
  + [Utilisation de la date et de l'heure natives pour les données temporelles GLV](best-practices-gremlin-datetime-glv.md)
+ [Bonnes pratiques pour l'utilisation du client Java Gremlin avec Neptune](best-practices-gremlin-java-client.md)
  + [Réutilisation de l'objet client dans plusieurs threads](best-practices-gremlin-java-reuse.md)
  + [Création d'objets client Java Gremlin distincts pour les points de terminaison en lecture et en écriture](best-practices-gremlin-java-separate.md)
  + [Ajout de plusieurs points de terminaison de réplica en lecture à un groupe de connexions Java Gremlin](best-practices-gremlin-java-multiple.md)
  + [Fermeture du client pour éviter de limiter les connexions](best-practices-gremlin-java-close-connections.md)
  + [Création d'une connexion après un basculement](best-practices-gremlin-java-new-connection.md)
  + [Définition de `maxInProcessPerConnection` et `maxSimultaneousUsagePerConnection` sur la même valeur](best-practices-gremlin-java-maxes.md)
  + [Envoi de requêtes au serveur sous la forme de bytecode et non de chaînes](best-practices-gremlin-java-bytecode.md)
  + [Consommez toujours complètement l'itérateur ResultSet ou l'itérateur renvoyé par une requête](best-practices-gremlin-java-resultset.md)
  + [Ajout en bloc de sommets et d'arêtes dans des lots](best-practices-gremlin-java-batch-add.md)
  + [Désactivation de la mise en cache du DNS dans la machine virtuelle Java](best-practices-gremlin-java-disable-dns-caching.md)
  + [Définition facultative de délais d'expiration au niveau de chaque requête](best-practices-gremlin-java-per-query-timeout.md)
  + [Résolution des problèmes de `java.util.concurrent.TimeoutException`](best-practices-gremlin-java-exceptions-TimeoutException.md)
+ [Bonnes pratiques Neptune avec openCypher et Bolt](best-practices-opencypher.md)
  + [Création d'une connexion après un basculement](best-practices-opencypher.md#best-practices-opencypher-renew-connection)
  + [Gestion des connexions pour les applications de longue durée](best-practices-opencypher.md#best-practices-opencypher-long-connections)
  + [Gestion des connexions pour AWS Lambda](best-practices-opencypher.md#best-practices-opencypher-lambda-connections)
  + [Préférer les arêtes dirigées aux arêtes bidirectionnelles dans les requêtes](best-practices-opencypher-directed-edges.md)
  + [Neptune ne prend pas en charge plusieurs requêtes simultanées dans une transaction](best-practices-opencypher-multiple-queries.md)
  + [Fermer les objets Driver lorsque vous avez terminé](best-practices-opencypher-close-driver.md)
  + [Utilisation des modes de transaction explicites pour la lecture et l'écriture](best-practices-opencypher-use-explicit-txs.md)
    + [Transactions en lecture seule](best-practices-opencypher-use-explicit-txs.md#best-practices-opencypher-read-txs)
    + [Transactions de mutation](best-practices-opencypher-use-explicit-txs.md#best-practices-opencypher-mutation-txs)
  + [Logique des nouvelles tentatives pour les exceptions](best-practices-opencypher-retry-logic.md)
  + [Définissez plusieurs propriétés à la fois à l'aide d'une seule clause SET](best-practices-content-0.md)
    + [Utilisez la clause SET pour supprimer plusieurs propriétés à la fois](best-practices-content-0.md#best-practices-content-1)
  + [Utilisation des requêtes paramétrées](best-practices-content-2.md)
  + [Utilisez des cartes aplaties au lieu de cartes imbriquées dans la clause UNWIND](best-practices-content-3.md)
  + [Placez des nœuds plus restrictifs sur le côté gauche dans les expressions VLP (Variable-Length Path)](best-practices-content-4.md)
  + [Évitez les vérifications redondantes des étiquettes des nœuds en utilisant des noms de relations granulaires](best-practices-content-5.md)
  + [Spécifiez les étiquettes de bord dans la mesure du possible](best-practices-content-6.md)
  + [Évitez d'utiliser la clause WITH dans la mesure du possible](best-practices-content-7.md)
  + [Placez les filtres restrictifs le plus tôt possible dans la requête](best-practices-content-8.md)
  + [Vérifiez explicitement si les propriétés existent](best-practices-content-9.md)
  + [N'utilisez pas de chemin nommé (sauf si cela est obligatoire)](best-practices-content-10.md)
  + [Évitez COLLECT (DISTINCT ())](best-practices-content-11.md)
  + [Préférez la fonction de propriétés à la recherche de propriétés individuelle lors de la récupération de toutes les valeurs de propriété](best-practices-content-12.md)
  + [Effectuer des calculs statiques en dehors de la requête](best-practices-content-13.md)
  + [Entrées par lots utilisant UNWIND au lieu d'instructions individuelles](best-practices-content-14.md)
  + [Préférez utiliser la personnalisation IDs pour le nœud ou la relation](best-practices-content-15.md)
  + [Évitez de faire des calculs \$1id dans la requête](best-practices-content-16.md)
  + [Mise à jour/fusion de plusieurs nœuds](best-practices-merge-multiple-nodes.md)
+ [Bonnes pratiques Neptune avec l'utilisation de SPARQL](best-practices-sparql.md)
  + [Interrogation de tous les graphes par défaut](best-practices-sparql-query.md)
  + [Spécification d'un graphe nommé pour le chargement](best-practices-sparql-graph.md)
  + [Choisir entre FILTER, FILTER...IN et VALUES dans vos requêtes](best-practices-sparql-batch.md)

# Directives opérationnelles de base Amazon Neptune
<a name="best-practices-general-basic"></a>

Vous trouverez ci-dessous les directives opérationnelles de base que vous devez suivre lorsque vous utilisez Neptune.
+ Familiarisez-vous avec les instances de base de données Neptune afin de pouvoir les dimensionner de manière appropriée en fonction de vos exigences en matière de performances et de cas d'utilisation. Consultez [Clusters de bases de données et instances de base de données Amazon Neptune](feature-overview-db-clusters.md).
+ Surveillez votre utilisation de CPU et de la mémoire. Ceci vous permet de savoir quand migrer vers une classe d'instances de base de données avec une plus grande capacité de CPU ou de la mémoire pour atteindre les performances des requêtes dont vous avez besoin. Vous pouvez configurer Amazon CloudWatch pour qu'il vous avertisse lorsque les habitudes d'utilisation changent ou lorsque vous approchez de la capacité de votre déploiement. Ceci peut vous aider à maintenir les performances et la disponibilité du système. Consultez [Surveillance des instances](feature-overview-db-clusters.md#feature-overview-monitoring-instances) et [Surveillance de Neptune](monitoring.md) pour en savoir plus.

  Comme Neptune dispose de son propre gestionnaire de mémoire, il est normal d'observer une utilisation de mémoire relativement faible, et ce même en cas de forte utilisation de CPU. Le fait de rencontrer out-of-memory des exceptions lors de l'exécution de requêtes est le meilleur indicateur de la nécessité d'augmenter la mémoire disponible.
+ Activez les sauvegardes automatiques et définissez la fenêtre de sauvegarde pour qu'elles se produisent à un moment opportun.
+ Testez le basculement pour votre instance de base de données afin de connaître la durée du processus pour votre cas d'utilisation. Il permet également de veiller à ce que l'application qui accède à votre instance DB puisse automatiquement se connecter à la nouvelle instance DB suite au basculement.
+ Si possible, exécutez votre client et votre cluster Neptune dans la même région et le même VPC, car les connexions entre régions avec l'appairage de VPC peuvent entraîner des délais dans les temps de réponse des requêtes. Pour obtenir des réponses aux requêtes en moins de 10 millisecondes, il est nécessaire de conserver le client et le cluster Neptune dans la même région et le même VPC.
+ Lorsque vous créez une instance de réplica en lecture, elle doit être au moins aussi volumineuse que l'instance d'enregistreur principale. Cela permet de contrôler le retard de réplication et d'éviter les redémarrages du réplica. Consultez [Éviter différentes classes d'instances dans un cluster](#best-practices-loader-heterogeneous-instances). 
+ Avant de procéder à une mise à niveau vers une nouvelle version majeure du moteur, assurez-vous de tester votre application sur cette dernière. Pour ce faire, clonez le cluster de bases de données afin qu'il exécute la nouvelle version du moteur, puis testez votre application sur ce clone.
+ Pour faciliter les basculements, toutes les instances devraient idéalement avoir la même taille.

**Topics**
+ [Bonnes pratiques de sécurité pour Amazon Neptune](best-practices-general-security.md)
+ [Éviter différentes classes d'instances dans un cluster](#best-practices-loader-heterogeneous-instances)
+ [Éviter les redémarrages répétés pendant le chargement en bloc](#best-practices-loader-repeated-restarts)
+ [Activation de l'index OSGP si le nombre de prédicats est élevé](#best-practices-general-predicates)
+ [Éviter les transactions de longue durée dans la mesure du possible](#best-practices-general-long-running-transactions)
+ [Bonnes pratiques pour l'utilisation des métriques Neptune](best-practices-general-metrics.md)
+ [Bonnes pratiques pour le réglage des requêtes Neptune](#best-practices-general-tuning)
+ [Équilibrage de charge entre les réplicas en lecture](#best-practices-general-loadbalance)
+ [Chargement plus rapide à l'aide d'une instance temporaire de plus grande taille](#best-practices-loader-tempinstance)
+ [Redimensionnement de l'instance d'enregistreur en basculant vers un réplica en lecture](#best-practices-resize-instance)
+ [Nouvelle tentative de chargement après une erreur d'interruption de tâche de lecture anticipée des données](#load-api-reference-status-interrupted)

# Bonnes pratiques de sécurité pour Amazon Neptune
<a name="best-practices-general-security"></a>

Utilisez des comptes Gestion des identités et des accès AWS (IAM) pour contrôler l'accès aux actions de l'API Neptune. Contrôlez les actions qui créent, modifient ou suppriment les ressources Neptune (comme les instances de base de données, les groupes de sécurité, les groupes d'options ou les groupes de paramètres), et celles qui effectuent des tâches administratives courantes (comme la sauvegarde et la restauration d'instances de base de données).
+ Utilisez des informations d'identification temporaires plutôt que persistantes dans la mesure du possible.
+ Attribuez un compte IAM individuel à chaque personne qui gère les ressources Amazon Relational Database Service (Amazon RDS). N'utilisez jamais AWS d'utilisateurs root pour gérer les ressources Neptune. Créez un utilisateur IAM pour chaque personne, y compris vous-même.
+ Accordez à chaque utilisateur un ensemble minimum d'autorisations requises pour exécuter ses tâches.
+ Utilisez des groupes IAM pour gérer efficacement des autorisations pour plusieurs utilisateurs.
+ Effectuer une rotation régulière des informations d’identification IAM.

Pour plus d'informations sur l'utilisation d'IAM afin d'accéder aux ressources Neptune, consultez [Sécurisation de votre base de données Amazon Neptune](security.md). Pour obtenir des informations générales sur l'utilisation d'IAM, consultez [Gestion des identités et des accès AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/Welcome.html) et [Bonnes pratiques IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/IAMBestPractices.html) dans le *Guide de l'utilisateur IAM*.

## Éviter différentes classes d'instances dans un cluster
<a name="best-practices-loader-heterogeneous-instances"></a>

Lorsque votre cluster de bases de données contient des instances de différentes classes, des problèmes peuvent survenir au fil du temps. Le problème le plus courant est qu'une petite instance de lecteur peut entrer dans un cycle de redémarrages répétés en raison d'un retard de réplication. Si la configuration de la classe d'instances de base de données d'un nœud de lecture est plus faible que celle d'une instance de base de données d'enregistreur, le volume de modifications peut être trop vaste pour que le lecteur puisse tout gérer.

**Important**  
Pour éviter les redémarrages répétés provoqués par un retard de réplication, configurez le cluster de bases de données de manière à ce que toutes les instances aient la même classe (taille) d'instance.

Vous pouvez voir le décalage entre l'instance du rédacteur (l'instance principale) et les lecteurs de votre cluster de bases de données à l'aide de la `ClusterReplicaLag` métrique d'Amazon CloudWatch. La métrique `VolumeWriteIOPs` vous permet également de détecter les pics d'activité d'écriture du cluster susceptibles de provoquer un retard de réplication.

## Éviter les redémarrages répétés pendant le chargement en bloc
<a name="best-practices-loader-repeated-restarts"></a>

Si vous êtes confronté à un cycle de redémarrages répétés des réplicas en lecture en raison d'un retard de réplication lors d'un chargement en bloc, les réplicas ne parviendront probablement pas à suivre le rythme de l'enregistreur du cluster de bases de données.

Vous pouvez soit mettre à l'échelle les lecteurs pour qu'ils soient plus grands que l'enregistreur, soit les supprimer temporairement pendant le chargement en bloc, puis les recréer une fois le chargement terminé.

## Activation de l'index OSGP si le nombre de prédicats est élevé
<a name="best-practices-general-predicates"></a>

Si le modèle de données contient un grand nombre de prédicats distincts (plus d'un millier dans la plupart des cas), vous pouvez subir une baisse des performances et une augmentation des coûts d'exploitation.

Si tel est le cas, vous pouvez activer l'[index OSGP](feature-overview-storage-indexing.md#feature-overview-storage-indexing-osgp) pour améliorer les performances. Consultez [Index OSGP](features-lab-mode.md#features-lab-mode-features-osgp-index).

## Éviter les transactions de longue durée dans la mesure du possible
<a name="best-practices-general-long-running-transactions"></a>

Les transactions de longue durée, qu'elles soient en lecture seule ou en lecture-écriture, peuvent causer des problèmes inattendus tels que les suivants :

Une transaction de longue durée sur une instance de lecteur ou d'enregistreur avec des écritures simultanées peut entraîner une accumulation importante de versions de données différentes. Les temps de latence peuvent être plus élevés pour les requêtes de lecture qui filtrent une grande partie de leurs résultats.

Dans certains cas, les versions accumulées au fil des heures peuvent entraîner la limitation des nouvelles écritures.

Une transaction de lecture-écriture de longue durée impliquant de nombreuses écritures peut également causer des problèmes si l'instance redémarre. Si une instance redémarre à la suite d'un événement de maintenance ou d'un blocage, toutes les écritures non validées seront annulées. Ces opérations d'annulation s'exécutent généralement en arrière-plan et n'empêchent pas l'instance de se rétablir, mais toute nouvelle écriture entrant en conflit avec les opérations en cours d'annulation échouera.

Par exemple, si la même requête fait l'objet d'une nouvelle tentative après une interruption de la connexion lors de l'exécution précédente, elle pourra échouer au redémarrage de l'instance.

Le temps nécessaire aux opérations d'annulation est proportionnel à l'ampleur des modifications impliquées.

# Bonnes pratiques pour l'utilisation des métriques Neptune
<a name="best-practices-general-metrics"></a>

Pour identifier les problèmes de performances causés par des ressources insuffisantes et d'autres goulots d'étranglement courants, vous pouvez surveiller les métriques disponibles pour votre cluster de bases de données Neptune. 

Surveillez régulièrement les métriques de performances pour rassembler les données sur les valeurs moyennes, maximales et minimales pour différents intervalles de temps. Cela vous aide à déterminer quand les performances se dégradent. À l'aide de ces données, vous pouvez définir des CloudWatch alarmes Amazon pour des seuils métriques spécifiques afin d'être alerté s'ils sont atteints. 

Lorsque vous configurez un nouveau cluster de base de données et l'exécutez avec une charge de travail classique, essayez de capturer les valeurs moyennes, maximales et minimales de toutes les métriques de performances à plusieurs intervalles différents (par exemple, une heure, 24 heures, une semaine, deux semaines). Cela vous permet de vous faire une idée de ce qui est normal. Cela permet de comparer l'activité pendant les heures pleines et les heures creuses. Vous pouvez alors utiliser ces informations pour identifier les périodes où les performances ont chuté en dessous des niveaux standard et définir des alarmes en conséquence.

Consultez [Surveillance de Neptune à l'aide d'Amazon CloudWatch](cloudwatch.md) pour découvrir comment afficher les métriques Neptune.

Voici les métriques les plus importantes pour démarrer :
+ **BufferCacheHitRatio**— Le pourcentage de demandes traitées par le cache tampon. Les échecs d'accès au cache ajoutent une latence importante à l'exécution des requêtes. Si le taux d'accès au cache est inférieur à 99,9 % et que la latence constitue un problème pour votre application, envisagez de mettre à niveau le type d'instance afin de mettre en cache davantage de données en mémoire.
+ **Utilisation de CPU** : pourcentage de la capacité de traitement informatique utilisée. Selon vos objectifs en matière de performance des requêtes, des valeurs de consommation de CPU élevées peuvent être appropriées.
+ **Mémoire libérable** : quantité de RAM disponible sur l'instance de base de données, en octets. Neptune possède son propre gestionnaire de mémoire. Cette métrique peut donc être inférieure à ce à quoi vous vous attendiez. Si les requêtes génèrent souvent des out-of-memory exceptions, vous devriez envisager de mettre à niveau votre classe d'instance vers une classe dotée de plus de RAM.

La ligne rouge sous l'onglet **Surveillance** indique 75 % pour les métriques de CPU et de mémoire. Si la consommation de mémoire de l'instance franchit régulièrement cette ligne, vérifiez votre charge de travail et envisagez de mettre à niveau votre instance pour améliorer les performances des requêtes.

## Bonnes pratiques pour le réglage des requêtes Neptune
<a name="best-practices-general-tuning"></a>

 L'un des meilleurs moyens d'améliorer les performances de Neptune consiste à ajuster les requêtes les plus communément utilisées et exigeantes en ressources pour les rendre moins onéreuses à exécuter. 

Pour en savoir plus sur le réglage des requêtes Gremlin, consultez [Indicateurs de requête Gremlin](gremlin-query-hints.md) et [Réglage des requêtes Gremlin](gremlin-traversal-tuning.md). Pour plus d'informations sur la façon d'ajuster les requêtes SPARQL, consultez [Indicateurs de requête SPARQL](sparql-query-hints.md).

## Équilibrage de charge entre les réplicas en lecture
<a name="best-practices-general-loadbalance"></a>

Le routage en tourniquet du point de terminaison de lecteur modifie l'hôte vers lequel l'entrée DNS pointe. Le client doit créer une nouvelle connexion et résoudre l'enregistrement DNS pour obtenir une connexion à une nouvelle réplique en lecture, car WebSocket les connexions sont souvent maintenues actives pendant de longues périodes.

Pour obtenir différents réplicas en lecture pour les requêtes successives, assurez-vous que votre client résout l'entrée DNS chaque fois qu'il se connecte. Cela peut demander la fermeture de la connexion et la reconnexion au point de terminaison du lecteur.

Vous pouvez également équilibrer la charge des requêtes entre des réplicas en lecture en vous connectant à des points de terminaison d'instance de manière explicite.

## Chargement plus rapide à l'aide d'une instance temporaire de plus grande taille
<a name="best-practices-loader-tempinstance"></a>

Vos performances de chargement augmentent avec des tailles d'instance plus grandes. Si vous n'utilisez pas un grand type d'instance, mais que vous souhaitez accélérer le chargement, vous pouvez utiliser une instance plus grande pour le chargement, puis la supprimer.

**Note**  
La procédure suivante est pour un nouveau cluster. Si vous disposez déjà d'un cluster existant, vous pouvez ajouter une nouvelle instance plus grande, puis la promouvoir en instance de base de données principale.

**Pour charger des données à l'aide d'une taille d'instance plus grande**

1.  Créez un cluster avec une seule instance `r5.12xlarge`. Cette instance est l'instance de base de données principale.

1. Créez un ou plusieurs réplicas en lecture de même taille (`r5.12xlarge`).

   Vous pouvez créer des réplicas en lecture de plus petite taille, mais s'ils ne sont pas assez grands pour suivre le rythme des écritures effectuées par l'instance principale, ils nécessiteront peut-être des redémarrages fréquents. Les temps d'arrêt qui en résulteront réduiront considérablement les performances.

1. Dans la commande de chargement en bloc, incluez `“parallelism” : “OVERSUBSCRIBE”` pour indiquer à Neptune d'utiliser toutes les ressources de CPU disponibles pour le chargement (voir [Paramètres de demande du chargeur Neptune](load-api-reference-load.md#load-api-reference-load-parameters)). L'opération de chargement se déroulera alors aussi rapidement que possible, I/O ce qui nécessite généralement 60 à 70 % des ressources du processeur.

1. Chargez vos données à l'aide du chargeur Neptune. La tâche de chargement s'exécute sur l'instance de base de données principale.

1. Une fois le chargement des données terminé, veillez à réduire toutes les instances du cluster au même type d'instance afin d'éviter des frais supplémentaires et des problèmes de redémarrage répétés (voir [Éviter différentes tailles d'instance](#best-practices-loader-heterogeneous-instances)).

## Redimensionnement de l'instance d'enregistreur en basculant vers un réplica en lecture
<a name="best-practices-resize-instance"></a>

La meilleure façon de redimensionner une instance de votre cluster de bases de données, y compris l'instance d'enregistreur, consiste à créer ou à modifier une instance de réplica en lecture afin qu'elle ait la taille souhaitée, puis de basculer délibérément vers ce réplica. Le temps d'arrêt observé par votre application correspond uniquement au temps nécessaire pour modifier l'adresse IP de l'enregistreur. Il devrait être d'environ trois à cinq secondes.

L'API de gestion Neptune que vous utilisez pour basculer délibérément de l'instance d'enregistreur actuelle vers une instance de réplica en lecture est [Basculement DBCluster](api-clusters.md#FailoverDBCluster). Si vous utilisez le client Java Gremlin, vous devrez peut-être créer un objet client après le basculement pour relever la nouvelle adresse IP, comme indiqué [ici](best-practices-gremlin-java-new-connection.md).

Assurez-vous d'ajuster vos instances pour qu'elles aient toutes la même taille afin d'éviter un cycle de redémarrages répétés, comme indiqué ci-dessous.

## Nouvelle tentative de chargement après une erreur d'interruption de tâche de lecture anticipée des données
<a name="load-api-reference-status-interrupted"></a>

Lorsque vous chargez des données dans Neptune à l'aide du chargeur en bloc, vous pouvez parfois obtenir le statut `LOAD_FAILED` avec une erreur `PARSING_ERROR`, tandis que le message `Data prefetch task interrupted` peut être indiqué en réponse à une demande d'informations détaillées, comme suit :

```
"errorLogs" : [
  {
    "errorCode" : "PARSING_ERROR",
    "errorMessage" : "Data prefetch task interrupted: Data prefetch task for 11467 failed",
    "fileName" : "s3://amzn-s3-demo-bucket/some-source-file",
    "recordNum" : 0
  }
]
```

Si vous rencontrez cette erreur, essayez simplement de relancer la demande de téléchargement par lot.

L'erreur se produit en raison d'une interruption temporaire qui n'est généralement pas provoquée par votre demande ni vos données, et elle peut généralement être résolue en réexécutant la demande de chargement par lot.

Si vous utilisez les paramètres par défaut, à savoir `"mode":"AUTO"` et `"failOnError":"TRUE"`, le chargeur ignore les fichiers qu'il a déjà chargés avec succès et reprend le chargement des fichiers qu'il n'avait pas encore chargés lorsque l'interruption s'est produite.

# Bonnes pratiques d'ordre général pour l'utilisation de Gremlin avec Neptune
<a name="best-practices-gremlin"></a>

Suivez ces recommandations lorsque vous utilisez le langage de parcours de graphe Gremlin avec Neptune. Pour plus d'informations sur l'utilisation de Gremlin avec Neptune, consultez [Accès au graphe Neptune avec Gremlin](access-graph-gremlin.md).

**Important**  
Une modification a été apportée à TinkerPop la version 3.4.11 qui améliore l'exactitude du traitement des requêtes, mais qui, pour le moment, peut parfois avoir un impact sérieux sur les performances des requêtes.  
Par exemple, une requête de ce type peut être beaucoup plus lente :  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  out()
```
Les sommets après l'étape de limite sont désormais récupérés de manière non optimale en raison de la TinkerPop modification 3.4.11. Pour éviter cela, vous pouvez modifier la requête en ajoutant l'étape barrier() à tout moment après `order().by()`. Par exemple :  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  barrier().
  out()
```
TinkerPop [La version 3.4.11 a été activée dans la version 1.0.5.0 du moteur Neptune.](engine-releases-1.0.5.0.md)

**Topics**
+ [Configuration Heartbeat pour Neptune Serverless](best-practices-gremlin-heartbeat-serverless.md)
+ [Structurer les requêtes upsert pour tirer parti du moteur DFE](#best-practices-gremlin-upserts)
+ [Testez le code Gremlin dans le contexte dans lequel vous allez le déployer](best-practices-gremlin-console-glv-differences.md)
+ [Création d'écritures Gremlin multithreads efficaces](best-practices-gremlin-multithreaded-writes.md)
+ [Élagage des enregistrements avec la propriété de date/heure de création](best-practices-gremlin-prune.md)
+ [Utilisation de la méthode `datetime( )` pour les données temporelles Groovy](best-practices-gremlin-datetime.md)
+ [Utilisation de la date et de l'heure natives pour les données temporelles GLV](best-practices-gremlin-datetime-glv.md)

# Configuration Heartbeat pour Neptune Serverless
<a name="best-practices-gremlin-heartbeat-serverless"></a>

Lorsque vous utilisez des WebSocket clients G705 avec Neptune Serverless, vous devez configurer l'intervalle de ping du client de manière appropriée afin de maintenir des connexions stables pendant les événements de dimensionnement. Le client G705 utilise des WebSocket connexions et envoie des pings périodiques pour vérifier que la connexion est active. Le client attend une réponse du serveur dans le délai imparti pour le ping. Si le serveur ne répond pas, le client ferme automatiquement la connexion.

**Pour les instances **provisionnées par** Neptune, nous recommandons de définir l'intervalle ping sur 5 secondes.** Pour les **clusters Neptune Serverless**, nous recommandons de définir l'intervalle ping sur au moins **20 secondes** afin de tenir compte des retards potentiels lors des opérations de dimensionnement. Ce paramètre contrôle la durée pendant laquelle le client attend entre deux écritures sur le serveur avant d'envoyer un ping pour vérifier que la connexion est toujours active.

La configuration de ce paramètre varie en fonction de l'implémentation du client :

**Configuration du client Java**

Pour le client Java TinkerPop G705, configurez le `keepAliveInterval` paramètre :

```
Cluster.Builder builder = Cluster.build()
    .addContactPoint(endpoint)
    .keepAliveInterval(20000); // Configure ping interval in milliseconds
```

Pour plus de détails sur la configuration du pilote Java, consultez la [ TinkerPop documentation Java](https://tinkerpop.apache.org/docs/current/reference/#gremlin-java-configuration).

**Configuration du client Go**

Pour le client G705 Go, configurez le `KeepAliveInterval` paramètre :

```
rc, err := driver.NewDriverRemoteConnection(endpoint,
    func(settings *driver.DriverRemoteConnectionSettings) {
        settings.TraversalSource = "g"
        settings.AuthInfo = auth
        settings.KeepAliveInterval = 20 * time.Second // Configure ping interval
        ...
    })
```

Pour plus de détails sur la configuration du pilote Go, consultez la [ TinkerPop documentation Go](https://tinkerpop.apache.org/docs/current/reference/#gremlin-go-configuration).

**JavaScriptConfiguration du client /Node.js**

Pour le client JavaScript /Node.js G705, configurez le `pingInterval` paramètre :

```
const gremlin = require('gremlin');
const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;

const connection = new DriverRemoteConnection(endpoint, {
    traversalSource: 'g',
    pingInterval: 20000  // Configure ping interval in milliseconds
});
```

Pour plus de détails sur la configuration du JavaScript pilote, reportez-vous à la [JavaScript TinkerPop documentation](https://tinkerpop.apache.org/docs/current/reference/#gremlin-javascript-configuration).

**Configuration du client Python**

Pour le client Python Gremlin, l'intervalle ping est généralement géré au niveau de la couche de transport. Consultez la documentation spécifique de mise en œuvre du transport pour connaître les options de configuration :

```
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection

g = traversal().with_remote(
    DriverRemoteConnection('wss://your-neptune-endpoint:your-neptune-port/gremlin','g',
        transport_factory=lambda: AiohttpTransport(read_timeout=60,
                                                    write_timeout=20,
                                                    heartbeat=20, // Configure heartbeat
                                                    call_from_event_loop=True,
                                                    max_content_length=100*1024*1024,
                                                    ssl_options=ssl.create_default_context(Purpose.CLIENT_AUTH))))
```

Pour plus de détails sur la configuration du pilote Python, consultez la [ TinkerPop documentation Python](https://tinkerpop.apache.org/docs/current/reference/#gremlin-python-configuration).

Cette configuration garantit à votre client la stabilité de la connexion lors des événements de dimensionnement Neptune Serverless, en évitant les fermetures de connexion inutiles et en améliorant la fiabilité des applications.

## Structurer les requêtes upsert pour tirer parti du moteur DFE
<a name="best-practices-gremlin-upserts"></a>

[Réalisation d'upserts efficaces avec les étapes Gremlin `mergeV()` et `mergeE()`](gremlin-efficient-upserts.md) explique comment structurer les requêtes upsert pour utiliser le moteur DFE le plus efficacement possible.

# Testez le code Gremlin dans le contexte dans lequel vous allez le déployer
<a name="best-practices-gremlin-console-glv-differences"></a>

Dans G705, les clients peuvent envoyer des requêtes au serveur de plusieurs manières : en utilisant ou en utilisant le Bytecode GLV WebSocket, ou via la console G705 à l'aide de scripts basés sur des chaînes.

Il est important de savoir que l'exécution d'une requête Gremlin peut varier en fonction de la manière dont vous la soumettez. Une requête qui renvoie un résultat vide peut être considérée comme ayant abouti si elle est soumise en mode Bytecode, mais comme ayant échoué si elle est soumise en mode script. Par exemple, si vous incluez une requête `next()` en mode script, elle `next()` est envoyée au serveur, mais l'utilisation du client ByteCode la `next()` traite généralement elle-même. Dans le premier cas, la requête échoue si aucun résultat n'est trouvé, mais dans le second, elle aboutit, que le jeu de résultats soit vide ou non.

Si vous développez et testez le code dans un seul contexte (par exemple, la console Gremlin qui envoie généralement des requêtes sous forme de texte), mais que vous déployez ensuite le code dans un autre contexte (par exemple via le pilote Java utilisant Bytecode), vous pouvez rencontrer des problèmes liés au fait que le code se comporte différemment en production que dans l'environnement de développement.

**Important**  
Assurez-vous de tester le code Gremlin dans le contexte GLV dans lequel il sera déployé, afin d'éviter des résultats inattendus.

# Création d'écritures Gremlin multithreads efficaces
<a name="best-practices-gremlin-multithreaded-writes"></a>

Quelques recommandations sont à observer pour le chargement multithread de données dans Neptune avec Gremlin.

Dans la mesure du possible, attribuez à chaque thread un ensemble de sommets ou d'arcs à insérer ou modifier qui ne se chevauchent pas. Par exemple, le thread 1 concerne la plage d'ID allant de 1 à 50 000, le thread 2 concerne la plage d'ID allant de 50 001 à 100 000, et ainsi de suite. Cela réduit le risque de générer une exception `ConcurrentModificationException`. Par mesure de sécurité, placez un bloc `try/catch` autour de toutes les écritures. En cas d'échec, vous pouvez effectuer une nouvelle tentative après un court délai.

Le traitement par lots de 50 à 100 écritures (sommets ou arcs) donne généralement de bons résultats. Si, pour chaque sommet, un grand nombre de propriétés sont ajoutées, il est préférable de tendre vers 50 plutôt que 100. Il est utile de réaliser des expérimentations. Ainsi, pour les écritures traitées par lots, vous pouvez utiliser un code similaire à ce qui suit :

```
g.addV(‘test’).property(id,’1’).as(‘a’).
  addV(‘test’).property(id,’2’).
  addE(‘friend’).to(‘a’).
```

Il est ensuite repris dans chaque opération de traitement par lots.

Il est nettement plus efficace d'utiliser des lots que d'ajouter un sommet ou un arc au serveur pour chaque boucle Gremlin.

Si vous utilisez un client GLV (Gremlin Language Variant), vous pouvez créer un lot par programmation en commençant par la création d'un parcours. Ensuite, effectuez des ajouts et, enfin, des itérations, par exemple :

```
  t.addV(‘test’).property(id,’1’).as(‘a’)
  t.addV(‘test’).property(id,’2’)
  t.addE(‘friend’).to(‘a’)
  t.iterate()
```

Il est préférable d'utiliser le client GLV (Gremlin Language Variant) si possible. Mais vous pouvez faire quelque chose de similaire avec un client qui soumet des requêtes sous forme de chaînes de texte qui sont concaténées pour former un lot.

Si vous utilisez l'une des bibliothèques de client Gremlin plutôt que le protocole HTTP de base pour les requêtes, les threads doivent tous partager le même client, cluster ou groupe de connexion. Vous pouvez être amené à ajuster les paramètres pour bénéficier d'un débit optimal, des paramètres tels que la taille du groupe de connexion et le nombre de threads de travail qu'utilise le client Gremlin.

# Élagage des enregistrements avec la propriété de date/heure de création
<a name="best-practices-gremlin-prune"></a>

Vous pouvez élaguer les enregistrements obsolètes en stockant la date/heure de création en tant que propriété sur les sommets et en les supprimant périodiquement.

Si vous devez stocker des données pour une durée de vie spécifique, puis les supprimer du graphe (durée de vie du sommet), vous pouvez stocker une propriété d'horodatage lors de la création du sommet. Vous pouvez ensuite exécuter périodiquement une requête `drop()` pour tous les sommets qui ont été créés avant une certaine date/heure, par exemple :

```
g.V().has(“timestamp”, lt(datetime('2018-10-11')))
```

# Utilisation de la méthode `datetime( )` pour les données temporelles Groovy
<a name="best-practices-gremlin-datetime"></a>

Neptune fournit la méthode `datetime` afin de spécifier des dates et heures pour les requêtes envoyées dans la variante Gremlin **Groovy**. Cela inclut la console Gremlin, les chaînes de texte utilisant l'API REST HTTP, et toute autre sérialisation qui utilise Groovy. 

**Important**  
Ceci s'applique *uniquement* aux méthodes dans lesquelles vous envoyez la requête Gremlin en tant que *chaîne de texte*. Si vous utilisez une variante du langage Gremlin (Gremlin Language Variant), vous devez utiliser les classes et fonctions de dates natives pour le langage. Pour plus d’informations, consultez la section suivante, [Utilisation de la date et de l'heure natives pour les données temporelles GLV](best-practices-gremlin-datetime-glv.md).  
Commencer par TinkerPop `3.5.2` (introduit dans la [version 1.1.1.0 du moteur Neptune](engine-releases-1.1.1.0.md)) fait partie intégrante de. `datetime` TinkerPop

Vous pouvez utiliser la méthode `datetime` pour stocker et comparer des dates :

```
g.V('3').property('date',datetime('2001-02-08'))
```

```
g.V().has('date',gt(datetime('2000-01-01')))
```

# Utilisation de la date et de l'heure natives pour les données temporelles GLV
<a name="best-practices-gremlin-datetime-glv"></a>

Si vous utilisez une variante du langage Gremlin (GLV), vous devez utiliser les classes et fonctions de date et d'heure natives fournies par le langage de programmation pour les données temporelles Gremlin.

Les TinkerPop bibliothèques officielles sont toutes des bibliothèques de variantes de langage G705.
+  [Go](https://tinkerpop.apache.org/docs/current/reference/#gremlin-go) 
+  [Java](https://tinkerpop.apache.org/docs/current/reference/#gremlin-java) 
+  [JavaScript](https://tinkerpop.apache.org/docs/current/reference/#gremlin-javascript) 
+  [.NET](https://tinkerpop.apache.org/docs/current/reference/#gremlin-dotnet) 
+  [Python](https://tinkerpop.apache.org/docs/current/reference/#gremlin-python) 

**Important**  
 Cette page s'applique uniquement aux bibliothèques GLV (G705 Language Variant). Si vous utilisez une méthode dans laquelle vous envoyez la requête Gremlin sous forme de chaîne de texte, vous devez utiliser la fonction datetime () de G705. Cela inclut la console G705, les chaînes de texte utilisant l'API HTTP REST ou la soumission directe de chaînes G705 via les pilotes. 



**Go**  
 Voici un exemple partiel dans Go qui crée une propriété unique nommée « date » pour le sommet avec un ID de « 3 ». Il définit la valeur comme étant une date générée à l'aide de la fonction Go Time.now (). 

```
import ( "time" )

g.V('3').property('date', time.Now()).next();
```

Pour un exemple complet de connexion à Neptune à l'aide de Go, voir [Utilisation d'un client Go pour se connecter à une instance de base de données Neptune](https://docs.aws.amazon.com//neptune/latest/userguide/access-graph-gremlin-go.html).

**Java**  
Voici un exemple partiel en Java qui crée une propriété unique nommée « `date` » pour le sommet avec l'ID « `3` ». Cet exemple définit la valeur sur une date générée à l'aide du constructeur `Date()` Java.

```
import java.util.date

g.V('3').property('date', new Date()).next();
```

Pour un exemple complet de connexion à Neptune à l'aide de Java, consultez. [Utilisation du client Java pour se connecter à une instance de base de données Neptune](access-graph-gremlin-java.md)

**Node.js (JavaScript)**  
Voici un exemple partiel de création JavaScript d'une propriété unique nommée « `date` » pour le sommet avec un ID de « `3` ». Cet exemple définit la valeur sur une date générée à l'aide du constructeur `Date()` Node.js.

```
g.V('3').property('date', new Date()).next()
```

Pour un exemple complet de connexion à Neptune à l'aide de Node.js, consultez. [Utilisation de Node.js pour se connecter à une instance de base de données Neptune](access-graph-gremlin-node-js.md)

**.NET (C\$1)**  
Voici un exemple partiel en C\$1 qui crée une propriété unique nommée « `date` » pour le sommet avec l'ID « `3` ». Cet exemple définit la valeur sur une date générée à l'aide de la propriété .NET `DateTime.UtcNow`.

```
Using System;

g.V('3').property('date', DateTime.UtcNow).next()
```

Pour un exemple complet de connexion à Neptune à l'aide de C\$1, consultez. [Utilisation de .NET pour se connecter à une instance de base de données Neptune](access-graph-gremlin-dotnet.md)

**Python**  
Voici un exemple partiel en Python qui crée une propriété unique nommée « `date` » pour le sommet avec l'ID « `3` ». Cet exemple définit la valeur sur une date générée à l'aide de la méthode `datetime.now()` Python.

```
import datetime

g.V('3').property('date',datetime.datetime.now()).next()
```

Pour un exemple complet de connexion à Neptune à l'aide de Python, consultez. [Utilisation de Python pour se connecter à une instance de base de données Neptune](access-graph-gremlin-python.md)

# Bonnes pratiques pour l'utilisation du client Java Gremlin avec Neptune
<a name="best-practices-gremlin-java-client"></a>

Suivez ces recommandations lors de l'utilisation du client Java Gremlin avec Neptune. Ces bonnes pratiques vous aident à optimiser les performances, à gérer efficacement les connexions et à éviter les pièges courants lorsque vous utilisez le pilote Java.

Pour plus d'informations sur la configuration des intervalles de pulsation pour Neptune Serverless, consultez. [Configuration Heartbeat pour Neptune Serverless](best-practices-gremlin-heartbeat-serverless.md)

**Topics**
+ [Réutilisation de l'objet client dans plusieurs threads](best-practices-gremlin-java-reuse.md)
+ [Création d'objets client Java Gremlin distincts pour les points de terminaison en lecture et en écriture](best-practices-gremlin-java-separate.md)
+ [Ajout de plusieurs points de terminaison de réplica en lecture à un groupe de connexions Java Gremlin](best-practices-gremlin-java-multiple.md)
+ [Fermeture du client pour éviter de limiter les connexions](best-practices-gremlin-java-close-connections.md)
+ [Création d'une connexion après un basculement](best-practices-gremlin-java-new-connection.md)
+ [Définition de `maxInProcessPerConnection` et `maxSimultaneousUsagePerConnection` sur la même valeur](best-practices-gremlin-java-maxes.md)
+ [Envoi de requêtes au serveur sous la forme de bytecode et non de chaînes](best-practices-gremlin-java-bytecode.md)
+ [Consommez toujours complètement l'itérateur ResultSet ou l'itérateur renvoyé par une requête](best-practices-gremlin-java-resultset.md)
+ [Ajout en bloc de sommets et d'arêtes dans des lots](best-practices-gremlin-java-batch-add.md)
+ [Désactivation de la mise en cache du DNS dans la machine virtuelle Java](best-practices-gremlin-java-disable-dns-caching.md)
+ [Définition facultative de délais d'expiration au niveau de chaque requête](best-practices-gremlin-java-per-query-timeout.md)
+ [Résolution des problèmes de `java.util.concurrent.TimeoutException`](best-practices-gremlin-java-exceptions-TimeoutException.md)

# Réutilisation de l'objet client dans plusieurs threads
<a name="best-practices-gremlin-java-reuse"></a>

Réutilisez le même objet client (ou `GraphTraversalSource`) dans plusieurs threads. Autrement dit, créez une instance partagée d'une `org.apache.tinkerpop.gremlin.driver.Client` classe dans votre application plutôt que de le faire dans chaque thread. L'objet `Client` est thread-safe, et la surcharge entraînée par l'initialisation est considérable.

Cela s'applique également à `GraphTraversalSource`, qui crée un objet `Client` en interne. Par exemple, le code suivant entraîne l'instanciation d'un nouvel objet `Client` :

```
import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal; 

  /////

GraphTraversalSource traversal = traversal()
                                   .withRemote(DriverRemoteConnection.using(cluster));
```

# Création d'objets client Java Gremlin distincts pour les points de terminaison en lecture et en écriture
<a name="best-practices-gremlin-java-separate"></a>

Vous pouvez augmenter les performances en effectuant uniquement les écritures sur le point de terminaison enregistreur et la lecture à partir d'un ou plusieurs points de terminaison en lecture seule.

```
Client readerClient = Cluster.build("https://reader-endpoint")
          ...
          .connect()

Client writerClient = Cluster.build("https://writer-endpoint")
          ...
          .connect()
```

# Ajout de plusieurs points de terminaison de réplica en lecture à un groupe de connexions Java Gremlin
<a name="best-practices-gremlin-java-multiple"></a>

Lorsque vous créez un objet `Cluster` Java Gremlin, vous pouvez utiliser la méthode `.addContactPoint()` pour ajouter plusieurs instances de réplicas en lecture aux points de contact du groupe de connexions.

```
Cluster.Builder readerBuilder = Cluster.build()
          .port(8182)
          .minConnectionPoolSize(…)
          .maxConnectionPoolSize(…)
          ………
          .addContactPoint("reader-endpoint-1")
          .addContactPoint("reader-endpoint-2")
```

# Fermeture du client pour éviter de limiter les connexions
<a name="best-practices-gremlin-java-close-connections"></a>

Il est important de fermer le client lorsque vous en avez terminé avec celui-ci pour vous assurer que les WebSocket connexions sont fermées par le serveur et que toutes les ressources associées aux connexions sont libérées. Cela se produit automatiquement si vous fermez le cluster à l'aide de `Cluster.close( )`, car `client.close( )` est ensuite appelé en interne.

Si le client n'est pas fermé correctement, Neptune met fin à toutes les WebSocket connexions inactives au bout de 20 à 25 minutes. Toutefois, si vous ne fermez pas explicitement les WebSocket connexions lorsque vous en avez terminé et que le nombre de connexions en direct atteint la [limite de connexions WebSocket simultanées](limits.md#limits-websockets), les connexions supplémentaires sont alors refusées avec un code `429` d'erreur HTTP. À ce stade, vous devez redémarrer l'instance Neptune pour fermer les connexions.

Le conseil avisant d'appeler `cluster.close()` ne s'applique pas aux fonctions AWS Lambda Java. Consultez [Gestion des WebSocket connexions G705 dans les fonctions AWS Lambda](lambda-functions-websocket-connections.md) pour plus de détails.

# Création d'une connexion après un basculement
<a name="best-practices-gremlin-java-new-connection"></a>

En cas de basculement, le pilote Gremlin peut continuer à se connecter à l'ancien enregistreur, car le nom DNS du cluster a été résolu en une adresse IP. Si cela se produit, vous pouvez créer un nouvel objet `Client` après le basculement.

# Définition de `maxInProcessPerConnection` et `maxSimultaneousUsagePerConnection` sur la même valeur
<a name="best-practices-gremlin-java-maxes"></a>

Les paramètres `maxInProcessPerConnection` et les `maxSimultaneousUsagePerConnection` paramètres sont liés au nombre maximum de requêtes simultanées que vous pouvez soumettre sur une seule WebSocket connexion. En interne, ces paramètres sont liés entre eux et la modification de l'un sans modifier l'autre peut entraîner la réception d'un délai d'expiration lors d'une tentative d'extraction d'une connexion à partir du groupe de connexions du client.

Nous vous recommandons de conserver la valeur minimale par défaut en cours et la valeur d'utilisation simultanée, et de définir `maxInProcessPerConnection` et `maxSimultaneousUsagePerConnection` sur la même valeur.

La valeur sur laquelle définir ces paramètres dépend de la complexité de la requête et du modèle de données. Un cas d'utilisation dans lequel la requête renvoie un grand nombre de données nécessite plus de bande passante de connexion par requête. Par conséquent, les paramètres doivent être définis sur des valeurs plus faibles et `maxConnectionPoolSize` sur une valeur plus élevée.

En revanche, dans le cas où la requête renvoie une plus petite quantité de données, `maxInProcessPerConnection` et `maxSimultaneousUsagePerConnection` doivent être définis sur une valeur plus élevée que `maxConnectionPoolSize`.

# Envoi de requêtes au serveur sous la forme de bytecode et non de chaînes
<a name="best-practices-gremlin-java-bytecode"></a>

L'utilisation de bytecode plutôt que d'une chaîne lors de la soumission de requêtes présente plusieurs avantages :
+ **Interception plus rapide d'une syntaxe de requête non valide : **  L'utilisation de la variante bytecode permet de détecter une syntaxe de requête non valide lors de l'étape de compilation. Si vous utilisez la variante basée sur une chaîne, vous ne pouvez pas identifier la syntaxe non valide tant que la requête n'est pas soumise au serveur et qu'une erreur n'est pas renvoyée.
+ **Évitez de réduire les performances basées sur des chaînes :** [toute soumission de requête basée sur des chaînes, que vous utilisiez HTTP WebSockets ou HTTP, entraîne le détachement d'un sommet, ce qui implique que l'objet Vertex comprend l'ID, l'étiquette et toutes les propriétés associées au sommet (voir Propriétés des éléments).](http://tinkerpop.apache.org/docs/current/reference/#_properties_of_elements)

  Dans les cas où les propriétés ne sont pas obligatoires, cela peut entraîner des calculs inutiles sur le serveur. Par exemple, si le client souhaite obtenir le vertex portant l'ID « hakuna\$11 » à l'aide de la requête `g.V("hakuna#1")`. Si la requête est envoyée sous la forme d'une soumission basée sur une chaîne, le serveur consacre du temps à la récupération de l'ID, de l'étiquette et de toutes les propriétés de ce vertex. Si la requête est envoyée sous la forme d'une soumission de bytecode, le serveur consacre du temps à la récupération de l'ID et de l'étiquette du vertex uniquement.

En d'autres termes, au lieu de soumettre une requête comme suit :

```
  final Cluster cluster = Cluster.build("localhost")
                                 .port(8182)
                                 .maxInProcessPerConnection(32)
                                 .maxSimultaneousUsagePerConnection(32)
                                 .serializer(Serializers.GRAPHBINARY_V1D0)
                                 .create();

  try {
      final Client client = cluster.connect();
      List<Result> results = client.submit("g.V().has('name','pumba').out('friendOf').id()").all().get();
      System.out.println(verticesWithNamePumba);
  } finally {
      cluster.close();
  }
```

Soumettez la requête en bytecode comme suit :

```
  final Cluster cluster = Cluster.build("localhost")
                                 .port(8182)
                                 .maxInProcessPerConnection(32)
                                 .maxSimultaneousUsagePerConnection(32)
                                 .serializer(Serializers.GRAPHBINARY_V1D0)
                                 .create();

  try {
      final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
      List<Object> verticesWithNamePumba = g.V().has("name", "pumba").out("friendOf").id().toList();
      System.out.println(verticesWithNamePumba);
  } finally {
      cluster.close();
  }
```

# Consommez toujours complètement l'itérateur ResultSet ou l'itérateur renvoyé par une requête
<a name="best-practices-gremlin-java-resultset"></a>

L'objet client doit toujours utiliser intégralement le `ResultSet` (dans le cas d'une soumission basée sur une chaîne) ou l'itérateur renvoyé par `GraphTraversal`. Si les résultats de la requête ne sont pas intégralement utilisés, le serveur les conserve et attend que le client finisse de les utiliser.

Si votre application a uniquement besoin d'une partie des résultats, vous pouvez utiliser une étape `limit(X)` avec votre requête pour limiter le nombre de résultats générés par le serveur.

# Ajout en bloc de sommets et d'arêtes dans des lots
<a name="best-practices-gremlin-java-batch-add"></a>

Chaque requête envoyée à la base de données Neptune s'exécute dans le cadre d'une seule transaction, sauf si vous utilisez une session. Autrement dit, si vous devez insérer un grand nombre de données à l'aide de requêtes Gremlin, leur traitement dans un lot contenant entre 50 et 100 éléments permet d'améliorer les performances en réduisant le nombre de transactions créées pour la charge.

Par exemple, l'ajout de 5 vertex à la base de données se présente comme suit :

```
// Create a GraphTraversalSource for the remote connection
final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
// Add 5 vertices in a single query
g.addV("Person").property(T.id, "P1")
 .addV("Person").property(T.id, "P2")
 .addV("Person").property(T.id, "P3")
 .addV("Person").property(T.id, "P4")
 .addV("Person").property(T.id, "P5").iterate();
```

De même, vous pouvez ajouter des arêtes par lots à l'aide de. `addE` `V()`À utiliser pour référencer des sommets existants en tant que source et cible de chaque arête :

```
// Add edges in a single batched query
g.V("P1").addE("knows").to(V("P2"))
 .V("P2").addE("knows").to(V("P3"))
 .V("P3").addE("knows").to(V("P4"))
 .V("P4").addE("knows").to(V("P5")).iterate();
```

Vous pouvez également combiner la création de sommets et d'arêtes en un seul lot. `as()`À utiliser pour étiqueter les sommets nouvellement créés afin de pouvoir les référencer lorsque vous ajoutez des arêtes dans le même parcours :

```
// Add vertices and edges together in a single query
g.addV("Person").property(T.id, "P1").as("p1")
 .addV("Person").property(T.id, "P2").as("p2")
 .addV("Person").property(T.id, "P3").as("p3")
 .addE("knows").from("p1").to("p2")
 .addE("knows").from("p2").to("p3").iterate();
```

# Désactivation de la mise en cache du DNS dans la machine virtuelle Java
<a name="best-practices-gremlin-java-disable-dns-caching"></a>

[Dans un environnement où vous souhaitez équilibrer la charge des requêtes entre plusieurs répliques de lecture, vous devez désactiver la mise en cache DNS dans la machine virtuelle Java (JVM) et fournir le point de terminaison du lecteur Neptune lors de la création de l'objet Cluster.](https://tinkerpop.apache.org/javadocs/current/core/org/apache/tinkerpop/gremlin/driver/Cluster.html) La désactivation du cache du DNS dans la JVM garantit que le DNS est à nouveau résolu pour chaque nouvelle connexion, de sorte que les demandes soient réparties entre tous les réplicas en lecture. Pour ce faire, ajoutez la ligne suivante dans le code d'initialisation de votre application :

```
java.security.Security.setProperty("networkaddress.cache.ttl", "0");
```

Cependant, une solution plus complète et plus robuste pour l'équilibrage de charge est fournie par le code [client Java Amazon Gremlin](https://github.com/awslabs/amazon-neptune-tools/tree/master/neptune-gremlin-client) sur. GitHub Le client Amazon Java Gremlin connaît la topologie de votre cluster et répartit équitablement les connexions et les demandes entre un ensemble d'instances du cluster Neptune. Consultez [ce billet de blog](https://aws.amazon.com/blogs/database/load-balance-graph-queries-using-the-amazon-neptune-gremlin-client/) pour découvrir un exemple de fonction Lambda Java qui utilise ce client.

# Définition facultative de délais d'expiration au niveau de chaque requête
<a name="best-practices-gremlin-java-per-query-timeout"></a>

Neptune offre la possibilité de définir un délai d'expiration pour vos requêtes en utilisant l'option de groupe de paramètres `neptune_query_timeout` (consultez [Parameters](parameters.md)). Vous pouvez également annuler le délai d'attente global, avec un code comme celui-ci :

```
  final Cluster cluster = Cluster.build("localhost")
                                 .port(8182)
                                 .maxInProcessPerConnection(32)
                                 .maxSimultaneousUsagePerConnection(32)
                                 .serializer(Serializers.GRAPHBINARY_V1D0)
                                 .create();

  try {
      final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
      List<Object> verticesWithNamePumba = g.with(ARGS_EVAL_TIMEOUT, 500L).V().has("name", "pumba").out("friendOf").id().toList();
      System.out.println(verticesWithNamePumba);
  } finally {
      cluster.close();
  }
```

Sinon, le code se présente comme suit pour la soumission d'une requête basée sur une chaîne :

```
  RequestOptions options = RequestOptions.build().timeout(500).create();
  List<Result> result = client.submit("g.V()", options).all().get();
```

**Note**  
Il est possible que vous encouriez des coûts inattendus si vous définissez une valeur trop élevée pour le délai d'expiration des requêtes, en particulier sur une instance sans serveur. En l'absence d'un délai raisonnable d'expiration des requêtes, la requête risque de s'exécuter bien plus longtemps que prévu, entraînant ainsi des coûts que vous n'aviez pas anticipés. Cela est particulièrement vrai pour une instance sans serveur, qui pourrait passer à un type d'instance volumineux et coûteux lors de l'exécution de la requête.  
Pour éviter des dépenses imprévues de ce type, utilisez une valeur de délai d'expiration qui s'adapte au temps d'éxécution prévu et qui n'implique que l'expiration des exécutions dont le délai est étonnamment long.  
 À partir de la version 1.3.2.0 du moteur Neptune, Neptune prend en charge un nouveau paramètre neptune\$1lab\$1mode sous la forme. `StrictTimeoutValidation` Lorsque ce paramètre a une valeur de`Enabled`, une valeur de délai par requête spécifiée en tant qu'option de demande ou indication de requête ne peut pas dépasser la valeur définie globalement dans le groupe de paramètres. Dans ce cas, Neptune lancera. `InvalidParameterException`   
 Ce paramètre peut être confirmé dans une réponse sur le point de terminaison « /status » lorsque la valeur est`Disabled`. Dans la version du moteur`1.3.2.0`, la valeur par défaut de ce paramètre est`Disabled`. À partir de la version du moteur`1.4.0.0`, le `StrictTimeoutValidation` paramètre est défini `Enabled` par défaut.   
 Pour plus d'informations sur la façon dont la priorité du délai d'expiration est déterminée lorsque plusieurs paramètres de délai d'expiration sont configurés, consultez la documentation du paramètre [neptune\$1query\$1timeout](parameters.md#parameters-db-cluster-parameters-neptune_query_timeout). 

# Résolution des problèmes de `java.util.concurrent.TimeoutException`
<a name="best-practices-gremlin-java-exceptions-TimeoutException"></a>

Le client Java Gremlin envoie au client lui-même un message indiquant `java.util.concurrent.TimeoutException` quand une requête Garmin expire en attendant qu'un emplacement soit disponible dans l'une des WebSocket connexions. Ce délai d'expiration est contrôlé par le paramètre configurable `maxWaitForConnection` côté client.

**Note**  
Comme les demandes qui expirent sur le client ne sont jamais envoyées au serveur, elles ne sont reflétées dans aucune des métriques capturées sur le serveur, telles que `GremlinRequestsPerSec`.

Ce type de délai d'expiration est généralement causé de l'une des deux manières suivantes :
+ **Le serveur a atteint sa capacité maximale.** Dans ce cas, la file d'attente du serveur se remplit, ce que vous pouvez détecter en surveillant la [MainRequestQueuePendingRequests](cw-metrics.md#cw-metrics-available) CloudWatch métrique. Le nombre de requêtes parallèles que le serveur peut traiter dépend de la taille de son instance.

  Si la métrique `MainRequestQueuePendingRequests` n'indique pas une accumulation des demandes en attente sur le serveur, le serveur peut traiter un plus grand nombre de demandes, et l'expiration est donc due à une limitation côté client.
+ **Limitation des demandes côté client.** Ce problème peut généralement être résolu en modifiant les paramètres de configuration du client.

  Le nombre maximal de demandes parallèles que le client peut envoyer peut être estimé approximativement comme suit :

  ```
  maxParallelQueries = maxConnectionPoolSize * Max( maxSimultaneousUsagePerConnection, maxInProcessPerConnection )
  ```

  L'envoi d'un nombre de demandes supérieur à `maxParallelQueries` au client entraîne des exceptions `java.util.concurrent.TimeoutException`. Vous pouvez généralement résoudre ce problème de plusieurs manières :
  + *Augmentez le délai d'expiration de la connexion.* Si la latence n'est pas cruciale pour votre application, augmentez la valeur du paramètre `maxWaitForConnection` du client. Le client attendra plus longtemps avant l'expiration du délai, ce qui contribue à accroître la latence.
  + *Augmentez le nombre maximum de demandes par connexion.* Cela permet d'envoyer un plus grand nombre de demandes via la même WebSocket connexion. Pour ce faire, augmentez la valeur des paramètres `maxSimultaneousUsagePerConnection` et `maxInProcessPerConnection` du client. Ces paramètres doivent généralement avoir la même valeur. 
  + *Augmentez le nombre de connexions dans le groupe de connexions.* Pour ce faire, augmentez la valeur du paramètre `maxConnectionPoolSize` du client. Le coût est une augmentation de la consommation de ressources, car chaque connexion utilise de la mémoire et un descripteur de fichier du système d'exploitation, et nécessite un protocole SSL et WebSocket une poignée de main lors de l'initialisation.

# Bonnes pratiques Neptune avec openCypher et Bolt
<a name="best-practices-opencypher"></a>

Suivez ces bonnes pratiques lorsque vous utilisez le langage de requête openCypher et le protocole Bolt avec Neptune. Pour plus d'informations sur l'utilisation d'openCypher dans Neptune, consultez [Accès au graphe Neptune avec openCypher](access-graph-opencypher.md).

**Topics**
+ [Création d'une connexion après un basculement](#best-practices-opencypher-renew-connection)
+ [Gestion des connexions pour les applications de longue durée](#best-practices-opencypher-long-connections)
+ [Gestion des connexions pour AWS Lambda](#best-practices-opencypher-lambda-connections)
+ [Préférer les arêtes dirigées aux arêtes bidirectionnelles dans les requêtes](best-practices-opencypher-directed-edges.md)
+ [Neptune ne prend pas en charge plusieurs requêtes simultanées dans une transaction](best-practices-opencypher-multiple-queries.md)
+ [Fermer les objets Driver lorsque vous avez terminé](best-practices-opencypher-close-driver.md)
+ [Utilisation des modes de transaction explicites pour la lecture et l'écriture](best-practices-opencypher-use-explicit-txs.md)
+ [Logique des nouvelles tentatives pour les exceptions](best-practices-opencypher-retry-logic.md)
+ [Définissez plusieurs propriétés à la fois à l'aide d'une seule clause SET](best-practices-content-0.md)
+ [Utilisation des requêtes paramétrées](best-practices-content-2.md)
+ [Utilisez des cartes aplaties au lieu de cartes imbriquées dans la clause UNWIND](best-practices-content-3.md)
+ [Placez des nœuds plus restrictifs sur le côté gauche dans les expressions VLP (Variable-Length Path)](best-practices-content-4.md)
+ [Évitez les vérifications redondantes des étiquettes des nœuds en utilisant des noms de relations granulaires](best-practices-content-5.md)
+ [Spécifiez les étiquettes de bord dans la mesure du possible](best-practices-content-6.md)
+ [Évitez d'utiliser la clause WITH dans la mesure du possible](best-practices-content-7.md)
+ [Placez les filtres restrictifs le plus tôt possible dans la requête](best-practices-content-8.md)
+ [Vérifiez explicitement si les propriétés existent](best-practices-content-9.md)
+ [N'utilisez pas de chemin nommé (sauf si cela est obligatoire)](best-practices-content-10.md)
+ [Évitez COLLECT (DISTINCT ())](best-practices-content-11.md)
+ [Préférez la fonction de propriétés à la recherche de propriétés individuelle lors de la récupération de toutes les valeurs de propriété](best-practices-content-12.md)
+ [Effectuer des calculs statiques en dehors de la requête](best-practices-content-13.md)
+ [Entrées par lots utilisant UNWIND au lieu d'instructions individuelles](best-practices-content-14.md)
+ [Préférez utiliser la personnalisation IDs pour le nœud ou la relation](best-practices-content-15.md)
+ [Évitez de faire des calculs \$1id dans la requête](best-practices-content-16.md)
+ [Mise à jour/fusion de plusieurs nœuds](best-practices-merge-multiple-nodes.md)

## Création d'une connexion après un basculement
<a name="best-practices-opencypher-renew-connection"></a>

En cas de basculement, le pilote Bolt peut continuer à se connecter à l'ancienne instance d'enregistreur plutôt qu'à la nouvelle instance active, car le nom DNS a été résolu en une adresse IP spécifique.

Pour éviter cela, fermez puis reconnectez l'objet `Driver` après un basculement.

## Gestion des connexions pour les applications de longue durée
<a name="best-practices-opencypher-long-connections"></a>

Lorsque vous créez des applications à longue durée de vie, telles que celles qui s'exécutent dans des conteneurs ou sur des instances Amazon EC2, instanciez un objet `Driver` une fois, puis réutilisez-le pendant toute la durée de vie de l'application. L'objet `Driver` est thread-safe, et la surcharge entraînée par l'initialisation est considérable.

## Gestion des connexions pour AWS Lambda
<a name="best-practices-opencypher-lambda-connections"></a>

Les pilotes Bolt ne sont pas recommandés pour une utilisation dans AWS Lambda les fonctions, en raison de leur surcharge de connexion et des exigences de gestion. Utilisez plutôt le [point de terminaison HTTPS](access-graph-opencypher-queries.md).

# Préférer les arêtes dirigées aux arêtes bidirectionnelles dans les requêtes
<a name="best-practices-opencypher-directed-edges"></a>

Lorsque Neptune optimise les requêtes, les arêtes bidirectionnelles compliquent la création de plans de requêtes optimaux. Les plans sous-optimaux obligent le moteur à effectuer des tâches superflues, ce qui entraîne une baisse des performances.

Par conséquent, utilisez des arêtes dirigées plutôt que des arêtes bidirectionnelles dans la mesure du possible. Par exemple, utilisez :

```
MATCH p=(:airport {code: 'ANC'})-[:route]->(d) RETURN p)
```

au lieu de :

```
MATCH p=(:airport {code: 'ANC'})-[:route]-(d) RETURN p)
```

La plupart des modèles de données n'ont pas besoin de traverser les arêtes dans les deux sens. Les requêtes peuvent donc améliorer considérablement les performances avec l'utilisation d'arêtes dirigées.

Si le modèle de données nécessite de traverser des arêtes bidirectionnelles, faites du premier nœud (côté gauche) le nœud avec le filtrage le plus restrictif dans le modèle `MATCH`.

Prenons un exemple recherchant tous les itinéraires (`routes`) à destination et en provenance de l'aéroport `ANC`. Écrivez cette requête pour commencer à l'aéroport `ANC`, comme suit :

```
MATCH p=(src:airport {code: 'ANC'})-[:route]-(d) RETURN p
```

Le moteur peut ainsi effectuer un minimum d'efforts pour satisfaire la requête, car le nœud le plus restreint est placé en tant que premier nœud (côté gauche) dans le modèle. Puis, il peut optimiser la requête.

Cette approche est de loin préférable à un filtrage de l'aéroport `ANC` à la fin du modèle, comme ceci :

```
MATCH p=(d)-[:route]-(src:airport {code: 'ANC'}) RETURN p
```

Lorsque le nœud le plus restreint n'est pas placé en premier dans le modèle, le moteur doit effectuer des efforts supplémentaires, car il ne peut pas optimiser la requête et doit réaliser des recherches supplémentaires pour obtenir les résultats.

# Neptune ne prend pas en charge plusieurs requêtes simultanées dans une transaction
<a name="best-practices-opencypher-multiple-queries"></a>

Bien que le pilote Bolt lui-même autorise les requêtes simultanées dans une transaction, Neptune ne prend pas en charge plusieurs requêtes dans le cadre d'une transaction exécutée simultanément. Neptune exige plutôt que plusieurs requêtes d'une transaction soient exécutées de manière séquentielle et que les résultats de chaque requête soient entièrement utilisés avant le lancement de la prochaine requête.

L'exemple ci-dessous montre comment utiliser Bolt pour exécuter plusieurs requêtes de manière séquentielle dans une transaction, de sorte que les résultats de chacune soient entièrement consommés avant le début de la suivante :

```
final String query = "MATCH (n) RETURN n";

try (Driver driver = getDriver(HOST_BOLT, getDefaultConfig())) {
  try (Session session = driver.session(readSessionConfig)) {
    try (Transaction trx = session.beginTransaction()) {
      final Result res_1 = trx.run(query);
      Assert.assertEquals(10000, res_1.list().size());
      final Result res_2 = trx.run(query);
      Assert.assertEquals(10000, res_2.list().size());
    }
  }
}
```

# Fermer les objets Driver lorsque vous avez terminé
<a name="best-practices-opencypher-close-driver"></a>

Il est important de fermer le client lorsque vous n'en avez plus besoin afin de garantir que les connexions Bolt sont fermées par le serveur et que toutes les ressources associées aux connexions sont libérées. Cela se produit automatiquement si vous fermez le pilote à l'aide de `driver.close()`.

Si le pilote n'est pas correctement fermé, Neptune met fin à toutes les connexions Bolt inactives au bout de 20 minutes, ou au bout de 10 jours si vous utilisez l'authentification IAM.

Neptune prend en charge jusqu'à 1 000 connexions Bolt simultanées. Si vous ne fermez pas explicitement les connexions lorsque vous n'en avez plus besoin, et que le nombre de connexions en direct atteint cette limite de 1 000, toute nouvelle tentative de connexion échouera.

# Utilisation des modes de transaction explicites pour la lecture et l'écriture
<a name="best-practices-opencypher-use-explicit-txs"></a>

Lorsque vous utilisez des transactions avec Neptune et le pilote Bolt, il est préférable de définir explicitement le mode d'accès des transactions en lecture et en écriture avec les paramètres appropriés.

## Transactions en lecture seule
<a name="best-practices-opencypher-read-txs"></a>

Pour les transactions en lecture seule, si vous ne transmettez pas la configuration de mode d'accès appropriée lors de la création de la session, le niveau d'isolement par défaut est utilisé, à savoir l'isolement des requêtes de mutation. Par conséquent, il est important de définir le mode d'accès `read` de manière explicite pour les transactions en lecture seule.

**Exemple de transaction de lecture avec validation automatique :**

```
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.READ)
  .build();
Session session = driver.session(sessionConfig);
try {
  (Add your application code here)
} catch (final Exception e) {
  throw e;
} finally {
  driver.close()
}
```

**Exemple de transaction de lecture :**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withDefaultAccessMode(AccessMode.READ)
  .build();
driver.session(sessionConfig).readTransaction(
  new TransactionWork<List<String>>() {
    @Override
    public List<String> execute(org.neo4j.driver.Transaction tx) {
      (Add your application code here)
    }
  }
);
```

Dans les deux cas, l'[isolement `SNAPSHOT`](transactions-isolation-levels.md) est réalisé à l'aide de la sémantique des [transactions en lecture seule de Neptune](transactions-neptune.md#transactions-neptune-read-only).

Comme les réplicas en lecture n'acceptent que les requêtes en lecture seule, toute requête soumise à un réplica en lecture s'exécute selon une sémantique d'isolement `SNAPSHOT`.

Il n'y a pas de lectures corrompues ni de lectures non reproductibles pour les transactions en lecture seule.

## Transactions de mutation
<a name="best-practices-opencypher-mutation-txs"></a>

Pour les requêtes de mutation, il existe trois mécanismes différents pour créer une transaction d'écriture, chacun étant illustré ci-dessous :

**Exemple de transaction d'écriture implicite :**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withDefaultAccessMode(AccessMode.WRITE)
  .build();
driver.session(sessionConfig).writeTransaction(
  new TransactionWork<List<String>>() {
    @Override
    public List<String> execute(org.neo4j.driver.Transaction tx) {
      (Add your application code here)
    }
  }
);
```

**Exemple de transaction d'écriture à validation automatique :**

```
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.Write)
  .build();
Session session = driver.session(sessionConfig);
try {
  (Add your application code here)
} catch (final Exception e) {
    throw e;
} finally {
    driver.close()
}
```

**Exemple de transaction d'écriture explicite :**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.WRITE)
  .build();
Transaction beginWriteTransaction = driver.session(sessionConfig).beginTransaction();
  (Add your application code here)
beginWriteTransaction.commit();
driver.close();
```

**Niveaux d'isolement pour les transactions d'écriture**
+ Les lectures effectuées dans le cadre de requêtes de mutation sont exécutées dans le cadre de l'isolement des transactions `READ COMMITTED`.
+ Il n'existe aucune lecture corrompue pour les lectures effectuées dans le cadre de requêtes de mutation.
+ Les enregistrements et les plages d'enregistrements sont verrouillés lors de la lecture d'une requête de mutation.
+ Lorsqu'une plage de l'index a été lue par une transaction de mutation, vous bénéficiez de la garantie que cette plage ne sera pas modifiée par des transactions simultanées jusqu'à la fin de la lecture.

Les requêtes de mutation ne sont pas thread-safe.

Pour les conflits, voir [Résolution des conflits à l'aide de délais d'attente de verrouillage](transactions-neptune.md#transactions-neptune-conflicts).

Les requêtes de mutation ne font pas automatiquement l'objet de nouvelles tentatives en cas d'échec.

# Logique des nouvelles tentatives pour les exceptions
<a name="best-practices-opencypher-retry-logic"></a>

Pour toutes les exceptions qui autorisent une nouvelle tentative, il est généralement préférable d'utiliser une [stratégie de backoff exponentiel et de nouvelle tentative](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) qui allonge progressivement les temps d'attente entre les tentatives afin de mieux gérer les problèmes transitoires tels que les erreurs `ConcurrentModificationException`. Voici un exemple de modèle de backoff exponentiel et de nouvelle tentative :

```
public static void main() {
  try (Driver driver = getDriver(HOST_BOLT, getDefaultConfig())) {
    retriableOperation(driver, "CREATE (n {prop:'1'})")
        .withRetries(5)
        .withExponentialBackoff(true)
        .maxWaitTimeInMilliSec(500)
        .call();
  }
}

protected RetryableWrapper retriableOperation(final Driver driver, final String query){
  return new RetryableWrapper<Void>() {
    @Override
    public Void submit() {
      log.info("Performing graph Operation in a retry manner......");
      try (Session session = driver.session(writeSessionConfig)) {
        try (Transaction trx =  session.beginTransaction()) {
            trx.run(query).consume();
            trx.commit();
        }
      }
      return null;
    }

    @Override
    public boolean isRetryable(Exception e) {
      if (isCME(e)) {
        log.debug("Retrying on exception.... {}", e);
        return true;
      }
      return false;
    }

    private boolean isCME(Exception ex) {
      return ex.getMessage().contains("Operation failed due to conflicting concurrent operations");
    }
  };
}



/**
 * Wrapper which can retry on certain condition. Client can retry operation using this class.
 */
@Log4j2
@Getter
public abstract class RetryableWrapper<T> {

  private long retries = 5;
  private long maxWaitTimeInSec = 1;
  private boolean exponentialBackoff = true;

  /**
   * Override the method with custom implementation, which will be called in retryable block.
   */
  public abstract T submit() throws Exception;

  /**
   * Override with custom logic, on which exception to retry with.
   */
  public abstract boolean isRetryable(final Exception e);

  /**
   * Define the number of retries.
   *
   * @param retries -no of retries.
   */
  public RetryableWrapper<T> withRetries(final long retries) {
    this.retries = retries;
    return this;
  }

  /**
   * Max wait time before making the next call.
   *
   * @param time - max polling interval.
   */
  public RetryableWrapper<T> maxWaitTimeInMilliSec(final long time) {
    this.maxWaitTimeInSec = time;
    return this;
  }

  /**
   * ExponentialBackoff coefficient.
   */
  public RetryableWrapper<T> withExponentialBackoff(final boolean expo) {
    this.exponentialBackoff = expo;
    return this;
  }

  /**
   * Call client method which is wrapped in submit method.
   */
  public T call() throws Exception {
    int count = 0;
    Exception exceptionForMitigationPurpose = null;
    do {
      final long waitTime = exponentialBackoff ? Math.min(getWaitTimeExp(retries), maxWaitTimeInSec) : 0;
      try {
          return submit();
      } catch (Exception e) {
        exceptionForMitigationPurpose = e;
        if (isRetryable(e) && count < retries) {
          Thread.sleep(waitTime);
          log.debug("Retrying on exception attempt - {} on exception cause - {}", count, e.getMessage());
        } else if (!isRetryable(e)) {
          log.error(e.getMessage());
          throw new RuntimeException(e);
        }
      }
    } while (++count < retries);

    throw new IOException(String.format(
          "Retry was unsuccessful.... attempts %d. Hence throwing exception " + "back to the caller...", count),
          exceptionForMitigationPurpose);
  }

  /*
   * Returns the next wait interval, in milliseconds, using an exponential backoff
   * algorithm.
   */
  private long getWaitTimeExp(final long retryCount) {
    if (0 == retryCount) {
      return 0;
    }
    return ((long) Math.pow(2, retryCount) * 100L);
  }
}
```

# Définissez plusieurs propriétés à la fois à l'aide d'une seule clause SET
<a name="best-practices-content-0"></a>

 Au lieu d'utiliser plusieurs clauses SET pour définir des propriétés individuelles, utilisez une carte pour définir simultanément plusieurs propriétés pour une entité. 

 Vous pouvez utiliser : 

```
MATCH (n:SomeLabel {`~id`: 'id1'})
SET n += {property1 : 'value1',
property2 : 'value2',
property3 : 'value3'}
```

 Au lieu de : 

```
MATCH (n:SomeLabel {`~id`: 'id1'})
SET n.property1 = 'value1'
SET n.property2 = 'value2'
SET n.property3 = 'value3'
```

 La clause SET accepte soit une propriété unique, soit une carte. Si vous mettez à jour plusieurs propriétés sur une seule entité, l'utilisation d'une seule clause SET avec une carte permet d'effectuer les mises à jour en une seule opération au lieu de plusieurs opérations, qui peuvent être exécutées plus efficacement. 

## Utilisez la clause SET pour supprimer plusieurs propriétés à la fois
<a name="best-practices-content-1"></a>

 Lorsque vous utilisez le langage OpenCypher, REMOVE est utilisé pour supprimer les propriétés d'une entité. Dans Neptune, chaque propriété supprimée nécessite une opération distincte, ce qui ajoute de la latence aux requêtes. Vous pouvez plutôt utiliser SET avec une carte pour définir les valeurs de toutes les propriétés`null`, ce qui, dans Neptune, revient à supprimer des propriétés. Neptune bénéficiera de performances accrues lorsque plusieurs propriétés d'une même entité doivent être supprimées. 

Utilisez :

```
WITH {prop1: null, prop2: null, prop3: null} as propertiesToRemove 
MATCH (n) 
SET n += propertiesToRemove
```

Au lieu de :

```
MATCH (n) 
REMOVE n.prop1, n.prop2, n.prop3
```

# Utilisation des requêtes paramétrées
<a name="best-practices-content-2"></a>

 Il est recommandé de toujours utiliser des requêtes paramétrées lorsque vous utilisez OpenCypher. Le moteur de requêtes peut exploiter des requêtes paramétrées répétées pour des fonctionnalités telles que le cache de plans de requêtes, où l'invocation répétée de la même structure paramétrée avec des paramètres différents peut tirer parti des plans mis en cache. Le plan de requête généré pour les requêtes paramétrées est mis en cache et réutilisé uniquement lorsqu'il est terminé dans les 100 ms et que les types de paramètres sont NUMBER, BOOLEAN ou STRING. 

Utilisez :

```
MATCH (n:foo) WHERE id(n) = $id RETURN n
```

Avec paramètres :

```
parameters={"id": "first"}
parameters={"id": "second"}
parameters={"id": "third"}
```

Au lieu de :

```
MATCH (n:foo) WHERE id(n) = "first" RETURN n
MATCH (n:foo) WHERE id(n) = "second" RETURN n
MATCH (n:foo) WHERE id(n) = "third" RETURN n
```

# Utilisez des cartes aplaties au lieu de cartes imbriquées dans la clause UNWIND
<a name="best-practices-content-3"></a>

 Une structure profondément imbriquée peut limiter la capacité du moteur de requêtes à générer un plan de requête optimal. Pour pallier partiellement ce problème, les modèles définis ci-dessous créeront des plans optimaux pour les scénarios suivants : 
+  Scénario 1 : DÉTENDEZ-VOUS avec une liste de littéraux chiffrés, qui inclut NUMBER, STRING et BOOLEAN. 
+  Scénario 2 : DÉTENDEZ-VOUS avec une liste de cartes aplaties, qui inclut uniquement des littéraux chiffrés (NUMBER, STRING, BOOLEAN) comme valeurs. 

 Lorsque vous rédigez une requête contenant la clause UNWIND, suivez les recommandations ci-dessus pour améliorer les performances. 

Exemple de scénario 1 :

```
UNWIND $ids as x
MATCH(t:ticket {`~id`: x})
```

Avec paramètres :

```
parameters={
  "ids": [1, 2, 3]
}
```

 Un exemple pour le scénario 2 consiste à générer une liste de nœuds à CRÉER ou à FUSIONNER. Au lieu d'émettre plusieurs instructions, utilisez le modèle suivant pour définir les propriétés sous la forme d'un ensemble de cartes aplaties : 

```
UNWIND $props as p
CREATE(t:ticket {title: p.title, severity:p.severity})
```

Avec paramètres :

```
parameters={
  "props": [
    {"title": "food poisoning", "severity": "2"},
    {"title": "Simone is in office", "severity": "3"}
  ]
}
```

Au lieu d'objets de nœuds imbriqués tels que :

```
UNWIND $nodes as n
CREATE(t:ticket n.properties)
```

Avec paramètres :

```
parameters={
  "nodes": [
    {"id": "ticket1", "properties": {"title": "food poisoning", "severity": "2"}},
    {"id": "ticket2", "properties": {"title": "Simone is in office", "severity": "3"}}
  ]
}
```

# Placez des nœuds plus restrictifs sur le côté gauche dans les expressions VLP (Variable-Length Path)
<a name="best-practices-content-4"></a>

 Dans les requêtes VLP (Variable-Length Path), le moteur de requêtes optimise l'évaluation en choisissant de démarrer la traversée à gauche ou à droite de l'expression. La décision est basée sur la cardinalité des motifs à gauche et à droite. La cardinalité est le nombre de nœuds correspondant au modèle spécifié. 
+  Si le motif droit a une cardinalité de un, le côté droit sera le point de départ. 
+  Si le côté gauche et le côté droit ont une cardinalité égale à un, l'expansion est vérifiée des deux côtés et commence du côté dont l'expansion est la plus faible. L'extension est le nombre d'arêtes sortantes ou entrantes pour le nœud de gauche et le nœud de droite de l'expression VLP. Cette partie de l'optimisation n'est utilisée que si la relation VLP est unidirectionnelle et si le type de relation est fourni. 
+  Sinon, le côté gauche sera le point de départ. 

 Pour une chaîne d'expressions VLP, cette optimisation ne peut être appliquée qu'à la première expression. Les autres VLPs sont évalués en commençant par le côté gauche. Par exemple, supposons que la cardinalité de (a), (b) soit un et que la cardinalité de (c) soit supérieure à un. 
+  `(a)-[*1..]->(c)`: L'évaluation commence par (a). 
+  `(c)-[*1..]->(a)`: L'évaluation commence par (a). 
+  `(a)-[*1..]-(c)`: L'évaluation commence par (a). 
+  `(c)-[*1..]-(a)`: L'évaluation commence par (a). 

 Supposons maintenant que les arêtes entrantes de (a) soient deux, les arêtes sortantes de (a) trois, les arêtes entrantes de (b) quatre et les arêtes sortantes de (b) cinq. 
+  `(a)-[*1..]->(b)`: L'évaluation commence par (a) car les arêtes sortantes de (a) sont inférieures aux arêtes entrantes de (b). 
+  `(a)<-[*1..]-(b)`: L'évaluation commence par (a) car les arêtes entrantes de (a) sont inférieures aux arêtes sortantes de (b). 

 En règle générale, placez le modèle le plus restrictif sur le côté gauche d'une expression VLP. 

# Évitez les vérifications redondantes des étiquettes des nœuds en utilisant des noms de relations granulaires
<a name="best-practices-content-5"></a>

 Lors de l'optimisation des performances, l'utilisation d'étiquettes de relation exclusives aux modèles de nœuds permet de supprimer le filtrage des étiquettes sur les nœuds. Prenons l'exemple d'un modèle de graphe dans lequel la relation `likes` est uniquement utilisée pour définir une relation entre deux `person` nœuds. Nous pourrions écrire la requête suivante pour trouver ce modèle : 

```
MATCH (n:person)-[:likes]->(m:person)
RETURN n, m
```

 La vérification des `person` étiquettes sur n et m est redondante, car nous avons défini la relation de manière à ce qu'elle n'apparaisse que lorsque les deux sont du même type`person`. Pour optimiser les performances, nous pouvons écrire la requête comme suit : 

```
MATCH (n)-[:likes]->(m)
RETURN n, m
```

 Ce modèle peut également s'appliquer lorsque les propriétés sont exclusives à une étiquette de nœud unique. Supposons que seuls `person` les nœuds possèdent cette propriété`email`. Il est donc redondant de vérifier que l'étiquette du nœud `person` correspond. Écrire cette requête sous la forme : 

```
MATCH (n:person)
WHERE n.email = 'xxx@gmail.com'
RETURN n
```

 C'est moins efficace que d'écrire cette requête sous la forme : 

```
MATCH (n)
WHERE n.email = 'xxx@gmail.com'
RETURN n
```

 Vous ne devez adopter ce modèle que lorsque les performances sont importantes et que votre processus de modélisation est contrôlé pour vous assurer que ces étiquettes de bord ne sont pas réutilisées pour des modèles impliquant d'autres étiquettes de nœuds. Si vous introduisez ultérieurement une `email` propriété sur une autre étiquette de nœud`company`, par exemple, les résultats seront différents entre ces deux versions de la requête. 

# Spécifiez les étiquettes de bord dans la mesure du possible
<a name="best-practices-content-6"></a>

 Il est recommandé de fournir une étiquette de bord lorsque cela est possible lorsque vous spécifiez une arête dans un motif. Prenons l'exemple de requête suivant, qui est utilisé pour relier toutes les personnes vivant dans une ville à toutes les personnes qui ont visité cette ville. 

```
MATCH (person)-->(city {country: "US"})-->(anotherPerson)
RETURN person, anotherPerson
```

 Si votre modèle de graphe relie des personnes à des nœuds autres que des villes à l'aide de plusieurs étiquettes de bord, Neptune devra évaluer d'autres chemins qui seront ensuite supprimés en omettant de spécifier l'étiquette finale. Dans la requête ci-dessus, aucune étiquette de bord n'ayant été donnée, le moteur effectue d'abord un travail supplémentaire, puis filtre les valeurs pour obtenir le résultat correct. Une meilleure version de la requête ci-dessus pourrait être : 

```
MATCH (person)-[:livesIn]->(city {country: "US"})-[:visitedBy]->(anotherPerson)
RETURN person, anotherPerson
```

 Cela facilite non seulement l'évaluation, mais permet également au planificateur de requêtes de créer de meilleurs plans. Vous pouvez même associer cette bonne pratique à des vérifications redondantes de l'étiquette des nœuds pour supprimer la vérification de l'étiquette de la ville et écrire la requête sous la forme suivante : 

```
MATCH (person)-[:livesIn]->({country: "US"})-[:visitedBy]->(anotherPerson)
RETURN person, anotherPerson
```

# Évitez d'utiliser la clause WITH dans la mesure du possible
<a name="best-practices-content-7"></a>

 La clause WITH d'OpenCypher agit comme une limite où tout ce qui se trouve avant son exécution, puis les valeurs qui en résultent, sont transmis aux parties restantes de la requête. La clause WITH est nécessaire lorsque vous avez besoin d'une agrégation temporaire ou que vous souhaitez limiter le nombre de résultats, mais en dehors de cela, vous devez essayer d'éviter d'utiliser la clause WITH. Le conseil général est de supprimer ces simples clauses WITH (sans agrégation, ordre par ou limite) pour permettre au planificateur de requêtes de travailler sur l'ensemble de la requête afin de créer un plan global optimal. Par exemple, supposons que vous ayez écrit une requête pour renvoyer toutes les personnes vivant dans `India` : 

```
MATCH (person)-[:lives_in]->(city)
WITH person, city
MATCH (city)-[:part_of]->(country {name: 'India'})
RETURN collect(person) AS result
```

 Dans la version ci-dessus, la clause WITH restreint le placement du motif `(city)-[:part_of]->(country {name: 'India'})` (ce qui est plus restrictif) avant`(person)-[:lives_in]->(city)`. Cela rend le plan sous-optimal. Une optimisation de cette requête serait de supprimer la clause WITH et de laisser le planificateur calculer le meilleur plan. 

```
MATCH (person)-[:lives_in]->(city)
MATCH (city)-[:part_of]->(country {name: 'India'})
RETURN collect(person) AS result
```

# Placez les filtres restrictifs le plus tôt possible dans la requête
<a name="best-practices-content-8"></a>

 Dans tous les scénarios, le placement précoce de filtres dans la requête permet de réduire les solutions intermédiaires qu'un plan de requête doit prendre en compte. Cela signifie que moins de mémoire et de ressources de calcul sont nécessaires pour exécuter la requête. 

 L'exemple suivant vous aide à comprendre ces impacts. Supposons que vous écriviez une requête pour renvoyer toutes les personnes qui y vivent`India`. L'une des versions de la requête peut être : 

```
MATCH (n)-[:lives_in]->(city)-[:part_of]->(country)
WITH country, collect(n.firstName + " "  + n.lastName) AS result
WHERE country.name = 'India'
RETURN result
```

 La version ci-dessus de la requête n'est pas le moyen le plus optimal pour réaliser ce cas d'utilisation. Le filtre `country.name = 'India'` apparaît ultérieurement dans le modèle de requête. Il collectera d'abord toutes les personnes et leur lieu de résidence, puis les regroupera par pays, puis filtrera uniquement pour le groupe pour`country.name = India`. La méthode optimale pour interroger uniquement les personnes vivant sur place, `India` puis effectuer l'agrégation des collectes. 

```
MATCH (n)-[:lives_in]->(city)-[:part_of]->(country)
WHERE country.name = 'India'
RETURN collect(n.firstName + " "  + n.lastName) AS result
```

 Une règle générale consiste à placer un filtre dès que possible après l'introduction de la variable. 

# Vérifiez explicitement si les propriétés existent
<a name="best-practices-content-9"></a>

 Selon la sémantique d'OpenCypher, l'accès à une propriété équivaut à une jointure facultative et doit conserver toutes les lignes même si la propriété n'existe pas. Si vous savez, sur la base de votre schéma graphique, qu'une propriété particulière existera toujours pour cette entité, le fait de vérifier explicitement l'existence de cette propriété permet au moteur de requêtes de créer des plans optimaux et d'améliorer les performances. 

 Prenons l'exemple d'un modèle de graphe dans lequel les nœuds de type ont `person` toujours une propriété`name`. Au lieu de faire ceci : 

```
MATCH (n:person)
RETURN n.name
```

 Vérifiez explicitement l'existence de la propriété dans la requête avec une vérification IS NOT NULL : 

```
MATCH (n:person)
WHERE n.name IS NOT NULL
RETURN n.name
```

# N'utilisez pas de chemin nommé (sauf si cela est obligatoire)
<a name="best-practices-content-10"></a>

 Le chemin indiqué dans une requête entraîne toujours un coût supplémentaire, ce qui peut entraîner des pénalités en termes de latence et d'utilisation de la mémoire plus élevées. Considérons la requête suivante : 

```
MATCH p = (n)-[:commentedOn]->(m)
WITH p, m, n, n.score + m.score as total
WHERE total > 100 
MATCH (m)-[:commentedON]->(o)
WITH p, m, n, distinct(o) as o1
RETURN p, m.name, n.name, o1.name
```

 Dans la requête ci-dessus, en supposant que nous voulions uniquement connaître les propriétés des nœuds, l'utilisation du chemin « p » n'est pas nécessaire. En spécifiant le chemin nommé sous forme de variable, l'opération d'agrégation utilisant DISTINCT deviendra coûteuse à la fois en termes de temps et d'utilisation de la mémoire. Une version plus optimisée de la requête ci-dessus pourrait être : 

```
MATCH (n)-[:commentedOn]->(m)
WITH m, n, n.score + m.score as total
WHERE total > 100 
MATCH (m)-[:commentedON]->(o)
WITH m, n, distinct(o) as o1
RETURN m.name, n.name, o1.name
```

# Évitez COLLECT (DISTINCT ())
<a name="best-practices-content-11"></a>

**Note**  
À partir de la version [1.4.7.0](engine-releases-1.4.7.0.md) du moteur, cette réécriture recommandée n'est plus nécessaire.

 COLLECT (DISTINCT ()) est utilisé chaque fois qu'une liste contenant des valeurs distinctes doit être formée. COLLECT est une fonction d'agrégation, et le regroupement est effectué sur la base de clés supplémentaires projetées dans la même instruction. Lorsque distinct est utilisé, l'entrée est divisée en plusieurs segments, chaque segment désignant un groupe à réduire. Les performances seront affectées à mesure que le nombre de groupes augmentera. Dans Neptune, il est beaucoup plus efficace d'exécuter DISTINCT avant collecting/forming la liste. Cela permet d'effectuer le regroupement directement sur les touches de regroupement pour l'ensemble du morceau. 

 Considérons la requête suivante : 

```
MATCH (n:Person)-[:commented_on]->(p:Post)
WITH n, collect(distinct(p.post_id)) as post_list
RETURN n, post_list
```

 Une manière plus optimale d'écrire cette requête est la suivante : 

```
MATCH (n:Person)-[:commented_on]->(p:Post)
WITH DISTINCT n, p.post_id as postId
WITH n, collect(postId) as post_list
RETURN n, post_list
```

# Préférez la fonction de propriétés à la recherche de propriétés individuelle lors de la récupération de toutes les valeurs de propriété
<a name="best-practices-content-12"></a>

 La `properties()` fonction est utilisée pour renvoyer une carte contenant toutes les propriétés d'une entité, et elle est bien plus efficace que de renvoyer des propriétés individuellement. 

 En supposant que vos `Person` nœuds contiennent 5 propriétés `firstName` `lastName``age`,,`dept`,`company`, et que la requête suivante serait préférable : 

```
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN properties(n) as personDetails
```

 Plutôt que d'utiliser : 

```
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN n.firstName, n.lastName, n.age, n.dept, n.company
    
=== OR ===
    
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN {firstName: n.firstName, lastName: n.lastName, age: n.age, 
department: n.dept, company: n.company} as personDetails
```

# Effectuer des calculs statiques en dehors de la requête
<a name="best-practices-content-13"></a>

 Il est recommandé de résoudre les calculs statiques ( mathematical/string opérations simples) côté client. Prenons cet exemple où vous souhaitez rechercher toutes les personnes âgées d'un an ou moins que l'auteur : 

```
MATCH (m:Message)-[:HAS_CREATOR]->(p:person)
WHERE p.age <= ($age + 1)
RETURN m
```

 Ici, `$age` est injecté dans la requête via des paramètres, puis ajouté à une valeur fixe. Cette valeur est ensuite comparée à`p.age`. Au lieu de cela, une meilleure approche consisterait à effectuer l'ajout côté client et à transmettre la valeur calculée sous forme de paramètre \$1ageplusone. Cela permet au moteur de requêtes de créer des plans optimisés et d'éviter les calculs statiques pour chaque ligne entrante. En suivant ces directives, une version plus efficace de la requête serait : 

```
MATCH (m:Message)-[:HAS_CREATOR]->(p:person)
WHERE p.age <= $ageplusone
RETURN m
```

# Entrées par lots utilisant UNWIND au lieu d'instructions individuelles
<a name="best-practices-content-14"></a>

 Chaque fois que la même requête doit être exécutée pour différentes entrées, au lieu d'exécuter une requête par entrée, il serait beaucoup plus performant d'exécuter une requête pour un lot d'entrées. 

 Si vous souhaitez effectuer une fusion sur un ensemble de nœuds, l'une des options consiste à exécuter une requête de fusion par entrée : 

```
MERGE (n:Person {`~id`: $id})
SET n.name = $name, n.age = $age, n.employer = $employer
```

 Avec paramètres : 

```
params = {id: '1', name: 'john', age: 25, employer: 'Amazon'}
```

 La requête ci-dessus doit être exécutée pour chaque entrée. Bien que cette approche fonctionne, elle peut nécessiter l'exécution de nombreuses requêtes pour un grand nombre d'entrées. Dans ce scénario, le traitement par lots peut contribuer à réduire le nombre de requêtes exécutées sur le serveur et à améliorer le débit global. 

 Utilisez le modèle suivant : 

```
UNWIND $persons as person
MERGE (n:Person {`~id`: person.id})
SET n += person
```

 Avec paramètres : 

```
params = {persons: [{id: '1', name: 'john', age: 25, employer: 'Amazon'}, 
{id: '2', name: 'jack', age: 28, employer: 'Amazon'},
{id: '3', name: 'alice', age: 24, employer: 'Amazon'}...]}
```

 Il est recommandé d'expérimenter avec différentes tailles de lots afin de déterminer ce qui convient le mieux à votre charge de travail. 

# Préférez utiliser la personnalisation IDs pour le nœud ou la relation
<a name="best-practices-content-15"></a>

 Neptune permet aux utilisateurs d'attribuer des nœuds et des relations IDs de manière explicite. L'identifiant doit être globalement unique dans l'ensemble de données et déterministe pour être utile. Un identifiant déterministe peut être utilisé comme mécanisme de recherche ou de filtrage, tout comme les propriétés ; toutefois, l'utilisation d'un identifiant est beaucoup plus optimisée du point de vue de l'exécution des requêtes que celle des propriétés. L'utilisation de la personnalisation présente plusieurs avantages IDs  : 
+  Les propriétés peuvent être nulles pour une entité existante, mais l'ID doit exister. Cela permet au moteur de requête d'utiliser une jointure optimisée lors de l'exécution. 
+  Lorsque des requêtes de mutation simultanées sont exécutées, les risques d'[exceptions de modification simultanées](https://docs.aws.amazon.com//neptune/latest/userguide/transactions-exceptions.html) (CMEs) sont considérablement réduits lorsqu'elles IDs sont utilisées pour accéder à des nœuds, car le nombre de verrous requis est inférieur à IDs celui des propriétés en raison de leur caractère unique imposé. 
+  L'utilisation IDs permet d'éviter de créer des doublons de données, car Neptune impose l'unicité, contrairement aux IDs propriétés. 

 L'exemple de requête suivant utilise un identifiant personnalisé : 

**Note**  
 La propriété `~id` est utilisée pour spécifier l'ID, alors qu'elle `id` est simplement stockée comme n'importe quelle autre propriété. 

```
CREATE (n:Person {`~id`: '1', name: 'alice'})
```

 Sans utiliser d'identifiant personnalisé : 

```
CREATE (n:Person {id: '1', name: 'alice'})
```

 Si vous utilisez ce dernier mécanisme, il n'y a pas d'application de l'unicité et vous pouvez exécuter la requête ultérieurement : 

```
CREATE (n:Person {id: '1', name: 'john'})
```

 Cela crée un deuxième nœud `id=1` nommé`john`. Dans ce scénario, vous auriez maintenant deux nœuds portant `id=1` chacun un nom différent - (alice et john). 

# Évitez de faire des calculs \$1id dans la requête
<a name="best-practices-content-16"></a>

 Lorsque vous utilisez la personnalisation IDs dans les requêtes, effectuez toujours des calculs statiques en dehors des requêtes et fournissez ces valeurs dans les paramètres. Lorsque des valeurs statiques sont fournies, le moteur est mieux à même d'optimiser les recherches et d'éviter de scanner et de filtrer ces valeurs. 

 Si vous souhaitez créer des limites entre les nœuds existants dans la base de données, l'une des options suivantes peut être la suivante : 

```
UNWIND $sections as section
MATCH (s:Section {`~id`: 'Sec-' + section.id})
MERGE (s)-[:IS_PART_OF]->(g:Group {`~id`: 'g1'})
```

 Avec paramètres : 

```
parameters={sections: [{id: '1'}, {id: '2'}]}
```

 Dans la requête ci-dessus, `id` la section est calculée dans la requête. Comme le calcul est dynamique, le moteur ne peut pas insérer les identifiants de manière statique et finit par scanner tous les nœuds de section. Le moteur effectue ensuite un post-filtrage pour les nœuds requis. Cela peut être coûteux si la base de données contient de nombreux nœuds de section. 

 Une meilleure façon d'y parvenir est d'ajouter au `Sec-` préalable les identifiants transmis à la base de données : 

```
UNWIND $sections as section
MATCH (s:Section {`~id`: section.id})
MERGE (s)-[:IS_PART_OF]->(g:Group {`~id`: 'g1'})
```

 Avec paramètres : 

```
parameters={sections: [{id: 'Sec-1'}, {id: 'Sec-2'}]}
```

# Mise à jour/fusion de plusieurs nœuds
<a name="best-practices-merge-multiple-nodes"></a>

 Lorsque vous exécutez `MERGE` ou `CREATE` interrogez plusieurs nœuds, il est recommandé d'utiliser une `UNWIND` clause combinée avec une seule MERGE/CREATE clause plutôt qu'une MERGE/CREATE clause pour chaque nœud. Les requêtes qui utilisent une clause pour un nœud entraînent un plan d'exécution inefficace car chaque ligne doit être optimisée. Cela signifie que la majeure partie du temps d'exécution de la requête est consacrée au traitement statique plutôt qu'à la mise à jour proprement dite. 

 Une clause par nœud n'est pas optimale car elle ne s'adapte pas à l'augmentation du nombre de nœuds : 

```
MERGE (p1:Person {name: 'NameA'})
ON CREATE SET p1 += {prop1: 'prop1V1', prop2: 'prop2V1'}
MERGE (p2:Person {name: 'NameB'})
ON CREATE SET p2 += {prop1: 'prop1V2', prop2: 'prop2V2'}
MERGE (p3:Person {name: 'NameC'})
ON CREATE SET p3 += {prop1: 'prop1V3', prop2: 'prop1V3'}
```

 L'utilisation d'une clause `UNWIND` en conjonction avec une MERGE/CREATE clause permet d'obtenir le même comportement, mais un plan d'exécution plus optimal. Dans cet esprit, la requête modifiée ressemblerait à ce qui suit : 

```
## If not using custom id for nodes/relationship
UNWIND [{name: 'NameA', prop1: 'prop1V1', prop2: 'prop2V1'}, {name: 'NameB', prop1: 'prop1V2', prop2: 'prop2V2'}, {name: 'NameC', prop1: 'prop1V3', prop2: 'prop1V3'}] AS props
MERGE (p:Person {name: props.name})
ON CREATE SET p = props

## If using custom id for nodes/relationship
UNWIND [{`~id`: '1', 'name': 'NameA', 'prop1: 'prop1V1', prop2: 'prop2V1'}, {`~id`: '2', name: 'NameB', prop1: 'prop1V2', prop2: 'prop2V2'}, {`~id`: '3', name: 'NameC', prop1: 'prop1V3', prop2: 'prop1V3'}] AS props
MERGE (p:Person {`~id`: props.id})
ON CREATE SET p = removeKeyFromMap(props, '~id')
```

# Bonnes pratiques Neptune avec l'utilisation de SPARQL
<a name="best-practices-sparql"></a>

Suivez ces bonnes pratiques lorsque vous utilisez le langage de requête SPARQL avec Neptune. Pour plus d'informations sur l'utilisation de SPARQL dans Neptune, consultez [Accès au graphe Neptune avec SPARQL](access-graph-sparql.md).

**Topics**
+ [Interrogation de tous les graphes par défaut](best-practices-sparql-query.md)
+ [Spécification d'un graphe nommé pour le chargement](best-practices-sparql-graph.md)
+ [Choisir entre FILTER, FILTER...IN et VALUES dans vos requêtes](best-practices-sparql-batch.md)

# Interrogation de tous les graphes par défaut
<a name="best-practices-sparql-query"></a>

Amazon Neptune associe chaque triplet à un graphe nommé. Le graphe par défaut est défini comme l'union de tous les graphes nommés. 

Si vous envoyez une requête SPARQL sans spécifier explicitement un graphe via le mot-clé `GRAPH` ou des constructions telles que `FROM NAMED`, Neptune prend toujours en compte tous les triplets dans votre instance de base de données. Par exemple, la requête suivante renvoie tous les triplets à partir d'un point de terminaison Neptune SPARQL : 

```
SELECT * WHERE { ?s ?p ?o }
```

Les triplets qui apparaissent dans plusieurs graphes ne sont renvoyés qu'une seule fois.

Pour plus d'informations sur la spécification du graphe par défaut, consultez la section [RDF Dataset](https://www.w3.org/TR/sparql11-query/#rdfDataset) dans la spécification de langage de requête SPARQL 1.1.

# Spécification d'un graphe nommé pour le chargement
<a name="best-practices-sparql-graph"></a>

Amazon Neptune associe chaque triplet à un graphe nommé. Si vous ne spécifiez pas un graphe nommé lors du chargement, de l'insertion ou de la mise à jour de triplets, Neptune utilise le graphe nommé de secours défini par l'URI, `http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph`.

Si vous utilisez le chargeur en bloc Neptune, vous pouvez spécifier le graphe nommé pour utiliser tous les triplets (ou quadrilatères avec la quatrième position vide) à l'aide du paramètre `parserConfiguration: namedGraphUri`. Pour plus d'informations sur la syntaxe de la commande `Load` du chargeur Neptune, consultez [Commande de chargeur Neptune](load-api-reference-load.md).

# Choisir entre FILTER, FILTER...IN et VALUES dans vos requêtes
<a name="best-practices-sparql-batch"></a>

Il existe trois façons de base d'injecter des valeurs dans les requêtes SPARQL :   `FILTER`,   `FILTER...IN` et `VALUES`.

Par exemple, imaginons que vous souhaitiez rechercher les amis de plusieurs personnes dans une seule requête. En utilisant `FILTER`, vous pouvez structurer votre requête comme suit :

```
  PREFIX ex: <https://www.example.com/>
  PREFIX foaf : <http://xmlns.com/foaf/0.1/>

  SELECT ?s ?o
  WHERE {?s foaf:knows ?o. FILTER (?s = ex:person1 || ?s = ex:person2)}
```

Cette opération renvoie tous les triplets du graphe dans lesquels `?s` est lié à `ex:person1` ou `ex:person2` et qui ont un arc sortant intitulé `foaf:knows`.

Vous pouvez également créer une requête en utilisant `FILTER...IN` qui renvoie des résultats équivalents :

```
  PREFIX ex: <https://www.example.com/>
  PREFIX foaf : <http://xmlns.com/foaf/0.1/>

  SELECT ?s ?o
  WHERE {?s foaf:knows ?o. FILTER (?s IN (ex:person1, ex:person2))}
```

Vous pouvez également créer une requête en utilisant `VALUES` qui, dans ce cas, renvoie également des résultats équivalents :

```
  PREFIX ex: <https://www.example.com/>
  PREFIX foaf : <http://xmlns.com/foaf/0.1/>

  SELECT ?s ?o
  WHERE {?s foaf:knows ?o. VALUES ?s {ex:person1 ex:person2}}
```

Bien que, dans bien des cas, ces requêtes soient équivalentes sémantiquement, il y a certains cas où les deux variantes `FILTER` diffèrent de la variante `VALUES` :
+ Le premier cas est lorsque vous injectez des valeurs en double, telles que l'injection de la même personne deux fois. Dans ce cas, la requête `VALUES` inclut les doublons dans votre résultat. Vous pouvez explicitement éliminer ces doublons en ajoutant un `DISTINCT` à la clause `SELECT`. Toutefois, dans certaines situations, vous souhaiterez peut-être conserver les doublons dans les résultats des requêtes à des fins d’injection des valeurs redondantes.

  Cependant, les versions `FILTER...IN` et `FILTER` extraient la valeur une seule fois lorsque la même valeur apparaît plusieurs fois.
+ Le deuxième cas est lié au fait que `VALUES` génère toujours une correspondance exacte, alors que `FILTER` pourrait appliquer une promotion de type et réaliser des correspondances partielles dans certains cas.

  Par exemple, lorsque vous incluez une valeur littérale comme `"2.0"^^xsd:float` dans votre clause VALUES, une requête `VALUES` établit une correspondance exacte avec cette valeur littérale, y compris la valeur littérale et le type de données.

  En revanche, `FILTER` produit une correspondance partielle pour ces littéraux numériques. Les correspondances peuvent inclure des littéraux avec la même valeur mais des types de données numériques, par exemple `xsd:double`.
**Note**  
Il n'y a aucune différence entre le `VALUES` comportement `FILTER` et lors de l'énumération des littéraux de chaîne ou. URIs

Les différences entre `FILTER` et `VALUES` peuvent affecter l'optimisation et la stratégie d'évaluation de requête qui en résulte. Sauf si votre cas d'utilisation nécessite des correspondances partielles, nous vous recommandons d'utiliser `VALUES` car cela évite d’examiner les cas spéciaux relatifs au transtypage. En conséquence, `VALUES` produit souvent une requête plus efficace qui s'exécute plus rapidement et qui coûte moins cher.