MemoryStoreService est un service de données à haut débit et faible latence qui fournit un stockage de données en mémoire rapide accessible depuis tous les serveurs dans une session en direct. Les magasins de 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 ils sont plus rapides d'accès et disparaissent lorsqu'ils 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 aux données brutes, les magasins de mémoire disposent de 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 :
- Appariement basé sur les compétences - Enregistrez les informations des utilisateurs, telles que le niveau de compétence, dans une file d'attente partagée entre les serveurs, et utilisez des serveurs de lobby pour exécuter l'appariement périodiquement.
- Échanges 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 changeant en temps réel, avec une carte triée de paires clé-valeur.
- Classements mondiaux - Stockez et mettez à jour les classements des utilisateurs sur 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 les objets d'inventaire simultanément les uns avec les autres.
- Cache pour les données persistantes - Synchronisez et copiez vos données persistantes dans un magasin de données vers une carte de hachage de magasin de mémoire qui peut agir comme un cache et améliorer les performances de votre jeu.
En général, si vous devez 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 la scalabilité et la performance du système, les magasins de 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 de 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 de 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 de 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 retour de huit jours avant que le quota ne soit réévalué à une valeur inférieure.
Après que votre jeu ait atteint le quota de taille de mémoire, toutes les requêtes API qui augmentent la taille de la mémoire échouent toujours. Les requêtes qui diminuent ou ne changent pas la taille de la mémoire réussissent toujours.
Avec le tableau de bord d'observabilité, vous pouvez voir le quota de taille de 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é 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, avec quelques exceptions :
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, cela 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 la lecture. Spécifiez le temps de lecture maximum 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 une flexibilité pour répartir les requêtes entre les serveurs tant que le taux total de requêtes ne dépasse pas le quota. Si vous dépassez le quota, vous recevez une réponse d'erreur lorsque le service limite vos requêtes.
Avec la fonctionnalité 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 maximale (y compris les clés pour la carte triée) : 100 Mo
Limites par partition
Voir les limites par partition.
Meilleures pratiques
Pour garder votre modèle d'utilisation de la mémoire optimal et éviter d'atteindre les limites, suivez ces meilleures pratiques :
Supprimez les éléments traités. Nettoyer régulièrement 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 garder la structure de données à jour.
Définissez le temps d'expiration au plus petit intervalle 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 automatiquement nettoyer les anciennes données pour éviter qu'elles ne remplissent votre quota d'utilisation de mémoire.
- Ne stockez pas une grande quantité de données avec une longue expiration, car cela risque de dépasser votre quota de mémoire et de potentiellement causer des problèmes qui peuvent casser votre jeu entier.
- Supprimez toujours explicitement les éléments non nécessaires ou définissez une courte expiration pour les é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 empêcher les éléments inutilisés d'occuper de la mémoire pendant une période prolongée.
Ne gardez que les valeurs nécessaires en mémoire.
Par exemple, pour un jeu de maison de vente aux enchères, vous n'avez besoin de maintenir que la plus haute enchère. Vous pouvez utiliser MemoryStoreSortedMap:UpdateAsync() sur une clé pour garder la plus haute enchère plutôt que de garder toutes les enchères dans votre structure de données.
Utilisez un retour exponentiel pour aider à 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 bonne réponse.
Divisez les grandes structures de données en plusieurs plus petites en shardant.
Il est souvent plus facile de gérer les 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 en fonction des derniers chiffres de leurs identifiants d'utilisateur.
Shard les clés fréquemment accédées dans les 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 Limite de Stockage et de Requête en vous inscrivant aux Services Étendus.
Observabilité
Le Tableau de bord d'Observabilité fournit des informations et des analyses pour surveiller et dépanner votre utilisation des magasins de mémoire. Avec des graphiques se mettant à jour en temps réel sur différents aspects de votre utilisation de la mémoire et des requêtes API, vous pouvez suivre le modèle d'utilisation de la mémoire de votre jeu, voir 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 d'état des réponses API disponibles sur les graphiques Nombre de requêtes par statut et Requêtes 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 d'état | Description |
|---|---|
| Succès | Succès. |
| DataStructureMemoryOverLimit | Dépasse la limite de taille de mémoire au niveau de la structure de données (100 Mo). |
| DataUpdateConflict | Conflit dû à une mise à jour concurrente. |
| AccessDenied | Non autorisé à accéder aux données du jeu. Cette requête ne consomme pas d'unités de requête ni n'utilise de quota. |
| InternalError | Erreur interne. |
| InvalidRequest | La requête n'a pas les informations requises ou a des informations mal formées. |
| DataStructureItemsOverLimit | Dépasse la limite de nombre d'éléments au niveau de la structure de données (1M). |
| NoItemFound | Aucun élément trouvé dans MemoryStoreQueue:ReadAsync() ou MemoryStoreSortedMap:UpdateAsync(). ReadAsync() interroge toutes les 2 secondes et retourne ce code d'état jusqu'à ce qu'il trouve des éléments dans la file d'attente. |
| DataStructureRequestsOverLimit | Dépasse la limite d'unités de requête au niveau de la structure de données (100 000 unités de requête par minute). |
| PartitionRequestsOverLimit | Dépasse la limite d'unités de requête de partition. |
| TotalRequestsOverLimit | Dépasse la limite d'unités de requête au niveau de l'univers. |
| TotalMemoryOverLimit | Dépasse le quota de mémoire au niveau de l'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 d'état | 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 | Les requêtes récentes de MemoryStores ont atteint une ou plusieurs limites. |
| UpdateConflict | Dépassement du nombre maximum de tentatives. |
Dépannage
Le tableau suivant répertorie et décrit la solution recommandée pour chaque code d'état de réponse :
| Erreur | Options de dépannage |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit | |
| DataUpdateConflict |
Exemple d'annulation de requête |
| Erreur interne |
|
| InvalidRequest |
|
| ItemValueSizeTooLarge |
|
Tester et déboguer dans Studio
Les données dans MemoryStoreService sont isolées entre Studio et la production, donc changer les données dans Studio n'affecte pas le comportement de 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 de 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 pour les 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 certaines vérifications supplémentaires effectuées pour vérifier l'accès et les autorisations.
Pour des informations sur la façon de déboguer un magasin de mémoire sur des jeux en direct ou lors de tests dans Studio, utilisez la Console de Développeur.