web-dev-qa-db-fra.com

La session ou l'application de portée MemoryCache est-elle étendue?

J'utilise MemoryCache dans ASP.NET et cela fonctionne bien. J'ai un objet qui est mis en cache pendant une heure pour empêcher de nouvelles extractions de données du référentiel.

Je peux voir la mise en cache fonctionner dans le débogage, mais aussi une fois déployée sur le serveur, après le premier appel et la mise en cache de l'objet, les appels suivants sont environ 1/5 du temps.

Cependant, je remarque que chaque nouvea appel client (toujours dans cette fenêtre d'une heure - en fait juste une minute ou 2 plus tard) semble avoir le premier appel à mon service (qui fait la mise en cache) prenant presque aussi longtemps que l'appel d'origine avant la mise en cache des données.

Cela m'a fait me demander - est MemoryCache spécifique à la session, et chaque nouveau client passant l'appel stocke son propre cache, ou quelque chose d'autre se passe-t-il pour que le premier appel prenne si longtemps même après Je sais que les données ont été mises en cache?

45
atconway

De MSDN :

Les principales différences entre les classes Cache et MemoryCache sont que la classe MemoryCache a été modifiée pour la rendre utilisable par les applications .NET Framework qui ne sont pas des applications ASP.NET. Par exemple, la classe MemoryCache n'a aucune dépendance sur l'assembly System.Web. Une autre différence est que vous pouvez créer plusieurs instances de la classe MemoryCache à utiliser dans la même application et dans la même instance AppDomain.

En lisant cela et en faisant des recherches dans le code réfléchi, il est évident que MemoryCache n'est qu'une simple classe. Vous pouvez utiliser la propriété MemoryCache.Default Pour (ré) utiliser la même instance ou vous pouvez construire autant d'instances que vous le souhaitez (bien que recommandé soit le moins possible).

Donc, fondamentalement, la réponse réside dans votre code.
Si vous utilisez MemoryCache.Default, Votre cache dure aussi longtemps que votre pool d'applications. (Juste pour vous rappeler que le délai d'inactivité du pool d'applications par défaut est de 20 minutes, ce qui correspond à moins d'une heure.)

Si vous le créez à l'aide de new MemoryCache(string, NameValueCollection), les considérations mentionnées ci-dessus s'appliquent ainsi que le contexte dans lequel vous créez votre instance, c'est-à-dire si vous créez votre instance à l'intérieur du contrôleur (ce qui, je l'espère, n'est pas le cas), votre cache vit pour une requête

Dommage que je ne trouve aucune référence, mais ... MemoryCache ne garantit pas de conserver les données selon une politique de cache que vous spécifiez. En particulier, si la machine sur laquelle vous exécutez votre application est sollicitée par la mémoire, votre cache peut être supprimé.

Si vous n'avez toujours pas de chance de trouver la raison de l'invalidation précoce des éléments du cache, vous pouvez profiter de RemoveCallback et rechercher la raison de l'invalidation des éléments.

70
Ramunas

En examinant cela un an plus tard, j'ai découvert plus d'informations sur mon message d'origine sur le cache "tombant" au hasard. Le MSDN indique ce qui suit pour les propriétés de cache configurables CacheMemoryLimitMegabytes et PhysicalMemoryLimitPercentage:

La valeur par défaut est 0, ce qui signifie que les heuristiques de taille automatique de la classe MemoryCache sont utilisées par défaut.

En procédant à une décompilation et à une enquête, il existe des scénarios prédéterminés profondément dans le CacheMemoryMonitor.cs classe qui définit les seuils de mémoire. Voici un échantillon des commentaires de cette classe sur la propriété AutoPrivateBytesLimit:

// Auto-generate the private bytes limit:
// - On 64bit, the auto value is MIN(60% physical_ram, 1 TB)
// - On x86, for 2GB, the auto value is MIN(60% physical_ram, 800 MB)
// - On x86, for 3GB, the auto value is MIN(60% physical_ram, 1800 MB)
//
// - If it's not a hosted environment (e.g. console app), the 60% in the above
//   formulas will become 100% because in un-hosted environment we don't launch
//   other processes such as compiler, etc.

Ce n'est pas nécessairement que les valeurs spécifiques sont autant importantes que de comprendre pourquoi le cache est souvent utilisé: pour stocker grand objets que nous ne voulons pas récupérer encore et encore. Si ces grands objets sont stockés dans le cache et que le seuil de mémoire des environnements d'hébergement basé sur ces calculs internes est dépassé, vous pouvez faire supprimer l'élément du cache automatiquement . Cela pourrait certainement expliquer mon OP car je stockais une très grande collection en mémoire sur un serveur hébergé avec probablement 2 Go de mémoire exécutant plusieurs applications dans IIS.

Il existe un remplacement explicite pour définir ces valeurs. Vous pouvez via la configuration (ou lors de la configuration de l'instance MemoryCache) définir les valeurs CacheMemoryLimitMegabytes et PhysicalMemoryLimitPercentage. Voici un exemple modifié du lien MSDN où j'ai défini le physicalMemoryPercentage sur 95 (%):

<configuration>
  <system.runtime.caching>
    <memoryCache>
      <namedCaches>
          <add name="default" 
               physicalMemoryLimitPercentage="95" />
      </namedCaches>
    </memoryCache>
  </system.runtime.caching>
</configuration>
29
atconway