MemoryStoreService es un servicio de datos de alto rendimiento y baja latencia que proporciona un almacenamiento de datos en memoria rápido y accesible desde todos los servidores en una sesión en vivo. Almacenes de Memoria son adecuados para datos frecuentes y efímeros que cambian rápidamente y no necesitan ser duraderos, porque son más rápidos de acceder y desaparecen al alcanzar la vida útil máxima. Para datos que necesitan persistir entre sesiones, utiliza almacenes de datos.
Estructuras de datos
En lugar de acceder directamente a datos en bruto, los almacenes de memoria tienen tres estructuras de datos primitivas compartidas entre servidores para un procesamiento rápido: mapa ordenado, cola y mapa hash. Cada estructura de datos es adecuada para ciertos casos de uso:
- Emparejamiento basado en habilidades - Guarda información del usuario, como el nivel de habilidad, en una cola compartida entre servidores, y utiliza servidores de vestíbulo para ejecutar el emparejamiento periódicamente.
- Comercio y subastas entre servidores - Permite el comercio universal entre diferentes servidores, donde los usuarios pueden pujar por artículos con precios que cambian en tiempo real, utilizando un mapa ordenado de pares clave-valor.
- Clasificaciones globales - Almacena y actualiza las clasificaciones de los usuarios en una tabla de clasificación compartida dentro de un mapa ordenado.
- Inventarios compartidos - Guarda artículos de inventario y estadísticas en un mapa hash compartido, donde los usuarios pueden utilizar artículos de inventario simultáneamente entre sí.
- Caché para datos persistentes - Sincroniza y copia tus datos persistentes en un almacén de datos a un mapa hash de almacén de memoria que puede actuar como una caché y mejorar el rendimiento de tu juego.
En general, si necesitas acceder a datos basados en una clave específica, usa un mapa hash. Si necesitas que esos datos estén ordenados, usa un mapa ordenado. Si necesitas procesar tus datos en un orden específico, usa una cola.
Límites y cuotas
Para mantener la escalabilidad y el rendimiento del sistema, los almacenes de memoria tienen cuotas de uso de datos para el tamaño de la memoria, solicitudes de API y el tamaño de la estructura de datos.
Los almacenes de memoria tienen una política de desalojo basada en el tiempo de expiración, también conocido como tiempo de vida (TTL). Los elementos se desalojan después de que expiran, y la cuota de memoria se libera para nuevas entradas. Cuando alcanzas el límite de memoria, todas las solicitudes de escritura subsiguientes fallan hasta que los elementos expiran o los eliminas manualmente.
Cuota de tamaño de memoria
La cuota de memoria limita la cantidad total de memoria que un juego puede consumir. No es un valor fijo; en cambio, cambia con el tiempo dependiendo del número de usuarios en el juego según la fórmula 64 KB + 1.2 KB * [número de usuarios]. La cuota se aplica a nivel de juego en lugar de a nivel de servidor.
Cuando los usuarios se unen al juego, la cuota de memoria adicional está disponible de inmediato. Cuando los usuarios abandonan el juego, la cuota no se reduce de inmediato. Hay un período de retroceso de ocho días antes de que la cuota se reevalúe a un valor más bajo.
Después de que tu juego alcanza la cuota de tamaño de memoria, cualquier solicitud de API que aumente el tamaño de la memoria siempre falla. Las solicitudes que disminuyen o no cambian el tamaño de la memoria aún tienen éxito.
Con el panel de observabilidad, puedes ver la cuota de tamaño de memoria de tu juego en tiempo real utilizando el gráfico de Uso de Memoria.
Límites de solicitudes de API
Una cuota de unidad de solicitud se aplica a todas las llamadas de API de MemoryStoreService. Esta cuota es 1000 + 120 * [número de usuarios concurrentes] unidades de solicitud por minuto.
La mayoría de las llamadas de API solo consumen una unidad de solicitud, con algunas excepciones:
MemoryStoreSortedMap:GetRangeAsync()
Consume unidades basadas en el número de elementos devueltos. Por ejemplo, si este método devuelve 10 elementos, la llamada cuenta como 10 unidades de solicitud. Si devuelve una respuesta vacía, cuenta como una unidad de solicitud.
Consume unidades basadas en el número de elementos devueltos, al igual que MemoryStoreSortedMap:GetRangeAsync(), pero consume una unidad adicional cada dos segundos mientras lee. Especifica el tiempo máximo de lectura con el parámetro waitTimeout.
MemoryStoreHashMap:UpdateAsync()
Consume un mínimo de dos unidades.
MemoryStoreHashMap:ListItemsAsync()
Consume [número de particiones escaneadas] + [elementos devueltos] unidades.
La cuota de solicitudes también se aplica a nivel de juego en lugar de a nivel de servidor. Esto proporciona flexibilidad para asignar las solicitudes entre servidores siempre que la tasa total de solicitudes no exceda la cuota. Si superas la cuota, recibes una respuesta de error cuando el servicio limita tus solicitudes.
Con la función de observabilidad disponible, puedes ver la cuota de unidad de solicitud de tu juego en tiempo real.
Límites de tamaño de estructura de datos
Para un solo mapa ordenado o cola, se aplican los siguientes límites de tamaño y recuento de elementos:
- Número máximo de elementos: 1,000,000
- Tamaño total máximo (incluyendo claves para el mapa ordenado): 100 MB
Límites por partición
Consulta límites por partición.
Mejores prácticas
Para mantener tu patrón de uso de memoria óptimo y evitar alcanzar los límites, sigue estas mejores prácticas:
Elimina elementos procesados. Limpiar consistentemente los elementos leídos utilizando el método MemoryStoreQueue:RemoveAsync() para colas y MemoryStoreSortedMap:RemoveAsync() para mapas ordenados puede liberar memoria y mantener la estructura de datos actualizada.
Establece el tiempo de expiración al marco de tiempo más pequeño posible al agregar datos. Aunque el tiempo de expiración predeterminado es de 45 días tanto para MemoryStoreQueue:AddAsync() como para MemoryStoreSortedMap:SetAsync(), establecer el tiempo más corto posible puede limpiar automáticamente los datos antiguos para evitar que llenen tu cuota de uso de memoria.
- No almacenes una gran cantidad de datos con una larga expiración, ya que corre el riesgo de exceder tu cuota de memoria y potencialmente causar problemas que pueden romper tu juego completo.
- Siempre elimina explícitamente los elementos innecesarios o establece una corta expiración de elementos.
- En general, deberías usar la eliminación explícita para liberar memoria y la expiración de elementos como un mecanismo de seguridad para evitar que los elementos no utilizados ocupen memoria durante un período prolongado.
Solo mantén valores necesarios en memoria.
Por ejemplo, para un juego de casa de subastas, solo necesitas mantener la puja más alta. Puedes usar MemoryStoreSortedMap:UpdateAsync() en una clave para mantener la puja más alta en lugar de mantener todas las pujas en tu estructura de datos.
Usa retroceso exponencial para ayudar a mantenerte por debajo de los límites de solicitudes de API.
Por ejemplo, si recibes un DataUpdateConflict, podrías reintentar después de dos segundos, luego cuatro, ocho, etc. en lugar de enviar constantemente solicitudes a MemoryStoreService para obtener la respuesta correcta.
Divide estructuras de datos gigantes en varias más pequeñas mediante sharding.
A menudo es más fácil gestionar datos en estructuras más pequeñas en lugar de almacenar todo en una gran estructura de datos. Este enfoque también puede ayudar a evitar límites de uso y tasa. Por ejemplo, si tienes un mapa ordenado que utiliza prefijos para sus claves, considera separar cada prefijo en su propio mapa ordenado. Para un juego especialmente popular, incluso podrías separar a los usuarios en múltiples mapas basados en los últimos dígitos de sus ID de usuario.
Shard claves de acceso frecuentes en mapas hash con múltiples copias de la clave para distribuir la carga.
Comprime los valores almacenados.
Por ejemplo, considera usar el algoritmo LZW para reducir el tamaño del valor almacenado.
Inscríbete en Servicios Extendidos.
Puedes aumentar tus cuotas de Almacenamiento y Límite de Solicitudes al unirte a Servicios Extendidos.
Observabilidad
El Panel de Observabilidad proporciona información y análisis para monitorear y solucionar problemas de uso de tu almacén de memoria. Con gráficos que se actualizan en tiempo real sobre diferentes aspectos de tu uso de memoria y solicitudes de API, puedes rastrear el patrón de uso de memoria de tu juego, ver las cuotas asignadas actuales, monitorear el estado de la API e identificar problemas potenciales para la optimización del rendimiento.
La siguiente tabla enumera y describe todos los códigos de estado de las respuestas de API disponibles en los gráficos de Conteo de Solicitudes por Estado y Solicitudes por API x Estado del Panel de Observabilidad. Para obtener más información sobre cómo resolver estos errores, consulta Solución de problemas. Para la cuota o límite específico al que se relaciona un error, consulta Límites y Cuotas.
| Código de estado | Descripción |
|---|---|
| Éxito | Éxito. |
| DataStructureMemoryOverLimit | Excede el límite de tamaño de memoria a nivel de estructura de datos (100 MB). |
| DataUpdateConflict | Conflicto debido a actualización concurrente. |
| AccessDenied | No autorizado para acceder a los datos del juego. Esta solicitud no consume unidades de solicitud ni utiliza cuota. |
| InternalError | Error interno. |
| InvalidRequest | La solicitud no tiene la información requerida o tiene información mal formada. |
| DataStructureItemsOverLimit | Excede el límite de recuento de elementos a nivel de estructura de datos (1M). |
| NoItemFound | No se encontró ningún elemento en MemoryStoreQueue:ReadAsync() o MemoryStoreSortedMap:UpdateAsync(). ReadAsync() sondea cada 2 segundos y devuelve este código de estado hasta que encuentra elementos en la cola. |
| DataStructureRequestsOverLimit | Excede el límite de unidades de solicitud a nivel de estructura de datos (100,000 unidades de solicitud por minuto). |
| PartitionRequestsOverLimit | Excede el límite de unidades de solicitud por partición. |
| TotalRequestsOverLimit | Excede el límite de unidades de solicitud a nivel de universo. |
| TotalMemoryOverLimit | Excede la cuota de memoria a nivel de universo. |
| ItemValueSizeTooLarge | El tamaño del valor excede el límite (32 KB). |
La siguiente tabla enumera los códigos de estado desde el lado del cliente, que actualmente no están disponibles en el Panel de Observabilidad.
| Código de estado | Descripción |
|---|---|
| InternalError | Error interno. |
| UnpublishedPlace | Debes publicar este lugar para usar MemoryStoreService. |
| InvalidClientAccess | MemoryStoreService debe ser llamado desde el servidor. |
| InvalidExpirationTime | El campo 'expiration' debe estar entre 0 y 3,888,000. |
| InvalidRequest | No se puede convertir el valor a json. |
| InvalidRequest | No se puede convertir sortKey a un número o cadena válida. |
| TransformCallbackFailed | Error al invocar la función de devolución de llamada de transformación. |
| RequestThrottled | Las solicitudes recientes de MemoryStores alcanzaron uno o más límites. |
| UpdateConflict | Se superó el número máximo de reintentos. |
Solución de problemas
La siguiente tabla enumera y describe la solución recomendada para cada código de estado de respuesta:
| Error | Opciones de solución de problemas |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit | |
| DataUpdateConflict |
Ejemplo de Abortar Solicitud |
| Error interno |
|
| InvalidRequest |
|
| ItemValueSizeTooLarge |
|
Prueba y depuración en Studio
Los datos en MemoryStoreService están aislados entre Studio y producción, por lo que cambiar los datos en Studio no afecta el comportamiento de producción. Esto significa que tus llamadas de API desde Studio no acceden a datos de producción, lo que te permite probar de manera segura los almacenes de memoria y nuevas características antes de pasar a producción.
Las pruebas en Studio tienen los mismos límites y cuotas que la producción. Para las cuotas calculadas en función del número de usuarios, la cuota resultante puede ser muy pequeña ya que eres el único usuario para las pruebas en Studio. Al probar desde Studio, también podrías notar una latencia ligeramente mayor y tasas de error elevadas en comparación con el uso en producción debido a algunas verificaciones adicionales que se realizan para verificar el acceso y los permisos.
Para obtener información sobre cómo depurar un almacén de memoria en juegos en vivo o al probar en studio, utiliza Consola de Desarrollador.