

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Prácticas recomendadas: sacar el máximo partido de Neptune
<a name="best-practices"></a>

Estas son algunas recomendaciones generales para trabajar con Amazon Neptune. Utilice esta información como referencia para encontrar rápidamente recomendaciones para usar Amazon Neptune y maximizar el rendimiento.

**Contents**
+ [Directrices operativas básicas de Amazon Neptune](best-practices-general-basic.md)
  + [Prácticas recomendadas de seguridad de Amazon Neptune](best-practices-general-security.md)
  + [Evite diferentes clases de instancia en un clúster](best-practices-general-basic.md#best-practices-loader-heterogeneous-instances)
  + [Evite reinicios repetidos durante la carga masiva](best-practices-general-basic.md#best-practices-loader-repeated-restarts)
  + [Habilite el índice OSGP si tiene un gran número de predicados](best-practices-general-basic.md#best-practices-general-predicates)
  + [Evite las transacciones de larga duración siempre que sea posible](best-practices-general-basic.md#best-practices-general-long-running-transactions)
  + [Prácticas recomendadas para utilizar métricas de Neptune](best-practices-general-metrics.md)
  + [Prácticas recomendadas para el ajuste de consultas de Neptune](best-practices-general-basic.md#best-practices-general-tuning)
  + [Equilibrio de carga entre réplicas de lectura](best-practices-general-basic.md#best-practices-general-loadbalance)
  + [Carga más rápida usando una instancia temporal más grande](best-practices-general-basic.md#best-practices-loader-tempinstance)
  + [Cambie el tamaño de la instancia de escritor mediante una conmutación por error a una réplica de lectura](best-practices-general-basic.md#best-practices-resize-instance)
  + [Reintente la carga tras el error de interrupción de la tarea de obtención previa de los datos](best-practices-general-basic.md#load-api-reference-status-interrupted)
+ [Prácticas recomendadas para utilizar Gremlin con Neptune](best-practices-gremlin.md)
  + [Configuración de Heartbeat para Neptune Serverless](best-practices-gremlin-heartbeat-serverless.md)
  + [Estructure las consultas de actualización o inserción para aprovechar el motor DFE](best-practices-gremlin.md#best-practices-gremlin-upserts)
  + [Pruebe el código de Gremlin en el contexto en el que lo va a implementar](best-practices-gremlin-console-glv-differences.md)
  + [Creación de escrituras de Gremlin eficientes de múltiples subprocesos](best-practices-gremlin-multithreaded-writes.md)
  + [Depuración de registros con la propiedad de hora de creación](best-practices-gremlin-prune.md)
  + [Uso del método `datetime( )` para datos de tiempo de Groovy](best-practices-gremlin-datetime.md)
  + [Uso de la fecha y hora nativas para los datos de tiempo de GLV](best-practices-gremlin-datetime-glv.md)
+ [Prácticas recomendadas del uso del cliente Java de Gremlin con Neptune](best-practices-gremlin-java-client.md)
  + [Vuelva a utilizar el objeto del cliente en varios subprocesos](best-practices-gremlin-java-reuse.md)
  + [Cree objetos de cliente Java de Gremlin independientes para puntos de conexión de lectura y escritura](best-practices-gremlin-java-separate.md)
  + [Añada varios puntos de conexión de réplica de lectura a un grupo de conexiones Java de Gremlin](best-practices-gremlin-java-multiple.md)
  + [Cierre el cliente para evitar el límite de conexiones](best-practices-gremlin-java-close-connections.md)
  + [Cree una nueva conexión después de una conmutación por error](best-practices-gremlin-java-new-connection.md)
  + [Establezca `maxInProcessPerConnection` y `maxSimultaneousUsagePerConnection` con el mismo valor.](best-practices-gremlin-java-maxes.md)
  + [Envíe consultas al servidor como bytecode en lugar de como cadenas](best-practices-gremlin-java-bytecode.md)
  + [Consume siempre por completo el iterador ResultSet or devuelto por una consulta](best-practices-gremlin-java-resultset.md)
  + [Añada vértices y bordes de forma masiva por lotes](best-practices-gremlin-java-batch-add.md)
  + [Deshabilitar el almacenamiento en caché de DNS en la máquina virtual de Java](best-practices-gremlin-java-disable-dns-caching.md)
  + [Opcionalmente, establezca tiempos de espera al nivel de consulta](best-practices-gremlin-java-per-query-timeout.md)
  + [Solución de problemas de `java.util.concurrent.TimeoutException`](best-practices-gremlin-java-exceptions-TimeoutException.md)
+ [Prácticas recomendadas de Neptune con openCypher y Bolt](best-practices-opencypher.md)
  + [Cree una nueva conexión después de una conmutación por error](best-practices-opencypher.md#best-practices-opencypher-renew-connection)
  + [Gestión de conexiones para aplicaciones de larga duración](best-practices-opencypher.md#best-practices-opencypher-long-connections)
  + [Manejo de conexiones para AWS Lambda](best-practices-opencypher.md#best-practices-opencypher-lambda-connections)
  + [En las consultas, utilice preferentemente bordes dirigidos en lugar de bidireccionales](best-practices-opencypher-directed-edges.md)
  + [Neptune no admite múltiples consultas simultáneas en una transacción](best-practices-opencypher-multiple-queries.md)
  + [Cierre los objetos del controlador cuando haya terminado](best-practices-opencypher-close-driver.md)
  + [Use modos de transacción explícitos para leer y escribir](best-practices-opencypher-use-explicit-txs.md)
    + [Transacciones de solo lectura](best-practices-opencypher-use-explicit-txs.md#best-practices-opencypher-read-txs)
    + [Transacciones de mutación](best-practices-opencypher-use-explicit-txs.md#best-practices-opencypher-mutation-txs)
  + [Lógica de reintentos para las excepciones](best-practices-opencypher-retry-logic.md)
  + [Establecimiento de varias propiedades a la vez mediante una sola cláusula SET](best-practices-content-0.md)
    + [Uso de la cláusula SET para eliminar varias propiedades a la vez](best-practices-content-0.md#best-practices-content-1)
  + [Uso de consultas parametrizadas](best-practices-content-2.md)
  + [Uso de mapas aplanados en lugar de mapas anidados en la cláusula UNWIND](best-practices-content-3.md)
  + [Inserción de nodos más restrictivos en el lado izquierdo en expresiones de ruta de longitud variable (VLP)](best-practices-content-4.md)
  + [Elusión de comprobaciones de etiquetas de nodos redundantes mediante el uso de nombres de relación granulares](best-practices-content-5.md)
  + [Especificación de etiquetas de borde cuando sea posible](best-practices-content-6.md)
  + [Elusión de la cláusula WITH cuando sea posible](best-practices-content-7.md)
  + [Inserción de filtros restrictivos lo antes posible en la consulta](best-practices-content-8.md)
  + [Comprobación explícita de la existencia de propiedades](best-practices-content-9.md)
  + [Elusión de rutas con nombre (a menos que sea necesario)](best-practices-content-10.md)
  + [Elusión de COLLECT (DISTINCT ())](best-practices-content-11.md)
  + [Preferencia de la función de propiedades sobre la búsqueda de propiedades individuales en la recuperación de todos los valores de las propiedades](best-practices-content-12.md)
  + [Realización de cálculos estáticos fuera de la consulta](best-practices-content-13.md)
  + [Entradas por lotes mediante UNWIND en lugar de instrucciones individuales](best-practices-content-14.md)
  + [Prefiere usar la opción personalizada para el nodo o la relación IDs](best-practices-content-15.md)
  + [Elusión de cálculos \$1id en la consulta](best-practices-content-16.md)
  + [Actualización/fusión de varios nodos](best-practices-merge-multiple-nodes.md)
+ [Prácticas recomendadas de Neptune con SPARQL](best-practices-sparql.md)
  + [Consulta de todos los gráficos con nombre de forma predeterminada](best-practices-sparql-query.md)
  + [Especificación de un gráfico con nombre para la carga](best-practices-sparql-graph.md)
  + [Elegir entre FILTER, FILTER... IN y VALUES en sus consultas](best-practices-sparql-batch.md)

# Directrices operativas básicas de Amazon Neptune
<a name="best-practices-general-basic"></a>

A continuación, se detallan las directrices operativas básicas que debe seguir cuando trabaje con Neptune.
+ Conozca las instancias de base de datos de Neptune para poder dimensionarlas correctamente en función de sus requisitos de rendimiento y casos de uso. Consulte [Clústeres e instancias de base de datos de Amazon Neptune](feature-overview-db-clusters.md).
+ Monitorice el uso de la memoria y de la CPU. Esto le servirá para saber cuándo migrar a una clase de instancia de base de datos con mayor capacidad de memoria o de CPU para conseguir el rendimiento de las consultas que necesita. Puedes configurar Amazon CloudWatch para que te notifique cuando cambien los patrones de uso o cuando te acerques a la capacidad de tu implementación. Si lo hace, le podrá servir para mantener el rendimiento y la disponibilidad del sistema. Consulte [Monitorización de instancias](feature-overview-db-clusters.md#feature-overview-monitoring-instances) y [Monitorización de Neptune](monitoring.md) para obtener más información.

  Dado que Neptune tiene su propio administrador de memoria, es normal observar un uso de la memoria relativamente bajo incluso cuando se utiliza mucha CPU. Encontrar out-of-memory excepciones al ejecutar consultas es el mejor indicador de que necesita aumentar la memoria liberable.
+ Habilite las copias de seguridad automatizadas y configure la ventana de copia de seguridad para que se produzca cuando le convenga.
+ Pruebe la conmutación por error de la instancia de base de datos para comprender cuánto tiempo tarda el proceso en su caso de uso. También ayuda a asegurarse de que la aplicación que obtiene acceso a su instancia de base de datos puede conectarse automáticamente a la nueva instancia de base de datos tras una conmutación por error.
+ Si es posible, ejecute el cliente y el clúster de Neptune en la misma región y VPC, ya que las conexiones entre regiones con VPC interconexión pueden producir retrasos en los tiempos de respuesta de las consultas. Para obtener respuestas a consultas en milisegundos de un solo dígito, es necesario mantener el cliente y el clúster de Neptune en la misma región y VPC.
+ Cuando cree una instancia de réplica de lectura, esta debe tener, como mínimo, el mismo tamaño que la instancia de escritor principal. Esto ayudará a mantener el retraso de replicación bajo control y evitará los reinicios de la réplica. Consulte [Evite diferentes clases de instancia en un clúster](#best-practices-loader-heterogeneous-instances). 
+ Antes de actualizar a una nueva versión principal del motor, asegúrese de probar la aplicación en ella. Para ello, puede clonar el clúster de base de datos para que el clúster clonado ejecute la nueva versión del motor y, a continuación, probar la aplicación en el clon.
+ Para facilitar la conmutación por error, lo ideal es que todas las instancias tengan el mismo tamaño.

**Topics**
+ [Prácticas recomendadas de seguridad de Amazon Neptune](best-practices-general-security.md)
+ [Evite diferentes clases de instancia en un clúster](#best-practices-loader-heterogeneous-instances)
+ [Evite reinicios repetidos durante la carga masiva](#best-practices-loader-repeated-restarts)
+ [Habilite el índice OSGP si tiene un gran número de predicados](#best-practices-general-predicates)
+ [Evite las transacciones de larga duración siempre que sea posible](#best-practices-general-long-running-transactions)
+ [Prácticas recomendadas para utilizar métricas de Neptune](best-practices-general-metrics.md)
+ [Prácticas recomendadas para el ajuste de consultas de Neptune](#best-practices-general-tuning)
+ [Equilibrio de carga entre réplicas de lectura](#best-practices-general-loadbalance)
+ [Carga más rápida usando una instancia temporal más grande](#best-practices-loader-tempinstance)
+ [Cambie el tamaño de la instancia de escritor mediante una conmutación por error a una réplica de lectura](#best-practices-resize-instance)
+ [Reintente la carga tras el error de interrupción de la tarea de obtención previa de los datos](#load-api-reference-status-interrupted)

# Prácticas recomendadas de seguridad de Amazon Neptune
<a name="best-practices-general-security"></a>

Utilice cuentas AWS Identity and Access Management (IAM) para controlar el acceso a las acciones de la API de Neptune. Controle las acciones que crean, modifican o eliminan recursos de Neptune (como instancias de bases de datos, grupos de seguridad, grupos de opciones o grupos de parámetros) y las acciones que realizan tareas administrativas frecuentes (como realizar copias de seguridad y restaurar instancias de bases de datos).
+ Utilice credenciales temporales en lugar de persistentes siempre que sea posible.
+ Asigne una cuenta de IAM individual a cada persona que administre recursos de Amazon Relational Database Service (Amazon RDS). Nunca utilice usuarios raíz de AWS cuentas para administrar los recursos de Neptune. Cree un usuario de IAM para todos, incluido usted mismo.
+ Asigne a cada usuario el conjunto mínimo de permisos requerido para realizar sus tareas.
+ Use los grupos de IAM para administrar con eficacia los permisos para varios usuarios.
+ Rote con regularidad sus credenciales de IAM.

Para obtener más información sobre el uso de IAM para acceder a los recursos de Neptune, consulte [Protección de la base de datos de Amazon Neptune](security.md). Para obtener información general sobre cómo trabajar con IAM, consulte [AWS Identity and Access Management](https://docs.aws.amazon.com/IAM/latest/UserGuide/Welcome.html) y [Prácticas recomendadas de seguridad en IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/IAMBestPractices.html) en la *Guía del usuario de IAM*.

## Evite diferentes clases de instancia en un clúster
<a name="best-practices-loader-heterogeneous-instances"></a>

Cuando el clúster de base de datos contiene instancias de clases diferentes, pueden surgir problemas con el paso del tiempo. El problema más común es que una instancia de lector pequeña puede entrar en un ciclo de reinicios repetidos debido al retardo en la replicación. Si un nodo de lector tiene una configuración de clase de instancia de base de datos más débil que la de una instancia de base de datos de escritor, el volumen de cambios puede ser demasiado grande para que el lector se adapte al ritmo.

**importante**  
Para evitar reinicios repetidos a causa del retardo en la replicación, configure el clúster de base de datos de modo que todas las instancias tengan la misma clase (tamaño).

Puede ver el desfase entre la instancia de escritura (la principal) y los lectores de su clúster de base de datos mediante la `ClusterReplicaLag` métrica de Amazon CloudWatch. La métrica `VolumeWriteIOPs` también le permite detectar ráfagas de actividad de escritura en su clúster que pueden provocar un retardo en la replicación.

## Evite reinicios repetidos durante la carga masiva
<a name="best-practices-loader-repeated-restarts"></a>

Si experimenta un ciclo de reinicios repetidos de réplicas de lectura debido a un retardo en la replicación durante una carga masiva, es probable que sus réplicas no puedan mantener el ritmo del escritor en el clúster de base de datos.

Escale los lectores a un tamaño superior al del escritor o quítelos temporalmente durante la carga masiva y vuelva a crearlos una vez finalizada la operación.

## Habilite el índice OSGP si tiene un gran número de predicados
<a name="best-practices-general-predicates"></a>

Si su modelo de datos contiene un gran número de predicados distintos (más de mil en la mayoría de los casos), es posible que el rendimiento se reduzca y aumenten los costos operativos.

Si es así, puede mejorar el rendimiento habilitando el [índice OSGP](feature-overview-storage-indexing.md#feature-overview-storage-indexing-osgp). Consulte [El índice OSGP](features-lab-mode.md#features-lab-mode-features-osgp-index).

## Evite las transacciones de larga duración siempre que sea posible
<a name="best-practices-general-long-running-transactions"></a>

Las transacciones de larga duración, ya sean de solo lectura o de lectura y escritura, pueden provocar problemas inesperados de los siguientes tipos:

Una transacción de larga duración en una instancia de lector o de escritor con escrituras simultáneas puede provocar una gran acumulación de diferentes versiones de los datos. Esto puede dar lugar a latencias más altas en las consultas de lectura que filtran una gran parte de sus resultados.

En algunos casos, las versiones acumuladas a lo largo de varias horas pueden provocar una limitación de las escrituras nuevas.

Una transacción de lectura-escritura de larga duración con muchas escrituras también puede provocar problemas si la instancia se reinicia. Si una instancia se reinicia tras un evento de mantenimiento o un bloqueo, se revierten todas las escrituras no confirmadas. Por lo general, estas operaciones de deshacer se ejecutan en segundo plano y no impiden que la instancia vuelva a funcionar, pero cualquier escritura nueva que entre en conflicto con las operaciones que se están revirtiendo fallará.

Por ejemplo, si se vuelve a intentar la misma consulta después de interrumpir la conexión en la ejecución anterior, es posible que se produzca un error al reiniciar la instancia.

El tiempo necesario para deshacer las operaciones es proporcional al tamaño de los cambios involucrados.

# Prácticas recomendadas para utilizar métricas de Neptune
<a name="best-practices-general-metrics"></a>

Para identificar los problemas de rendimiento causados por la falta de recursos y otros cuellos de botella frecuentes, puede monitorizar las métricas disponibles para su clúster de base de datos de Neptune. 

Monitorice las métricas de desempeño con frecuencia para recopilar datos sobre los valores medios, máximos y mínimos de diversos intervalos de tiempo. Esto le servirá para identificar cuándo se degrada el rendimiento. Con estos datos, puedes configurar CloudWatch las alarmas de Amazon para determinados umbrales de métricas, de forma que recibas una alerta si se alcanzan. 

Cuando se configura un nuevo clúster de base de datos y se ejecuta con una carga de trabajo típica, intente capturar los valores medio, máximo y mínimo de todas las métricas de rendimiento en diversos intervalos (por ejemplo, una hora, 24 horas, una semana, dos semanas). Esto permite hacerse una idea de lo que es normal. Ayuda a obtener comparaciones para las horas con picos y valles de funcionamiento. Puede usar esta información para saber cuándo cae el rendimiento por debajo de los niveles estándar y puede configurar las alarmas según corresponda.

Consulte [Monitorización de Neptune con Amazon CloudWatch](cloudwatch.md) para obtener información acerca de cómo ver las métricas de Neptune.

Las métricas más importantes con las que comenzar son las siguientes:
+ **BufferCacheHitRatio**— El porcentaje de solicitudes que atiende la caché del búfer. Los errores de caché añaden una latencia significativa a la ejecución de las consultas. Si la relación de aciertos de la caché es inferior al 99,9 % y la latencia es un problema para su aplicación, considere la posibilidad de actualizar el tipo de instancia para almacenar en caché más datos en memoria.
+ **Utilización de la CPU**: porcentaje de la capacidad de procesamiento del equipo que está en uso. Unos valores elevados de consumo de CPU podrían ser adecuados, en función de los objetivos de rendimiento de las consultas.
+ **Memoria que se puede liberar**: cuánta RAM está disponible en la instancia de base de datos en megabytes. Neptune tiene su propio administrador de memoria, por lo que esta métrica podría ser inferior a la esperada. Una buena señal de que deberías considerar la posibilidad de actualizar tu clase de instancia a una con más RAM es si las consultas suelen generar out-of-memory excepciones.

La línea roja de las métricas de la pestaña **Monitoring (Monitorización)** está marcada en el 75 % para las métricas de CPU y memoria. Si el consumo de memoria de instancia supera con frecuencia esta línea, significa que debe verificar la carga de trabajo y plantearse actualizar la instancia para mejorar el rendimiento de las consultas.

## Prácticas recomendadas para el ajuste de consultas de Neptune
<a name="best-practices-general-tuning"></a>

 Una de las formas más eficaces de mejorar el rendimiento de Neptune es ajustar las consultas más utilizadas y que más recursos consumen para que ejecutarlas resulte más económico. 

Para obtener información sobre cómo ajustar las consultas de Gremlin, consulte [Sugerencias de consulta de Gremlin](gremlin-query-hints.md) y [Ajuste de las consultas de Gremlin](gremlin-traversal-tuning.md). Para obtener información sobre cómo ajustar las consultas SPARQL, consulte [Sugerencias de consulta SPARQL](sparql-query-hints.md).

## Equilibrio de carga entre réplicas de lectura
<a name="best-practices-general-loadbalance"></a>

El enrutamiento de turnos rotativos para puntos de enlace de lectura funciona mediante el cambio del host al que apunta la entrada DNS. El cliente debe crear una conexión nueva y resolver el registro DNS para poder conectarse a una nueva réplica de lectura, ya que WebSocket las conexiones suelen mantenerse activas durante períodos prolongados.

Para obtener réplicas de lectura diferentes para las solicitudes sucesivas, asegúrese de que el cliente resuelva la entrada DNS cada vez que se conecte. Esto puede requerir cerrar la conexión y volver a conectarse al punto de enlace del lector.

También puede balancear la carga de las solicitudes en réplicas de lectura conectando los puntos de enlace de instancia explícitamente.

## Carga más rápida usando una instancia temporal más grande
<a name="best-practices-loader-tempinstance"></a>

Su rendimiento de carga aumenta con tamaños de instancia más grandes. Si no utiliza un tipo de instancia grande, pero desea aumentar la velocidad de carga, puede utilizar una instancia mayor para cargarla y, a continuación, eliminarla.

**nota**  
El siguiente procedimiento es para un nuevo clúster. Si tiene un clúster existente, puede agregar una nueva instancia más grande y, a continuación, promoverla a una instancia de base de datos principal.

**Para cargar datos utilizando un tamaño de instancia mayor**

1.  Cree un clúster con una sola instancia `r5.12xlarge`. Esta instancia es la instancia de base de datos principal.

1. Cree una o varias réplicas de lectura del mismo tamaño (`r5.12xlarge`).

   Puede crear réplicas de lectura más pequeñas, pero si no son lo suficientemente grandes como para soportar las escrituras que realiza la instancia principal, es posible que tengan que reiniciarse con frecuencia. El tiempo de inactividad resultante reduce drásticamente el rendimiento.

1. En el comando de carga masiva, incluya `“parallelism” : “OVERSUBSCRIBE”` para indicarle a Neptune que utilice todos los recursos de CPU disponibles para la carga (consulte [Parámetros de solicitudes del programa de carga de Neptune](load-api-reference-load.md#load-api-reference-load-parameters)). La operación de carga se realizará entonces tan rápido como sea posible, I/O lo que generalmente requiere entre un 60 y un 70% de los recursos de la CPU.

1. Cargue sus datos mediante el programa de carga de Neptune. El trabajo de carga se ejecuta en la instancia de base de datos principal.

1. Cuando terminen de cargarse los datos, asegúrese de escalar verticalmente todas las instancias del clúster al mismo tipo de instancia para evitar cargos adicionales y problemas de reinicio repetidos (consulte [Evite distintos tamaños de instancias](#best-practices-loader-heterogeneous-instances)).

## Cambie el tamaño de la instancia de escritor mediante una conmutación por error a una réplica de lectura
<a name="best-practices-resize-instance"></a>

La mejor forma de cambiar el tamaño de una instancia del clúster de base de datos, incluida la instancia de escritor, es crear o modificar una instancia de réplica de lectura para que tenga el tamaño deseado y, a continuación, realizar una conmutación por error deliberada a esa réplica de lectura. El tiempo de inactividad registrado por la aplicación es solo el tiempo necesario para cambiar la dirección IP del escritor, que debería ser de unos 3 a 5 segundos.

La API de administración de Neptune que se utiliza para conmutar por error la instancia de escritor actual a una instancia de réplica de lectura es [FailoverDBCluster](api-clusters.md#FailoverDBCluster) de forma deliberada. Si utiliza el cliente Java de Gremlin, es posible que tenga que crear un nuevo objeto de cliente tras la conmutación por error para seleccionar la nueva dirección IP, tal y como se menciona [aquí](best-practices-gremlin-java-new-connection.md).

Asegúrese de cambiar todas las instancias al mismo tamaño para evitar un ciclo de reinicios repetidos, como se explica a continuación.

## Reintente la carga tras el error de interrupción de la tarea de obtención previa de los datos
<a name="load-api-reference-status-interrupted"></a>

Cuando esté cargando datos en Neptune mediante el programa de carga masiva, ocasionalmente puede producirse un estado `LOAD_FAILED`, con los mensajes `PARSING_ERROR` y `Data prefetch task interrupted`, en respuesta a una solicitud de información detallada, como este:

```
"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 detecta este error, solo tiene que reintentar la solicitud de carga masiva.

El error se produce si ha habido una interrupción temporal no provocada normalmente por su solicitud o sus datos y, en general, puede resolverse ejecutando de nuevo la solicitud de carga masiva.

En caso de que esté usando la configuración predeterminada, es decir, `"mode":"AUTO"` y `"failOnError":"TRUE"`, el programa de carga omite los archivos que ya ha cargado correctamente y reanuda la carga de los archivos que aún no había cargado en el momento de la interrupción.

# Prácticas recomendadas para utilizar Gremlin con Neptune
<a name="best-practices-gremlin"></a>

Siga estas recomendaciones cuando utilice el lenguaje de recorrido de gráficos Gremlin con Neptune. Para obtener información sobre el uso de Gremlin con Neptune, consulte [Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md).

**importante**  
Se realizó un cambio en la TinkerPop versión 3.4.11 que mejora la corrección del procesamiento de las consultas, pero por el momento a veces puede afectar gravemente al rendimiento de las consultas.  
Por ejemplo, una consulta de este tipo podría ejecutarse de una forma mucho más lenta:  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  out()
```
Los vértices situados tras el paso límite ahora se obtienen de forma no óptima debido al cambio de la versión 3.4.11. TinkerPop Para evitarlo, puede modificar la consulta añadiendo el paso barrier() en cualquier punto después de `order().by()`. Por ejemplo:  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  barrier().
  out()
```
TinkerPop [La versión 3.4.11 estaba habilitada en la versión 1.0.5.0 del motor de Neptune.](engine-releases-1.0.5.0.md)

**Topics**
+ [Configuración de Heartbeat para Neptune Serverless](best-practices-gremlin-heartbeat-serverless.md)
+ [Estructure las consultas de actualización o inserción para aprovechar el motor DFE](#best-practices-gremlin-upserts)
+ [Pruebe el código de Gremlin en el contexto en el que lo va a implementar](best-practices-gremlin-console-glv-differences.md)
+ [Creación de escrituras de Gremlin eficientes de múltiples subprocesos](best-practices-gremlin-multithreaded-writes.md)
+ [Depuración de registros con la propiedad de hora de creación](best-practices-gremlin-prune.md)
+ [Uso del método `datetime( )` para datos de tiempo de Groovy](best-practices-gremlin-datetime.md)
+ [Uso de la fecha y hora nativas para los datos de tiempo de GLV](best-practices-gremlin-datetime-glv.md)

# Configuración de Heartbeat para Neptune Serverless
<a name="best-practices-gremlin-heartbeat-serverless"></a>

Al utilizar WebSocket clientes Gremlin con Neptune Serverless, debe configurar el intervalo de ping del cliente de forma adecuada para mantener conexiones estables durante los eventos de escalado. El cliente Gremlin utiliza WebSocket las conexiones y envía pings periódicos para comprobar que la conexión está activa. El cliente espera una respuesta del servidor dentro del intervalo de ping. Si el servidor no responde, el cliente cierra automáticamente la conexión.

**Para las instancias **aprovisionadas** por Neptune, recomendamos establecer el intervalo de ping en 5 segundos.** Para los **clústeres Neptune Serverless**, recomendamos establecer el intervalo de ping en al menos **20 segundos** para adaptarse a posibles retrasos durante las operaciones de escalado. Este parámetro controla el tiempo que el cliente espera entre escrituras en el servidor antes de enviar un ping para comprobar que la conexión sigue activa.

La configuración de este parámetro varía en función de la implementación del cliente:

**Configuración del cliente Java**

Para el cliente Java TinkerPop Gremlin, configure el `keepAliveInterval` parámetro:

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

Para obtener más información sobre la configuración del controlador de Java, consulte la documentación de [Java TinkerPop ](https://tinkerpop.apache.org/docs/current/reference/#gremlin-java-configuration).

**Configuración del cliente Go**

Para el cliente Gremlin Go, configure el `KeepAliveInterval` parámetro:

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

Para obtener más información sobre la configuración del controlador Go, consulte la documentación de [Go TinkerPop ](https://tinkerpop.apache.org/docs/current/reference/#gremlin-go-configuration).

**JavaScriptConfiguración del cliente de /Node.js**

Para el cliente Gremlin de JavaScript /Node.js, configure el `pingInterval` parámetro:

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

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

[Para obtener más información sobre la configuración JavaScript del controlador, consulte la JavaScript TinkerPop documentación.](https://tinkerpop.apache.org/docs/current/reference/#gremlin-javascript-configuration)

**Configuración del cliente Python**

Para el cliente Python Gremlin, el intervalo de ping normalmente se gestiona en la capa de transporte. Consulte la documentación específica de implementación del transporte para ver las opciones de configuración:

```
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))))
```

Para obtener más información sobre la configuración del controlador de Python, consulte la [ TinkerPop documentación de Python](https://tinkerpop.apache.org/docs/current/reference/#gremlin-python-configuration).

Esta configuración garantiza que su cliente mantenga la estabilidad de la conexión durante los eventos de escalado de Neptune Serverless, lo que evita cierres de conexión innecesarios y mejora la confiabilidad de las aplicaciones.

## Estructure las consultas de actualización o inserción para aprovechar el motor DFE
<a name="best-practices-gremlin-upserts"></a>

En [Realización de actualizaciones o inserciones eficientes con los pasos `mergeV()` y `mergeE()` de Gremlin](gremlin-efficient-upserts.md), se explica cómo estructurar las consultas de actualización o inserción para utilizar el motor DFE de la forma más eficaz posible.

# Pruebe el código de Gremlin en el contexto en el que lo va a implementar
<a name="best-practices-gremlin-console-glv-differences"></a>

En Gremlin, los clientes pueden enviar consultas al servidor de varias maneras: mediante Bytecode GLV o mediante WebSocket la consola Gremlin mediante scripts basados en cadenas.

Es importante reconocer que la ejecución de las consultas de Gremlin puede variar según la forma en que se envíe la consulta. Una consulta que devuelve un resultado vacío puede considerarse correcta si se envía en el modo de Bytecode, pero puede considerarse que ha fallado si se envía en el modo de script. Por ejemplo, si incluyes una consulta `next()` en modo script, se envía al servidor, pero al usar el cliente, por lo general, `next()` se procesa por sí misma. ByteCode `next()` En el primer caso, la consulta falla si no se encuentra ningún resultado, pero en el segundo, la consulta se realiza correctamente independientemente de que el conjunto de resultados esté vacío o no.

Si desarrolla y prueba su código en un solo contexto (por ejemplo, la consola de Gremlin, que normalmente envía consultas en forma de texto), pero luego implementa el código en un contexto diferente (por ejemplo, mediante el controlador de Java con Bytecode), podría tener problemas, ya que su código podría comportarse de manera diferente en producción que en su entorno de desarrollo.

**importante**  
Asegúrese de probar el código de Gremlin en el contexto de GLV en el que se va a implementar para evitar resultados inesperados.

# Creación de escrituras de Gremlin eficientes de múltiples subprocesos
<a name="best-practices-gremlin-multithreaded-writes"></a>

Hay algunas directrices para la carga con múltiples subprocesos de datos en Neptune mediante Gremlin.

Si fuera posible, asigne a cada hilo un conjunto de vértices o bordes para su inserción o modificación que no colisionen. Por ejemplo, ID de direcciones de hilo 1, rango 1–50 000; ID de direcciones de hilo 2, rango 50 001–100 000, y así sucesivamente. Esto reduce el riesgo de alcanzar un `ConcurrentModificationException`. Para estar seguro, coloque un bloque `try/catch` alrededor de todas las escrituras. Si se produce cualquier error, puede volver a intentarlo después de un breve retraso.

Las operaciones de escritura por lotes en un tamaño de lote entre 50 y 100 (vértices o bordes) generalmente funcionan bien. Si está agregando una gran cantidad de propiedades para cada vértice, un número cercano al 50 en lugar de 100 podría ser una elección más acertada. Merece la pena experimentar. Por lo tanto, para las escrituras por lotes, puede utilizar algo así como esto:

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

Esto se repite en cada operación por lotes.

El uso de lotes es bastante más eficiente que la adición de un vértice o borde por cada viaje de ida y vuelta de Gremlin al servidor.

Si está utilizando un cliente de la variante de lenguaje Gremlin (GLV), puede crear un lote mediante programación creando en primer lugar un recorrido. Añádala y, por último, realice una iteración sobre ella. Por ejemplo:

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

Si es posible, es mejor utilizar el cliente de la variante de lenguaje Gremlin. Sin embargo, puede hacer algo similar con un cliente que envía consultas como cadenas de texto concatenando cadenas para construir un lote.

Si utiliza una de las bibliotecas de clientes Gremlin en lugar de HTTP básicos de las consultas, todos los subprocesos deberán compartir el mismo cliente, clúster o grupo de conexiones. Es posible que tenga que ajustar la configuración para obtener el mejor rendimiento posible; por ejemplo, puede que tenga que ajustar el tamaño del grupo de conexiones y el número de subprocesos de trabajo que utiliza el cliente de Gremlin.

# Depuración de registros con la propiedad de hora de creación
<a name="best-practices-gremlin-prune"></a>

Puede depurar los registros viejos almacenando la hora de creación como una propiedad en los vértices y descartándolos periódicamente.

Si necesita almacenar datos durante un periodo específico y, a continuación, eliminarlos del gráfico (tiempo de vida de vértice), puede almacenar una propiedad de marca temporal en la creación del vértice. A continuación, puede emitir periódicamente una consulta `drop()` para todos los vértices que se crearon antes de una hora determinada; por ejemplo:

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

# Uso del método `datetime( )` para datos de tiempo de Groovy
<a name="best-practices-gremlin-datetime"></a>

Neptune proporciona el método `datetime` para especificar fechas y horas para las consultas enviadas en la variante **Groovy** de Gremlin. Esto incluye la consola de Gremlin, cadenas de texto que utilizan la API REST HTTP y cualquier otra serialización que utilice Groovy. 

**importante**  
Esto *solo* se aplica a aquellos casos en los que envíe la consulta de Gremlin como una *cadena de texto*. Si utiliza una variante del lenguaje Gremlin, debe usar las clases y funciones de fecha nativas para el lenguaje. Para obtener más información, consulte la siguiente sección, [Uso de la fecha y hora nativas para los datos de tiempo de GLV](best-practices-gremlin-datetime-glv.md).  
Empezando por TinkerPop `3.5.2` (introducido en la [versión 1.1.1.0 del motor Neptune](engine-releases-1.1.1.0.md)), `datetime` es una parte integral de. TinkerPop

Puede utilizar el método `datetime` para almacenar y comparar fechas:

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

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

# Uso de la fecha y hora nativas para los datos de tiempo de GLV
<a name="best-practices-gremlin-datetime-glv"></a>

Si utiliza una variante del lenguaje Gremlin (GLV), debe usar las clases y funciones de fecha y hora nativas que proporciona el lenguaje de programación para los datos de tiempo de Gremlin.

Las TinkerPop bibliotecas oficiales son todas las bibliotecas de variantes del lenguaje Gremlin.
+  [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) 

**importante**  
 Esta página solo se aplica a las bibliotecas de la variante de lenguaje Gremlin (GLV). Si utiliza un método en el que envía la consulta de Gremlin como cadena de texto, debe utilizar la función datetime() de Gremlin. Esto incluye la consola de Gremlin, las cadenas de texto que utilizan la API de REST de HTTP o el envío directo de cadenas de Gremlin a través de los controladores. 



**Go**  
 A continuación, se muestra un ejemplo parcial en Go que crea una única propiedad llamada “date” para el vértice con un ID de “3”. Establece el valor como una fecha generada mediante la función time.Now() de Go. 

```
import ( "time" )

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

Para ver un ejemplo completo de conexión a Neptune mediante Go, consulte [Uso de un cliente Go para conectarse a una instancia de base de datos de Neptune](https://docs.aws.amazon.com//neptune/latest/userguide/access-graph-gremlin-go.html).

**Java**  
A continuación se muestra un ejemplo parcial en Java que crea una única propiedad llamada "`date`" para el vértice con un ID de "`3`". Define el valor que será una fecha generada utilizando el constructor `Date()` de Java.

```
import java.util.date

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

Para ver un ejemplo completo de cómo conectarse a Neptune mediante Java, consulte [Uso de un cliente Java para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-java.md).

**Node.js () JavaScript**  
El siguiente es un ejemplo parcial en el JavaScript que se crea una propiedad única llamada '`date`' para el vértice con un identificador de '`3`'. Define el valor que será una fecha generada utilizando el constructor `Date()` de Node.js.

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

Para ver un ejemplo completo de cómo conectarse a Neptune mediante Node.js, consulte [Uso de Node.js para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-node-js.md).

**.NET (C\$1)**  
A continuación se muestra un ejemplo parcial en C\$1 que crea una única propiedad llamada "`date`" para el vértice con un ID de "`3`". Define el valor que será una fecha generada utilizando la propiedad `DateTime.UtcNow` de .NET.

```
Using System;

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

Para ver un ejemplo completo de cómo conectarse a Neptune mediante C\$1, consulte [Uso de .NET para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-dotnet.md).

**Python**  
A continuación se muestra un ejemplo parcial en Python que crea una única propiedad llamada "`date`" para el vértice con un ID de "`3`". Define el valor que será una fecha generada utilizando el método `datetime.now()` de Python.

```
import datetime

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

Para ver un ejemplo completo de cómo conectarse a Neptune mediante Python, consulte [Uso de Python para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-python.md).

# Prácticas recomendadas del uso del cliente Java de Gremlin con Neptune
<a name="best-practices-gremlin-java-client"></a>

Siga estas recomendaciones cuando utilice el cliente Java Gremlin con Neptune. Estas prácticas recomendadas le ayudan a optimizar el rendimiento, gestionar las conexiones de forma eficaz y evitar los errores habituales al trabajar con el controlador de Java.

Para obtener información sobre la configuración de los intervalos de latidos para Neptune Serverless, consulte. [Configuración de Heartbeat para Neptune Serverless](best-practices-gremlin-heartbeat-serverless.md)

**Topics**
+ [Vuelva a utilizar el objeto del cliente en varios subprocesos](best-practices-gremlin-java-reuse.md)
+ [Cree objetos de cliente Java de Gremlin independientes para puntos de conexión de lectura y escritura](best-practices-gremlin-java-separate.md)
+ [Añada varios puntos de conexión de réplica de lectura a un grupo de conexiones Java de Gremlin](best-practices-gremlin-java-multiple.md)
+ [Cierre el cliente para evitar el límite de conexiones](best-practices-gremlin-java-close-connections.md)
+ [Cree una nueva conexión después de una conmutación por error](best-practices-gremlin-java-new-connection.md)
+ [Establezca `maxInProcessPerConnection` y `maxSimultaneousUsagePerConnection` con el mismo valor.](best-practices-gremlin-java-maxes.md)
+ [Envíe consultas al servidor como bytecode en lugar de como cadenas](best-practices-gremlin-java-bytecode.md)
+ [Consume siempre por completo el iterador ResultSet or devuelto por una consulta](best-practices-gremlin-java-resultset.md)
+ [Añada vértices y bordes de forma masiva por lotes](best-practices-gremlin-java-batch-add.md)
+ [Deshabilitar el almacenamiento en caché de DNS en la máquina virtual de Java](best-practices-gremlin-java-disable-dns-caching.md)
+ [Opcionalmente, establezca tiempos de espera al nivel de consulta](best-practices-gremlin-java-per-query-timeout.md)
+ [Solución de problemas de `java.util.concurrent.TimeoutException`](best-practices-gremlin-java-exceptions-TimeoutException.md)

# Vuelva a utilizar el objeto del cliente en varios subprocesos
<a name="best-practices-gremlin-java-reuse"></a>

Volver a utilizar el mismo objeto de cliente (o `GraphTraversalSource`) en varios subprocesos. Es decir, cree una instancia compartida de una clase `org.apache.tinkerpop.gremlin.driver.Client` en su aplicación, en lugar de hacerlo en cada subproceso. El objeto `Client` es seguro para subprocesos y la sobrecarga para inicializar es considerable.

Esto también se aplica a `GraphTraversalSource`, que crea un objeto `Client` internamente. Por ejemplo, el siguiente código hace que se cree una instancia para un nuevo objeto `Client`:

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

  /////

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

# Cree objetos de cliente Java de Gremlin independientes para puntos de conexión de lectura y escritura
<a name="best-practices-gremlin-java-separate"></a>

Puede aumentar el rendimiento si solo escribe en el punto de enlace del escritor y lee desde uno o más puntos de enlace de solo lectura.

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

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

# Añada varios puntos de conexión de réplica de lectura a un grupo de conexiones Java de Gremlin
<a name="best-practices-gremlin-java-multiple"></a>

Al crear un objeto `Cluster` de Java de Gremlin, puede utilizar el método `.addContactPoint()` para agregar varias instancias de réplicas de lectura a los puntos de contacto del grupo de conexiones.

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

# Cierre el cliente para evitar el límite de conexiones
<a name="best-practices-gremlin-java-close-connections"></a>

Es importante cerrar el cliente cuando haya terminado con él para garantizar que el servidor cierre las WebSocket conexiones y que se liberen todos los recursos asociados a las conexiones. Esto ocurre automáticamente si cierra el clúster mediante `Cluster.close( )`, ya que `client.close( )` se llama internamente.

Si el cliente no está cerrado correctamente, Neptune finaliza todas las WebSocket conexiones inactivas después de 20 a 25 minutos. Sin embargo, si no cierras WebSocket las conexiones de forma explícita cuando terminas de usarlas y el número de conexiones activas alcanza el [límite de conexiones WebSocket simultáneas](limits.md#limits-websockets), se rechazarán las conexiones adicionales con un código de error HTTP`429`. En ese momento, deberá reiniciar la instancia de Neptune para cerrar las conexiones.

La recomendación de llamar a `cluster.close()` no se aplica a las funciones AWS Lambda de Java. Para obtener más información, consulte [Administración de las conexiones WebSocket de Gremlin en las funciones de AWS Lambda](lambda-functions-websocket-connections.md).

# Cree una nueva conexión después de una conmutación por error
<a name="best-practices-gremlin-java-new-connection"></a>

En caso de una conmutación por error, el controlador de Gremlin podría continuar conectándose al escritor antiguo porque el nombre DNS del clúster está resuelto a una dirección IP. Si esto sucede, puede crear un nuevo objeto `Client` después de la conmutación por error.

# Establezca `maxInProcessPerConnection` y `maxSimultaneousUsagePerConnection` con el mismo valor.
<a name="best-practices-gremlin-java-maxes"></a>

`maxInProcessPerConnection`Tanto el parámetro como el `maxSimultaneousUsagePerConnection` parámetro están relacionados con el número máximo de consultas simultáneas que se pueden enviar en una sola WebSocket conexión. Internamente, estos parámetros están correlacionados y la modificación de uno sin el otra podría dar lugar a que un cliente reciba un tiempo de espera al intentar recuperar una conexión desde el grupo de conexión del cliente.

Le recomendamos que mantenga los valores predeterminados mínimo en proceso y de uso simultáneo, y defina `maxInProcessPerConnection` y `maxSimultaneousUsagePerConnection` con el mismo valor.

El valor con el que establecer estos parámetros es una función de complejidad de consulta y el modelo de datos. Un caso de uso en el que la consulta devuelve una gran cantidad de datos requeriría más ancho de banda de conexión por consulta y, por tanto, debería tener unos valores más bajos para los parámetros y un valor más alto para `maxConnectionPoolSize`.

En cambio, en los casos en los que la consulta devuelve una menor cantidad de datos, `maxInProcessPerConnection` y `maxSimultaneousUsagePerConnection` deben establecerse en un valor más alto que `maxConnectionPoolSize`.

# Envíe consultas al servidor como bytecode en lugar de como cadenas
<a name="best-practices-gremlin-java-bytecode"></a>

Existen ventajas con el uso Bytecode en lugar de una cadena al enviar consultas:
+ **Detección temprana de sintaxis de consultas no válida:** el uso de la variante Bytecode permite detectar la sintaxis de consultas no válidas en la fase de compilación. Si utiliza la variación basada en cadena, no detectará la sintaxis no válida hasta que la consulta se envíe al servidor y se devuelva un error.
+ **Evite las penalizaciones de rendimiento basadas en cadenas:** [cualquier consulta basada en cadenas, ya sea que utilice HTTP WebSockets o HTTP, da como resultado un vértice separado, lo que implica que el objeto Vertex está formado por el identificador, la etiqueta y todas las propiedades asociadas al vértice (consulte Propiedades de los elementos).](http://tinkerpop.apache.org/docs/current/reference/#_properties_of_elements)

  Esto puede dar lugar a cálculos innecesarios en el servidor en casos en que las propiedades no son obligatorias. Por ejemplo, si el cliente está interesado en obtener el vértice con el ID "hakuna\$11" utilizando la consulta `g.V("hakuna#1")`. Si la consulta se envía como envío basado en cadena, el servidor gastaría tiempo en recuperar el ID, la etiqueta y todas las propiedades de este vértice. Si la consulta se envía como un envío de Bytecode, el servidor solo dedica tiempo en recuperar el ID y la etiqueta del vértice.

En otras palabras, en lugar de enviar una consulta de esta manera:

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

Envíe la consulta mediante Bytecode, tal y como se muestra a continuación:

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

# Consume siempre por completo el iterador ResultSet or devuelto por una consulta
<a name="best-practices-gremlin-java-resultset"></a>

El objeto de cliente siempre debe consumir por completo `ResultSet` (en el caso de envío basado en cadena) o el iterador devuelto por `GraphTraversal`. Si los resultados de la consulta no se consumen por completo, el servidor los contiene, esperando a que el cliente termine de consumirlos.

Si la aplicación solo necesita un conjunto de resultados parcial, puede utilizar un paso `limit(X)` con su consulta para restringir el número de resultados que el servidor genera.

# Añada vértices y bordes de forma masiva por lotes
<a name="best-practices-gremlin-java-batch-add"></a>

Cada consulta a la base de datos de Neptune se ejecuta en el ámbito de una sola transacción, a menos que utilice una sesión. Esto significa que, si se necesita insertar una gran cantidad de datos con las consultas de Gremlin, agruparlos en lotes en un tamaño de 50-100 mejora el rendimiento al reducir el número de transacciones creadas para la carga.

Por ejemplo, agregar 5 vértices a la base de datos tendría el siguiente aspecto:

```
// 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();
```

Del mismo modo, puede agregar bordes por lotes utilizando. `addE` Se utiliza `V()` para hacer referencia a los vértices existentes como origen y destino de cada arista:

```
// 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();
```

También puede combinar la creación de vértices y aristas en un solo lote. Se utiliza `as()` para etiquetar los vértices recién creados de forma que pueda hacer referencia a ellos al añadir aristas en el mismo recorrido:

```
// 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();
```

# Deshabilitar el almacenamiento en caché de DNS en la máquina virtual de Java
<a name="best-practices-gremlin-java-disable-dns-caching"></a>

[En un entorno en el que desee equilibrar la carga de las solicitudes en varias réplicas de lectura, debe deshabilitar el almacenamiento en caché del DNS en la máquina virtual Java (JVM) y proporcionar el terminal de lectura de Neptune al crear el objeto de clúster.](https://tinkerpop.apache.org/javadocs/current/core/org/apache/tinkerpop/gremlin/driver/Cluster.html) Desactivar la caché de DNS de la JVM garantiza que DNS se resuelva de nuevo para cada conexión nueva, de modo que las solicitudes se distribuyan en todas las réplicas de lectura. Puede hacerlo en el código de inicialización de su aplicación con la siguiente línea:

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

Sin embargo, el código del cliente [Java de Amazon Gremlin](https://github.com/awslabs/amazon-neptune-tools/tree/master/neptune-gremlin-client) proporciona una solución más completa y sólida para el equilibrio de carga. GitHub El cliente Gremlin de Amazon Java conoce la topología de su clúster y distribuye de forma equitativa las conexiones y solicitudes entre un conjunto de instancias del clúster de Neptune. Consulte [esta entrada del blog](https://aws.amazon.com/blogs/database/load-balance-graph-queries-using-the-amazon-neptune-gremlin-client/) para ver un ejemplo de una función de Lambda de Java que usa ese cliente.

# Opcionalmente, establezca tiempos de espera al nivel de consulta
<a name="best-practices-gremlin-java-per-query-timeout"></a>

Neptune ofrece la posibilidad de definir un tiempo de espera para sus consultas con la opción de grupo de parámetros `neptune_query_timeout` (consulte [Parameters](parameters.md)). También puede anular el tiempo de espera global con un código como este:

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

O bien, para envío de consultas basado en cadenas, el código sería el siguiente:

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

**nota**  
Es posible incurrir en costos inesperados si se establece un valor de tiempo de espera de consulta demasiado alto, especialmente en una instancia sin servidor. Si la configuración del tiempo de espera no es razonable, es posible que la consulta siga ejecutándose durante mucho más tiempo del esperado, lo que dará lugar a costos que no había previsto. Esto ocurre especialmente en una instancia sin servidor que podría escalarse verticalmente hasta convertirse en un tipo de instancia grande y caro mientras se ejecuta la consulta.  
Puede evitar gastos inesperados de este tipo si utiliza un valor de tiempo de espera de consulta que se adapte al tiempo de ejecución esperado y que solo provoque un tiempo de espera inusualmente largo.  
 A partir de la versión 1.3.2.0 del motor de Neptune, Neptune admite un nuevo parámetro neptune\$1lab\$1mode como `StrictTimeoutValidation`. Cuando este parámetro tiene el valor `Enabled`, el valor de tiempo de espera por consulta especificado como una opción de solicitud o una sugerencia de consulta no puede superar el valor establecido globalmente en el grupo de parámetros. En tal caso, Neptune generará una excepción `InvalidParameterException`.   
 Esta configuración se puede confirmar en una respuesta en el punto final «/status» cuando el valor es. `Disabled` En la versión de motor`1.3.2.0`, el valor predeterminado de este parámetro es`Disabled`. A partir de la versión de motor`1.4.0.0`, el `StrictTimeoutValidation` parámetro es `Enabled` el predeterminado.   
 Para obtener más información sobre cómo se determina la prioridad del tiempo de espera cuando se configuran varios ajustes de tiempo de espera, consulte la documentación del parámetro [neptune\$1query\$1timeout](parameters.md#parameters-db-cluster-parameters-neptune_query_timeout). 

# Solución de problemas de `java.util.concurrent.TimeoutException`
<a name="best-practices-gremlin-java-exceptions-TimeoutException"></a>

El cliente Java de Gremlin lanza un mensaje `java.util.concurrent.TimeoutException` cuando se agota el tiempo de espera de una solicitud de Gremlin en el propio cliente mientras se espera a que quede disponible un espacio en una de las conexiones. WebSocket Este tiempo de espera se controla mediante el parámetro configurable del lado del cliente `maxWaitForConnection`.

**nota**  
Como las solicitudes cuyo tiempo de espera se agota en el cliente nunca se envían al servidor, no se reflejan en ninguna de las métricas recopiladas en el servidor, por ejemplo `GremlinRequestsPerSec`.

Este tipo de tiempo de espera se produce generalmente de una de estas dos formas:
+ **El servidor ha alcanzado su capacidad máxima.** Si este es el caso, la cola del servidor se llena, lo que puedes detectar monitorizando la métrica. [MainRequestQueuePendingRequests](cw-metrics.md#cw-metrics-available) CloudWatch La cantidad de consultas paralelas que el servidor puede gestionar depende del tamaño de la instancia.

  Si la métrica `MainRequestQueuePendingRequests` no muestra una acumulación de solicitudes pendientes en el servidor, el servidor puede gestionar más solicitudes y el tiempo de espera se debe a una limitación del lado del cliente.
+ **Limitación de las solicitudes por parte del cliente.** Por lo general, esto se puede solucionar cambiando los ajustes de la configuración del cliente.

  El número máximo de solicitudes paralelas que puede enviar el cliente se puede calcular de manera aproximada de la siguiente forma:

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

  Si se envía al cliente una cantidad superior a la especificada en `maxParallelQueries`, se producen excepciones `java.util.concurrent.TimeoutException`. Por lo general, hay varias maneras de solucionar esto:
  + *Aumente la duración del tiempo de espera de la conexión.* Si la latencia no es crucial para su aplicación, aumente la configuración de `maxWaitForConnection` del cliente. Por tanto, el cliente espera más antes de agotar el tiempo de espera, lo que a su vez puede aumentar la latencia.
  + *Aumente el número máximo de solicitudes por conexión.* Esto permite enviar más solicitudes con la misma WebSocket conexión. Para ello, aumente los ajustes de `maxSimultaneousUsagePerConnection` y `maxInProcessPerConnection` del cliente. Por lo general, estos ajustes deben tener el mismo valor. 
  + *Aumente el número de conexiones en el grupo de conexiones.* Para ello, aumente el ajuste de `maxConnectionPoolSize` del cliente. El coste se traduce en un mayor consumo de recursos, ya que cada conexión utiliza memoria y un descriptor de archivos del sistema operativo, y requiere un SSL y WebSocket un protocolo de enlace durante la inicialización.

# Prácticas recomendadas de Neptune con openCypher y Bolt
<a name="best-practices-opencypher"></a>

Siga estas prácticas recomendadas cuando use el lenguaje de consulta openCypher y el protocolo Bolt con Neptune. Para obtener más información sobre el uso de openCypher en Neptune, consulte [Acceso al gráfico de Neptune con openCypher](access-graph-opencypher.md).

**Topics**
+ [Cree una nueva conexión después de una conmutación por error](#best-practices-opencypher-renew-connection)
+ [Gestión de conexiones para aplicaciones de larga duración](#best-practices-opencypher-long-connections)
+ [Manejo de conexiones para AWS Lambda](#best-practices-opencypher-lambda-connections)
+ [En las consultas, utilice preferentemente bordes dirigidos en lugar de bidireccionales](best-practices-opencypher-directed-edges.md)
+ [Neptune no admite múltiples consultas simultáneas en una transacción](best-practices-opencypher-multiple-queries.md)
+ [Cierre los objetos del controlador cuando haya terminado](best-practices-opencypher-close-driver.md)
+ [Use modos de transacción explícitos para leer y escribir](best-practices-opencypher-use-explicit-txs.md)
+ [Lógica de reintentos para las excepciones](best-practices-opencypher-retry-logic.md)
+ [Establecimiento de varias propiedades a la vez mediante una sola cláusula SET](best-practices-content-0.md)
+ [Uso de consultas parametrizadas](best-practices-content-2.md)
+ [Uso de mapas aplanados en lugar de mapas anidados en la cláusula UNWIND](best-practices-content-3.md)
+ [Inserción de nodos más restrictivos en el lado izquierdo en expresiones de ruta de longitud variable (VLP)](best-practices-content-4.md)
+ [Elusión de comprobaciones de etiquetas de nodos redundantes mediante el uso de nombres de relación granulares](best-practices-content-5.md)
+ [Especificación de etiquetas de borde cuando sea posible](best-practices-content-6.md)
+ [Elusión de la cláusula WITH cuando sea posible](best-practices-content-7.md)
+ [Inserción de filtros restrictivos lo antes posible en la consulta](best-practices-content-8.md)
+ [Comprobación explícita de la existencia de propiedades](best-practices-content-9.md)
+ [Elusión de rutas con nombre (a menos que sea necesario)](best-practices-content-10.md)
+ [Elusión de COLLECT (DISTINCT ())](best-practices-content-11.md)
+ [Preferencia de la función de propiedades sobre la búsqueda de propiedades individuales en la recuperación de todos los valores de las propiedades](best-practices-content-12.md)
+ [Realización de cálculos estáticos fuera de la consulta](best-practices-content-13.md)
+ [Entradas por lotes mediante UNWIND en lugar de instrucciones individuales](best-practices-content-14.md)
+ [Prefiere usar la opción personalizada para el nodo o la relación IDs](best-practices-content-15.md)
+ [Elusión de cálculos \$1id en la consulta](best-practices-content-16.md)
+ [Actualización/fusión de varios nodos](best-practices-merge-multiple-nodes.md)

## Cree una nueva conexión después de una conmutación por error
<a name="best-practices-opencypher-renew-connection"></a>

En caso de una conmutación por error, el controlador de Bolt puede seguir conectándose a la antigua instancia de escritor en lugar de a la nueva instancia activa, ya que el nombre DNS se ha resuelto en una dirección IP específica.

Para evitarlo, cierre el objeto `Driver` y vuelva a conectarlo después de cualquier conmutación por error.

## Gestión de conexiones para aplicaciones de larga duración
<a name="best-practices-opencypher-long-connections"></a>

Al crear aplicaciones de larga duración, como las que se ejecutan en contenedores o en instancias de Amazon EC2, cree una instancia de un objeto `Driver` una vez y, a continuación, reutilícelo durante toda la vida útil de la aplicación. El objeto `Driver` es seguro para subprocesos y la sobrecarga para inicializar es considerable.

## Manejo de conexiones para AWS Lambda
<a name="best-practices-opencypher-lambda-connections"></a>

No se recomienda el uso de destornilladores dentro de AWS Lambda las funciones debido a su sobrecarga de conexión y a los requisitos de administración. En su lugar, utilice el [punto de conexión HTTPS](access-graph-opencypher-queries.md).

# En las consultas, utilice preferentemente bordes dirigidos en lugar de bidireccionales
<a name="best-practices-opencypher-directed-edges"></a>

Cuando Neptune optimiza las consultas, los bordes bidireccionales dificultan la creación de planes de consulta óptimos. Cuando los planes no son óptimos, el motor debe realizar trabajos innecesarios y eso se traduce en un rendimiento inferior.

Por consiguiente, utilice bordes dirigidas en lugar de bidireccionales siempre que sea posible. Por ejemplo, utilice

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

en lugar de

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

En realidad, la mayoría de los modelos de datos no necesitan atravesar los bordes en ambas direcciones, por lo que se pueden lograr mejoras significativas de rendimiento en las consultas si se opta por utilizar bordes dirigidos.

Si su modelo de datos requiere atravesar bordes bidireccionales, haga que el primer nodo (lado izquierdo) del patrón `MATCH` sea el nodo con el filtrado más restrictivo.

Utilicemos como ejemplo: “Encuéntrame todas las `routes` de ida y vuelta al aeropuerto de `ANC`“. Escriba esta consulta para empezar en el aeropuerto de `ANC`, de esta forma:

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

El motor puede realizar la cantidad mínima de trabajo necesaria para satisfacer la consulta, ya que el nodo más restringido se coloca como el primer nodo (a la izquierda) del patrón. A continuación, el motor puede optimizar la consulta.

Esto es mucho mejor que filtrar el aeropuerto de `ANC` al final del patrón, de la siguiente manera:

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

Cuando el nodo más restringido no se coloca primero en el patrón, el motor debe realizar un trabajo adicional porque no puede optimizar la consulta y tiene que realizar búsquedas adicionales para llegar a los resultados.

# Neptune no admite múltiples consultas simultáneas en una transacción
<a name="best-practices-opencypher-multiple-queries"></a>

Aunque el propio controlador de Bolt permite consultas simultáneas en una transacción, Neptune no admite múltiples consultas en una transacción que se ejecute simultáneamente. En su lugar, Neptune requiere que se ejecuten varias consultas en una transacción de forma secuencial y que los resultados de cada consulta se consuman por completo antes de que se inicie la siguiente consulta.

En el siguiente ejemplo, se muestra cómo usar Bolt para ejecutar varias consultas de forma secuencial en una transacción, de modo que los resultados de cada una de ellas se consuman por completo antes de que comience la siguiente:

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

# Cierre los objetos del controlador cuando haya terminado
<a name="best-practices-opencypher-close-driver"></a>

Asegúrese de cerrar el cliente cuando haya terminado con él para que el servidor cierre las conexiones de Bolt y se liberen todos los recursos asociados a las conexiones. Esto se realiza automáticamente si cierra el controlador utilizando `driver.close()`.

Si el controlador no se cierra correctamente, Neptune finaliza todas las conexiones de Bolt inactivas pasados 20 minutos, o después de 10 días si utiliza la autenticación de IAM.

Neptune no admite más de 1000 conexiones de Bolt simultáneas. Si no cierra las conexiones de forma explícita cuando termine de usarlas y el número de conexiones activas alcanza ese límite de 1000, cualquier nuevo intento de conexión fallará.

# Use modos de transacción explícitos para leer y escribir
<a name="best-practices-opencypher-use-explicit-txs"></a>

Cuando se utilizan transacciones con Neptune y el controlador de Bolt, es mejor establecer explícitamente el modo de acceso para las transacciones de lectura y escritura con la configuración correcta.

## Transacciones de solo lectura
<a name="best-practices-opencypher-read-txs"></a>

En el caso de las transacciones de solo lectura, si no se introduce la configuración de modo de acceso adecuada al crear la sesión, se utiliza el nivel de aislamiento predeterminado, que es el aislamiento de las consultas de mutación. Por ello, es importante que en las transacciones de solo lectura se establezca el modo de acceso a `read` de forma explícita.

**Ejemplo de transacción de lectura con confirmación automática:**

```
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()
}
```

**Ejemplo de transacción de lectura:**

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

En ambos casos, el [aislamiento de `SNAPSHOT`](transactions-isolation-levels.md) se logra mediante la [semántica de transacciones de solo lectura de Neptune](transactions-neptune.md#transactions-neptune-read-only).

Como las réplicas de lectura solo aceptan consultas de solo lectura, cualquier consulta que se envíe a una réplica de lectura se ejecuta en función de una semántica de aislamiento de `SNAPSHOT`.

No hay lecturas incorrectas ni lecturas no repetibles para las transacciones de solo lectura.

## Transacciones de mutación
<a name="best-practices-opencypher-mutation-txs"></a>

En el caso de las consultas de mutación, existen tres mecanismos diferentes para crear una transacción de escritura, cada uno de los cuales se ilustra a continuación:

**Ejemplo de transacción de escritura implícita:**

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

**Ejemplo de transacción de escritura con confirmación automática:**

```
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()
}
```

**Ejemplo de transacción de escritura explícita:**

```
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();
```

**Niveles de aislamiento para transacciones de escritura**
+ Las lecturas realizadas como parte de las consultas de mutación se ejecutan de forma aislada en las transacciones de `READ COMMITTED`.
+ No hay lecturas incorrectas en las lecturas realizadas como parte de las consultas de mutación.
+ Los registros y rangos de registros se bloquean al leer una consulta de mutación.
+ Cuando una transacción de mutación ha leído un rango del índice, existe una gran garantía de que este rango no se modificará mediante ninguna transacción simultánea hasta el final de la lectura.

Las consultas de mutación no son seguras para los subprocesos.

Para conocer los conflictos, consulte [Resolución de conflictos mediante tiempos de espera de bloqueo](transactions-neptune.md#transactions-neptune-conflicts).

Las consultas de mutación no se vuelven a intentar automáticamente en caso de error.

# Lógica de reintentos para las excepciones
<a name="best-practices-opencypher-retry-logic"></a>

Para todas las excepciones que permiten un reintento, generalmente es mejor utilizar una [estrategia de reintento y retroceso exponencial](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) que proporcione tiempos de espera progresivamente más largos entre los reintentos, a fin de gestionar mejor los problemas transitorios, como los errores `ConcurrentModificationException`. A continuación, se muestra un ejemplo de patrón de reintento y retroceso exponencial:

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

# Establecimiento de varias propiedades a la vez mediante una sola cláusula SET
<a name="best-practices-content-0"></a>

 En lugar de utilizar varias cláusulas SET para establecer propiedades individuales, utilice un mapa para establecer varias propiedades de una entidad a la vez. 

 Puede usar: 

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

 En lugar de: 

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

 La cláusula SET acepta una sola propiedad o un mapa. Si se actualizan varias propiedades en una sola entidad, el uso de una sola cláusula SET con un mapa permite que las actualizaciones se realicen en una sola operación en lugar de en varias, lo que permite una ejecución más eficiente. 

## Uso de la cláusula SET para eliminar varias propiedades a la vez
<a name="best-practices-content-1"></a>

 Cuando se utiliza el lenguaje openCypher, se utiliza REMOVE para eliminar propiedades de una entidad. En Neptune, cada propiedad que se elimina requiere una operación independiente, lo que aumenta la latencia de la consulta. En su lugar, puede usar SET con un mapa para establecer todos los valores de las propiedades en `null`, lo que en Neptune equivale a eliminar propiedades. Neptune tendrá un mayor rendimiento cuando sea necesario eliminar varias propiedades de una sola entidad. 

Uso:

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

En lugar de:

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

# Uso de consultas parametrizadas
<a name="best-practices-content-2"></a>

 Se recomienda utilizar siempre consultas parametrizadas al realizar consultas con openCypher. El motor de consultas puede aprovechar las consultas parametrizadas repetidas para características como la memoria caché de planes de consultas, donde la invocación repetida de la misma estructura parametrizada con diferentes parámetros puede aprovechar los planes almacenados en caché. El plan de consultas generado para las consultas parametrizadas se almacena en caché y se reutiliza solo cuando se completa en 100 ms y los tipos de parámetros son NUMBER, BOOLEAN o STRING. 

Uso:

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

Con los parámetros:

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

En lugar 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
```

# Uso de mapas aplanados en lugar de mapas anidados en la cláusula UNWIND
<a name="best-practices-content-3"></a>

 Una estructura anidada profunda puede limitar la capacidad del motor de consultas para generar un plan de consulta óptimo. Para solucionar parcialmente este problema, los siguientes patrones definidos crearán planes óptimos para los siguientes escenarios: 
+  Escenario 1: UNWIND con una lista de literales cypher, que incluye NUMBER, STRING y BOOLEAN. 
+  Escenario 2: UNWIND con una lista de mapas aplanados, que incluye solo literales cypher (NUMBER, STRING, BOOLEAN) como valores. 

 Al escribir una consulta que contenga la cláusula UNWIND, utilice la recomendación anterior para mejorar el rendimiento. 

Ejemplos del escenario 1:

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

Con los parámetros:

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

 Un ejemplo para el escenario 2 es generar una lista de nodos para CREATE o MERGE. En lugar de emitir varias instrucciones, utilice el siguiente patrón para definir las propiedades como un conjunto de mapas aplanados: 

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

Con los parámetros:

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

En lugar de objetos de nodo anidados como:

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

Con los parámetros:

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

# Inserción de nodos más restrictivos en el lado izquierdo en expresiones de ruta de longitud variable (VLP)
<a name="best-practices-content-4"></a>

 En las consultas de ruta de longitud variable (VLP), el motor de consultas optimiza la evaluación al elegir iniciar el recorrido por el lado izquierdo o derecho de la expresión. La decisión se basa en la cardinalidad de los patrones de los lados izquierdo y derecho. La cardinalidad es el número de nodos que coinciden con el patrón especificado. 
+  Si el patrón derecho tiene una cardinalidad igual a uno, entonces el lado derecho será el punto de partida. 
+  Si el lado izquierdo y el derecho tienen una cardinalidad igual a uno, la expansión se comprueba en ambos lados y comienza en el lado con la expansión más pequeña. La expansión es el número de bordes salientes o entrantes del nodo del lado izquierdo y del nodo del lado derecho de la expresión de VLP. Esta parte de la optimización solo se usa si la relación de VLP es unidireccional y se proporciona el tipo de relación. 
+  De lo contrario, el lado izquierdo será el punto de partida. 

 Para una cadena de expresiones de VLP, esta optimización solo se puede aplicar a la primera expresión. Los demás VLPs se evalúan empezando por el lado izquierdo. Por ejemplo, supongamos que la cardinalidad de (a), (b) es igual a uno y que la cardinalidad de (c) es mayor que uno. 
+  `(a)-[*1..]->(c)`: la evaluación comienza con (a). 
+  `(c)-[*1..]->(a)`: la evaluación comienza con (a). 
+  `(a)-[*1..]-(c)`: la evaluación comienza con (a). 
+  `(c)-[*1..]-(a)`: la evaluación comienza con (a). 

 Ahora supongamos que los bordes entrantes de (a) son dos y bordes salientes de (a) son tres, mientras que los bordes entrantes de (b) son cuatro y los bordes salientes de (b) son cinco. 
+  `(a)-[*1..]->(b)`: la evaluación comienza con (a), ya que los bordes salientes de (a) son inferiores a los bordes entrantes de (b). 
+  `(a)<-[*1..]-(b)`: la evaluación comienza con (a) ya que los bordes entrantes de (a) son inferiores a los bordes salientes de (b). 

 Como regla general, coloque el patrón más restrictivo en el lado izquierdo de una expresión de VLP. 

# Elusión de comprobaciones de etiquetas de nodos redundantes mediante el uso de nombres de relación granulares
<a name="best-practices-content-5"></a>

 Para optimizar el rendimiento, el uso de etiquetas de relación exclusivas de los patrones de nodos permite eliminar el filtrado de etiquetas en los nodos. Considere un modelo de gráfico en el que la relación `likes` solo se utiliza para definir una relación entre dos nodos `person`. Podríamos escribir la siguiente consulta para encontrar este patrón: 

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

 Comprobar si la etiqueta `person` está presente en n y m es redundante, ya que hemos definido que la relación solo aparezca cuando ambos sean del tipo `person`. Para optimizar el rendimiento, podemos escribir la consulta de la siguiente manera: 

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

 Este patrón también se puede aplicar cuando las propiedades son exclusivas de una sola etiqueta de nodo. En el caso de que solo los nodos `person` tengan la propiedad `email`, comprobar que la etiqueta del nodo coincide con `person` es redundante. Escribir esta consulta como: 

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

 Es menos eficiente que escribir esta consulta como: 

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

 Solo debe adoptar este patrón cuando el rendimiento sea importante y haya realizado comprobaciones en el proceso de modelado para garantizar que estas etiquetas de borde no se reutilicen en patrones que impliques otras etiquetas de nodo. Si posteriormente introduce una propiedad `email` en otra etiqueta de nodo, como por ejemplo `company`, los resultados diferirán entre estas dos versiones de la consulta. 

# Especificación de etiquetas de borde cuando sea posible
<a name="best-practices-content-6"></a>

 Se recomienda proporcionar una etiqueta de borde cuando sea posible al especificar un borde en un patrón. Considere la siguiente consulta de ejemplo, que se utiliza para vincular a todas las personas que viven en una ciudad con todas las personas que han visitado esa ciudad. 

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

 Si el modelo de gráfico vincula a las personas con nodos distintos de las ciudades a través de varias etiquetas de borde, al no especificar la etiqueta final, Neptune tendrá que evaluar rutas adicionales que posteriormente se descartarán. En la consulta anterior, al no proporcionar una etiqueta de borde, el motor primero hace un trabajo adicional y, a continuación, filtra los valores para obtener el resultado correcto. Una versión mejor de la consulta anterior podría ser: 

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

 Esto no solo ayuda en la evaluación, sino que permite al planificador de consultas crear mejores planes. Incluso se podría combinar esta práctica recomendada con las comprobaciones de etiquetas de nodos redundantes para eliminar la comprobación de la etiqueta de ciudad y escribir la consulta de la siguiente manera: 

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

# Elusión de la cláusula WITH cuando sea posible
<a name="best-practices-content-7"></a>

 La cláusula WITH en openCypher actúa como un límite donde se ejecuta todo lo que hay antes de ella y, a continuación, los valores resultantes se pasan al resto de partes de la consulta. La cláusula WITH es necesaria cuando se requiere una agregación provisional o se desea limitar el número de resultados, pero, aparte de eso, se debe intentar evitar su uso. La recomendación general consiste es eliminar estas cláusulas WITH simples (sin agregación, ordenación o limitación) para que el planificador de consultas pueda trabajar en toda la consulta y crear un plan óptimo en su conjunto. Por ejemplo, supongamos que ha escrito una consulta para obtener todas las personas que viven en`India`: 

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

 En la versión anterior, la cláusula WITH restringe la ubicación del patrón `(city)-[:part_of]->(country {name: 'India'})` (que es más restrictivo) antes de `(person)-[:lives_in]->(city)`. De este modo, el plan no es óptimo. Para optimizar esta consulta, se podría eliminar la cláusula WITH y dejar que el planificador calcule el mejor plan. 

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

# Inserción de filtros restrictivos lo antes posible en la consulta
<a name="best-practices-content-8"></a>

 Siempre que sea posible, coloque los filtros al principio de la consulta, ya que esto ayuda a reducir las soluciones intermedias que debe considerar un plan de consulta. De este modo, se necesitará menos memoria y menos recursos de computación para ejecutar la consulta. 

 El siguiente ejemplo le ayudará a comprender estas repercusiones. Supongamos que escribe una consulta para obtener todas las personas que viven en `India`. Una versión de la consulta podría ser: 

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

 La versión anterior de la consulta no es la forma más óptima de lograr este caso de uso. El filtro `country.name = 'India'` aparece más adelante en el patrón de consulta. Primero recopilará todas las personas y su lugar de residencia, las agrupará por país y, a continuación, filtrará solo por el grupo correspondiente a `country.name = India`. La forma más óptima es consultar solo las personas que viven en `India` y, a continuación, realizar la agregación de recopilación. 

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

 Por regla general, se debe colocar el filtro lo antes posible después de introducir la variable. 

# Comprobación explícita de la existencia de propiedades
<a name="best-practices-content-9"></a>

 Según la semántica de openCypher, el acceso a una propiedad equivale a una unión opcional y debe conservar todas las filas aunque la propiedad no exista. Si, a partir del esquema de gráfico, sabe que una propiedad determinada siempre existirá para esa entidad, comprobar de forma explícita la existencia de dicha propiedad permite al motor de consultas crear planes óptimos y mejorar el rendimiento. 

 Considere un modelo de gráfico en el que los nodos de tipo `person` siempre tengan una propiedad `name`. En lugar de hacer esto: 

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

 Compruebe de forma explícita la existencia de la propiedad en la consulta con una comprobación IS NOT NULL: 

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

# Elusión de rutas con nombre (a menos que sea necesario)
<a name="best-practices-content-10"></a>

 Las rutas con nombre en una consulta siempre tienen un costo adicional, lo que puede suponer penalizaciones en términos de mayor latencia y uso de memoria. Analice la siguiente consulta: 

```
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
```

 En la consulta anterior, en el supuesto de que solo queramos conocer las propiedades de los nodos, el uso de la ruta “p” es innecesario. Al especificar la ruta nombrada como una variable, la operación de agregación mediante DISTINCT resultará costosa tanto en términos de tiempo como de uso de memoria. Una versión más optimizada de la consulta anterior podría ser: 

```
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
```

# Elusión de COLLECT (DISTINCT ())
<a name="best-practices-content-11"></a>

**nota**  
A partir de la versión [1.4.7.0](engine-releases-1.4.7.0.md) del motor, esta reescritura recomendada ya no es necesaria.

 COLLECT (DISTINCT ()) se usa siempre que se va a formar una lista que contenga valores distintos. COLLECT es una función de agregación y la agrupación se realiza en función de la proyección de claves adicionales en la misma instrucción. Cuando se usa distinct, la entrada se divide en varios fragmentos, cada uno de los cuales representa un grupo para la reducción. El rendimiento se verá afectado a medida que aumente el número de grupos. En Neptune, es mucho más eficiente ejecutar DISTINCT antes que collecting/forming la lista. De este modo, la agrupación se puede realizar directamente en las claves de agrupación para todo el fragmento. 

 Analice la siguiente consulta: 

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

 Una forma más óptima de escribir esta consulta es: 

```
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
```

# Preferencia de la función de propiedades sobre la búsqueda de propiedades individuales en la recuperación de todos los valores de las propiedades
<a name="best-practices-content-12"></a>

 La función `properties()` se utiliza para devolver un mapa que contiene todas las propiedades de una entidad y es mucho más eficiente que devolver las propiedades individualmente. 

 En el supuesto de que los nodos `Person` contengan 5 propiedades, `firstName` `lastName`, `age`, `dept` y`company`, sería preferible la siguiente consulta: 

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

 En lugar de usar: 

```
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
```

# Realización de cálculos estáticos fuera de la consulta
<a name="best-practices-content-13"></a>

 Se recomienda resolver los cálculos estáticos ( mathematical/string operaciones simples) en el lado del cliente. Considere este ejemplo en el que se desea encontrar a todas las personas que son un año mayor o menor que el autor: 

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

 En este caso, `$age` se inserta en la consulta mediante parámetros y, a continuación, se añade a un valor fijo. A continuación, este valor se compara con `p.age`. En cambio, un mejor enfoque sería realizar la suma en el cliente y pasar el valor calculado como parámetro \$1ageplusone. Así, el motor de consultas puede crear planes optimizados y se evita el cálculo estático por cada fila entrante. Si se siguen estas pautas, una versión más eficiente de la consulta sería: 

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

# Entradas por lotes mediante UNWIND en lugar de instrucciones individuales
<a name="best-practices-content-14"></a>

 Siempre que sea necesario ejecutar la misma consulta para diferentes entradas, en lugar de ejecutar una consulta por cada entrada, sería mucho más eficaz ejecutar una consulta para un lote de entradas. 

 Si desea fusionar un conjunto de nodos, una opción es ejecutar una consulta merge por cada entrada: 

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

 Con los parámetros: 

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

 La consulta anterior debe ejecutarse para cada entrada. Aunque este enfoque funciona, puede requerir la ejecución de muchas consultas para un conjunto grande de entradas. En este escenario, el procesamiento por lotes puede ayudar a reducir el número de consultas ejecutadas en el servidor, así como a mejorar el rendimiento general. 

 Use el siguiente patrón: 

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

 Con los parámetros: 

```
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'}...]}
```

 Se recomienda probar con diferentes tamaños de lotes para determinar cuál funciona mejor para la carga de trabajo. 

# Prefiere usar la opción personalizada para el nodo o la relación IDs
<a name="best-practices-content-15"></a>

 Neptune permite a los usuarios asignar nodos y relaciones IDs de forma explícita. Para que sea útil, el ID debe ser único a nivel global en el conjunto de datos y determinista. Un ID determinista se puede utilizar como mecanismo de búsqueda o filtrado, al igual que las propiedades; sin embargo, desde el punto de vista de la ejecución de consultas, es mucho más óptimo el uso de un ID que el de propiedades. El uso de la personalización tiene varias ventajas: IDs 
+  Las propiedades de una entidad existente pueden ser null, pero el ID debe existir. De este modo, el motor de consultas puede utilizar una unión optimizada durante la ejecución. 
+  Cuando se ejecutan consultas de mutación simultáneas, las posibilidades de que se produzcan [excepciones de modificación simultánea](https://docs.aws.amazon.com//neptune/latest/userguide/transactions-exceptions.html) (CMEs) se reducen significativamente cuando se IDs utilizan para acceder a los nodos, ya que se utilizan menos bloqueos IDs que propiedades debido a su unicidad obligatoria. 
+  El uso IDs evita la posibilidad de crear datos duplicados, ya que Neptune impone la singularidad, a diferencia de las IDs propiedades. 

 En el siguiente ejemplo de consulta se utiliza un ID personalizado: 

**nota**  
 La propiedad `~id` se usa para especificar el ID, mientras que `id` se almacena como cualquier otra propiedad. 

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

 Si no se utiliza un ID personalizado: 

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

 En este último caso, no se garantiza la unicidad y posteriormente se podría ejecutar la consulta: 

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

 En este caso, se crea un segundo nodo con `id=1` llamado `john`. En este escenario, ahora tendrías dos nodos con `id=1`, cada uno con un nombre: (alice y john). 

# Elusión de cálculos \$1id en la consulta
<a name="best-practices-content-16"></a>

 Cuando utilice la opción personalizada IDs en las consultas, realice siempre cálculos estáticos fuera de las consultas y proporcione estos valores en los parámetros. Cuando se proporcionan valores estáticos, el motor puede optimizar mejor las búsquedas y evitar analizar y filtrar estos valores. 

 Si desea crear bordes entre los nodos que existen en la base de datos, una opción podría ser: 

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

 Con los parámetros: 

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

 En la consulta anterior, el `id` de la sección se calcula en la consulta. Dado que el cálculo es dinámico, el motor no puede insertar los ID de forma estática y termina analizando todos los nodos de sección. A continuación, el motor realiza un filtrado posterior de los nodos necesarios. Esta operación puede resultar costosa si hay muchos nodos de sección en la base de datos. 

 Una mejor forma de lograrlo es anteponer `Sec-` a los ID que se van a transferir a la base de datos: 

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

 Con los parámetros: 

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

# Actualización/fusión de varios nodos
<a name="best-practices-merge-multiple-nodes"></a>

 Al ejecutar `CREATE` consultas `MERGE` o ejecutar consultas en varios nodos, se recomienda utilizar una `UNWIND` en combinación con una sola MERGE/CREATE cláusula en lugar de utilizar una MERGE/CREATE cláusula para cada nodo. Las consultas que utilizan una cláusula para cada nodo dan lugar a un plan de ejecución ineficiente, ya que cada línea requiere de una optimización. Esto conlleva que la mayor parte del tiempo de ejecución de la consulta se dedique al procesamiento estático, en lugar de a la actualización real. 

 Una cláusula por nodo no es óptima, ya que no es escalable con el aumento del número de nodos: 

```
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'}
```

 El uso `UNWIND` de una junto con una MERGE/CREATE cláusula permite el mismo comportamiento pero un plan de ejecución más óptimo. En este sentido, la consulta modificada quedaría así: 

```
## 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')
```

# Prácticas recomendadas de Neptune con SPARQL
<a name="best-practices-sparql"></a>

Siga estas prácticas recomendadas cuando utilice el lenguaje de consulta SPARQL con Neptune. Para obtener más información sobre el uso de SPARQL en Neptune, consulte [Acceso al gráfico de Neptune con SPARQL](access-graph-sparql.md).

**Topics**
+ [Consulta de todos los gráficos con nombre de forma predeterminada](best-practices-sparql-query.md)
+ [Especificación de un gráfico con nombre para la carga](best-practices-sparql-graph.md)
+ [Elegir entre FILTER, FILTER... IN y VALUES en sus consultas](best-practices-sparql-batch.md)

# Consulta de todos los gráficos con nombre de forma predeterminada
<a name="best-practices-sparql-query"></a>

Amazon Neptune asocia cada triple con un gráfico con nombre. El gráfico predeterminado se define como la unión de todos los gráficos con nombre. 

Si envía una consulta SPARQL sin especificar explícitamente un gráfico mediante la palabra clave `GRAPH` o construcciones como `FROM NAMED`, Neptune siempre tiene en cuenta todos los triples en su instancia de base de datos. Por ejemplo, la siguiente consulta devuelve todos los triples desde un punto de conexión de SPARQL de Neptune: 

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

Los triples que aparecen en más de un gráfico se devuelven solo una vez.

Para obtener información sobre la especificación de gráfico predeterminado, consulte la sección [Conjunto de datos de RDF](https://www.w3.org/TR/sparql11-query/#rdfDataset) de la especificación del lenguaje de consulta SPARQL 1.1.

# Especificación de un gráfico con nombre para la carga
<a name="best-practices-sparql-graph"></a>

Amazon Neptune asocia cada triple con un gráfico con nombre. Si no especifica un gráfico con nombre al cargar, insertar o actualizar triples, Neptune utiliza el gráfico con nombre alternativo definido por el URI, `http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph`.

Si utiliza el programa de carga masiva de Neptune, puede especificar el gráfico con nombre que se utilizará para todos los triples (o cuádruples con la cuarta posición en blanco) utilizando el parámetro `parserConfiguration: namedGraphUri`. Para obtener información acerca de la sintaxis del comando `Load` del programa de carga de Neptune, consulte [Comando del programa de carga de Neptune](load-api-reference-load.md).

# Elegir entre FILTER, FILTER... IN y VALUES en sus consultas
<a name="best-practices-sparql-batch"></a>

Existen tres formas básicas para introducir valores en las consultas SPARQL:     `FILTER`,   `FILTER...IN`,   y   `VALUES`.

Por ejemplo, suponiendo que desee buscar los amigos de varias personas dentro de una única consulta. Con `FILTER`, podría estructurar su consulta de la siguiente manera:

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

Este devuelve todos los valores triples en el gráfico que tiene `?s` vinculado a `ex:person1` o `ex:person2` y que dispone de un borde saliente etiquetado como `foaf:knows`.

También puede crear una consulta mediante `FILTER...IN` que devuelve resultados equivalentes.

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

También puede crear una consulta mediante `VALUES` que, en este caso, también devuelve resultados equivalentes.

```
  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}}
```

Aunque en muchos casos estas consultas son equivalente semánticamente, existen algunos casos en los que las dos variantes de `FILTER` difieren de la variante `VALUES`:
+ El primer caso es cuando introduce los valores duplicados; como, por ejemplo, introducir a la misma persona dos veces. En ese caso, la consulta `VALUES` incluye los valores duplicados en su resultado. Puede eliminar los duplicados de forma explícita mediante la incorporación de un `DISTINCT` a la cláusula `SELECT`. Sin embargo, podría haber situaciones en las que realmente quiere duplicados en los resultados de la consulta para la introducción de un valor redundante.

  Sin embargo, las versiones `FILTER` y `FILTER...IN` extraen solo una vez el valor cuando el mismo valor aparece varias veces.
+ El segundo caso está relacionado con el hecho de que `VALUES` siempre realiza una coincidencia exacta, mientras que `FILTER` podría aplicar el tipo de promoción y realizar coincidencias parciales en algunos casos.

  Por ejemplo, cuando incluya una cláusula literal como `"2.0"^^xsd:float` en sus valores, una consulta `VALUES` coincide totalmente con este valor literal, incluidos el valor literal y el tipo de datos.

  En cambio, `FILTER` produce una coincidencia parcial para estos valores numéricos. Las coincidencias podría incluir valores literales con el mismo valor, pero con distintos tipos de valores numéricos, como `xsd:double`.
**nota**  
No hay ninguna diferencia entre el comportamiento de`FILTER` y `VALUES` cuando se enumeran los valores literales de las cadena o de los URI.

Las diferencias entre `FILTER` y `VALUES` pueden afectar a la optimización y a la estrategia de evaluación de consulta resultante. A menos que su caso de uso requiera una coincidencia parcial, le recomendamos que utilice `VALUES`, ya que evita examinar los casos especiales relacionados con la conversión de tipos. Como resultado, `VALUES` produce a menudo una consulta más eficaz, que se ejecuta más rápido y es más económica.