MemoryStoreService est un service de données à fort débit et faible latence qui fournit un stockage de données rapide en mémoire accessible depuis tous les serveurs lors d'une session en direct. Les magasins mémoire sont adaptés aux données fréquentes et éphémères qui changent rapidement et n'ont pas besoin d'être durables, car elles sont plus rapides d'accès et disparaissent lorsqu'elles atteignent la durée de vie maximale. Pour les données qui doivent persister entre les sessions, utilisez les magasins de données.
Structures de données
Au lieu d'accéder directement à des données brutes, les magasins mémoire possèdent trois structures de données primitives partagées entre les serveurs pour un traitement rapide : carte triée, file d'attente, et carte de hachage. Chaque structure de données est bien adaptée à certains cas d'utilisation :
- Matchmaking basé sur les compétences - Enregistrez les informations d'utilisateur, telles que le niveau de compétence, dans une file d'attente partagée entre les serveurs, et utilisez les serveurs de salle d'attente pour exécuter le matchmaking périodiquement.
- Échange et enchères inter-serveurs - Permettez un échange universel entre différents serveurs, où les utilisateurs peuvent enchérir sur des objets avec des prix variant en temps réel, à l'aide d'une carte triée de paires clé-valeur.
- Classements mondiaux - Stockez et mettez à jour les classements des utilisateurs dans un classement partagé à l'intérieur d'une carte triée.
- Inventaires partagés - Enregistrez les objets d'inventaire et les statistiques dans une carte de hachage partagée, où les utilisateurs peuvent utiliser des objets d'inventaire en même temps.
- Cache pour données persistantes - Synchronisez et copiez vos données persistantes dans un magasin de données vers une carte de hachage de magasin mémoire qui peut agir comme un cache et améliorer la performance de votre jeu.
En général, si vous avez besoin d'accéder aux données en fonction d'une clé spécifique, utilisez une carte de hachage. Si vous avez besoin que ces données soient ordonnées, utilisez une carte triée. Si vous devez traiter vos données dans un ordre spécifique, utilisez une file d'attente.
Limites et quotas
Pour maintenir l'évolutivité et la performance du système, les magasins mémoire ont des quotas d'utilisation des données pour la taille de la mémoire, les requêtes API et la taille de la structure de données.
Les magasins mémoire ont une politique d'éviction basée sur le temps d'expiration, également connu sous le nom de temps de vie (TTL). Les éléments sont évincés après leur expiration, et le quota de mémoire est libéré pour de nouvelles entrées. Lorsque vous atteignez la limite mémoire, toutes les requêtes d'écriture suivantes échouent jusqu'à ce que les éléments expirent ou que vous les supprimiez manuellement.
Quota de taille mémoire
Le quota de mémoire limite la quantité totale de mémoire qu'un jeu peut consommer. Ce n'est pas une valeur fixe ; au lieu de cela, elle change au fil du temps en fonction du nombre d'utilisateurs dans le jeu selon la formule 64 Ko + 1,2 Ko * [nombre d'utilisateurs]. Le quota s'applique au niveau du jeu plutôt qu'au niveau du serveur.
Lorsque des utilisateurs rejoignent le jeu, le quota de mémoire supplémentaire est disponible immédiatement. Lorsque des utilisateurs quittent le jeu, le quota ne diminue pas immédiatement. Il y a une période de rétroaction de huit jours avant que le quota soit réévalué à une valeur inférieure.
Après que votre jeu atteigne le quota de taille mémoire, toutes les requêtes API qui augmentent la taille de la mémoire échouent toujours. Les requêtes qui diminuent ou ne modifient pas la taille de la mémoire réussissent toujours.
Avec le tableau de bord observabilité, vous pouvez voir le quota de taille mémoire de votre jeu en temps réel à l'aide du graphique Utilisation de la mémoire.
Limites de requêtes API
Un quota de unités de requête s'applique à tous les appels API de MemoryStoreService. Ce quota est de 1000 + 120 * [nombre d'utilisateurs simultanés] unités de requête par minute.
La plupart des appels API ne consomment qu'une unité de requête, à quelques exceptions près :
MemoryStoreSortedMap:GetRangeAsync()
Consomme des unités en fonction du nombre d'éléments retournés. Par exemple, si cette méthode retourne 10 éléments, l'appel compte comme 10 unités de requête. S'il retourne une réponse vide, il compte comme une unité de requête.
Consomme des unités en fonction du nombre d'éléments retournés, tout comme MemoryStoreSortedMap:GetRangeAsync(), mais consomme une unité supplémentaire toutes les deux secondes pendant que la lecture est en cours. Spécifiez le temps de lecture maximal avec le paramètre waitTimeout.
MemoryStoreHashMap:UpdateAsync()
Consomme un minimum de deux unités.
MemoryStoreHashMap:ListItemsAsync()
Consomme [nombre de partitions scannées] + [éléments retournés] unités.
Le quota de requêtes s'applique également au niveau du jeu plutôt qu'au niveau du serveur. Cela offre la flexibilité d'allouer les requêtes entre les serveurs tant que le taux de requêtes total ne dépasse pas le quota. Si vous dépassez le quota, vous recevrez une réponse d'erreur lorsque le service limitera vos requêtes.
Avec la fonction d'observabilité disponible, vous pouvez voir le quota d'unités de requête de votre jeu en temps réel.
Limites de taille de structure de données
Pour une seule carte triée ou file d'attente, les limites de taille et de nombre d'éléments suivantes s'appliquent :
- Nombre maximum d'éléments : 1 000 000
- Taille totale maximum (y compris les clés pour la carte triée) : 100 Mo
Limites par partition
Voir limites par partition.
Meilleures pratiques
Pour garder votre utilisation de mémoire optimale et éviter d'atteindre les limites, suivez ces meilleures pratiques :
Supprimez les éléments traités. Nettoyer de manière cohérente les éléments lus en utilisant la méthode MemoryStoreQueue:RemoveAsync() pour les files d'attente et MemoryStoreSortedMap:RemoveAsync() pour les cartes triées peut libérer de la mémoire et tenir la structure de données à jour.
Réglez le temps d'expiration au plus petit intervalle de temps possible lors de l'ajout de données. Bien que le temps d'expiration par défaut soit de 45 jours pour MemoryStoreQueue:AddAsync() et MemoryStoreSortedMap:SetAsync(), définir le temps le plus court possible peut nettoyer automatiquement les anciennes données pour éviter qu'elles ne remplissent votre quota d'utilisation mémoire.
- Ne stockez pas une grande quantité de données avec un long expiration, car cela risque de dépasser votre quota mémoire et de causer potentiellement des problèmes qui peuvent casser votre jeu entier.
- Supprimez toujours les éléments non nécessaires ou définissez une courte expiration des éléments.
- En général, vous devriez utiliser la suppression explicite pour libérer de la mémoire et l'expiration des éléments comme un mécanisme de sécurité pour éviter que des éléments inutilisés n'occupent la mémoire pendant une longue période.
Ne gardez en mémoire que les valeurs nécessaires.
Par exemple, pour un jeu de maison de vente aux enchères, vous devez seulement maintenir la plus haute enchère. Vous pouvez utiliser MemoryStoreSortedMap:UpdateAsync() sur une clé pour garder la plus haute enchère plutôt que de conserver toutes les enchères dans votre structure de données.
Utilisez un rétrogradage exponentiel pour rester en dessous des limites de requêtes API.
Par exemple, si vous recevez un DataUpdateConflict, vous pourriez réessayer après deux secondes, puis quatre, huit, etc. plutôt que d'envoyer constamment des requêtes à MemoryStoreService pour obtenir la réponse correcte.
Séparez les grandes structures de données en plusieurs plus petites par sharding.
Il est souvent plus facile de gérer des données dans des structures plus petites plutôt que de tout stocker dans une grande structure de données. Cette approche peut également aider à éviter les limites d'utilisation et de taux. Par exemple, si vous avez une carte triée qui utilise des préfixes pour ses clés, envisagez de séparer chaque préfixe dans sa propre carte triée. Pour un jeu particulièrement populaire, vous pourriez même séparer les utilisateurs en plusieurs cartes basées sur les derniers chiffres de leurs identifiants d'utilisateur.
Shard les clés fréquemment accédées dans des cartes de hachage avec plusieurs copies de la clé pour répartir la charge.
Compressez les valeurs stockées.
Par exemple, envisagez d'utiliser l'algorithme LZW pour réduire la taille des valeurs stockées.
Inscrivez-vous aux services étendus.
Vous pouvez augmenter vos quotas de stock et de limites de requêtes en vous inscrivant sur les services étendus.
Observabilité
Le tableau de bord d'observabilité fournit des insights et des analyses pour surveiller et dépanner votre utilisation du magasin mémoire. Avec des graphiques se mettant à jour en temps réel sur différents aspects de votre utilisation de mémoire et des requêtes API, vous pouvez suivre le modèle d'utilisation de mémoire de votre jeu, visualiser les quotas alloués actuels, surveiller l'état de l'API, et identifier les problèmes potentiels pour l'optimisation des performances.
Le tableau suivant répertorie et décrit tous les codes de statut des réponses API disponibles sur les graphiques Nombre de demandes par statut et Demandes par API x Statut du tableau de bord d'observabilité. Pour plus d'informations sur la façon de résoudre ces erreurs, voir Dépannage. Pour le quota ou la limite spécifique à laquelle une erreur se rapporte, voir Limites et Quotas.
| Code de statut | Description |
|---|---|
| Succès | Succès. |
| DataStructureMemoryOverLimit | Dépasse la limite de taille mémoire de niveau de structure de données (100 Mo). |
| DataUpdateConflict | Conflit dû à une mise à jour concurrente. |
| AccessDenied | Non autorisé à accéder aux données du jeu. Cette demande ne consomme pas d'unités de requêtes ni n'utilise de quota. |
| InternalError | Erreur interne. |
| InvalidRequest | La demande ne contient pas les informations requises ou comporte des informations mal formées. |
| DataStructureItemsOverLimit | Dépasse la limite de nombre d'éléments de niveau de structure de données (1 M). |
| NoItemFound | Aucun élément trouvé dans MemoryStoreQueue:ReadAsync() ou MemoryStoreSortedMap:UpdateAsync(). ReadAsync() interroge toutes les 2 secondes et retourne ce code de statut jusqu'à ce qu'il trouve des éléments dans la file d'attente. |
| DataStructureRequestsOverLimit | Dépasse la limite d'unités de requête de niveau de structure de données (100 000 unités de requête par minute). |
| PartitionRequestsOverLimit | Dépasse la limite d'unités de requête par partition. |
| TotalRequestsOverLimit | Dépasse la limite d'unités de requête de niveau univers. |
| TotalMemoryOverLimit | Dépasse le quota de mémoire de niveau univers. |
| ItemValueSizeTooLarge | La taille de la valeur dépasse la limite (32 Ko). |
Le tableau suivant répertorie les codes d'état du côté client, qui ne sont actuellement pas disponibles sur le tableau de bord d'observabilité.
| Code de statut | Description |
|---|---|
| InternalError | Erreur interne. |
| UnpublishedPlace | Vous devez publier cet endroit pour utiliser MemoryStoreService. |
| InvalidClientAccess | MemoryStoreService doit être appelé depuis le serveur. |
| InvalidExpirationTime | Le champ 'expiration' doit être compris entre 0 et 3 888 000. |
| InvalidRequest | Impossible de convertir la valeur en json. |
| InvalidRequest | Impossible de convertir sortKey en un nombre ou une chaîne valide. |
| TransformCallbackFailed | Échec de l'invocation de la fonction de rappel de transformation. |
| RequestThrottled | Des requêtes récentes de MemoryStores ont atteint une ou plusieurs limites. |
| UpdateConflict | Nombre maximal de tentatives dépassé. |
Dépannage
Le tableau suivant répertorie et décrit la solution recommandée pour chaque code de statut de réponse :
| Erreur | Options de dépannage |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit | |
| DataUpdateConflict |
|
| Erreur interne |
|
| InvalidRequest |
|
| ItemValueSizeTooLarge |
|
Tester et déboguer dans Studio
Les données dans MemoryStoreService sont isolées entre Studio et la production, donc modifier les données dans Studio n'affecte pas le comportement en production. Cela signifie que vos appels API depuis Studio n'accèdent pas aux données de production, vous permettant de tester en toute sécurité les magasins mémoire et les nouvelles fonctionnalités avant de passer à la production.
Les tests en Studio ont les mêmes limites et quotas que la production. Pour les quotas calculés en fonction du nombre d'utilisateurs, le quota résultant peut être très faible puisque vous êtes le seul utilisateur lors des tests en Studio. Lorsque vous testez depuis Studio, vous pourriez également remarquer une latence légèrement plus élevée et des taux d'erreur accrus par rapport à l'utilisation en production en raison de certains contrôles supplémentaires effectués pour vérifier l'accès et les autorisations.
Pour des informations sur la façon de déboguer un magasin mémoire sur des jeux en direct ou lors de tests dans Studio, utilisez Console de développement.