Question Comment implémenter un cache ?

Plus d'informations
il y a 6 ans 1 mois #18180 par Laurent Dardenne
Voici un exemple d'une implémentation simple d'un mécanisme de cache sous Powershell.
L'objectif est de limiter les accès à une source de données locale ou distante :
[code:1]
function Reset-MyCache {
#cache basé sur une hashtable
# NE PAS utiliser de collection de type System.Array
$script:ObjectCache=@{}
}

function Get-MyObject {
param ([int] $Key)

Write-Verbose \"On appel le provider/la source de l'objet '$Key'\"
return \"Elément $Key\"
}

function faitQqChose {
Param ($Identity)

#Accède au cache
#renvoi un objet selon sa clé ou $null s'il n'existe pas
$MyInformations=$script:ObjectCache.Item($Identity)
$isObjectInCache=$MyInformations -ne $null

Write-Verbose \"L'objet '$Identity' est-il présent dans le cache ? $isObjectInCache\"

if (!$isObjectInCache)
{
$NewObject=Get-MyObject $Identity
#suite...
}
else
{
Write-Verbose \"Récupère l'objet '$Info' à partir du cache\"
$Info= $MyInformations
}

# .... Traitement ...

#Si l'objet n'existe pas dans le cache, on l'ajoute
if (!$isObjectInCache)
{
Write-Verbose \"Ajoute l'Objet '$Identity' dans le cache\"
$script:ObjectCache.Add($Identity,$NewObject)
}
}

#début du script/module
#Crée le cache hébergeant les informations
Reset-MyCache

$VerbosePreference='Continue'
faitQqChose 1
faitQqChose 2
faitQqChose 1
faitQqChose 2
faitQqChose 2
$script:ObjectCache
Reset-MyCache
faitQqChose 2
[/code:1]
A part la fonction Reset-MyCache, l'objet cache doit être inaccessible à l'utilisateur du script ou du module.
Limite : Pas de gestion de l'occupation mémoire, pas de durée de vie de l'instance dans le cache.
Contrainte : Contrôler le nombre d'objet à gérer dans le cache ou procéder par lot, par Site, par OU ...

Le framework 4.0 propose la classe System.Runtime.Caching.MemoryCache qui implémente un mécansime de cache plus élaboré.
Un usage simple sous Powershell 3.0( testé sous Seven x64 Fr) :
[code:1]
#requires -Version 3.0

#NOTE :
# Windows server 2008 Core doesn’t support the ObjectCache and some Itanium implementations
# also do not support ObjectCache.
# Windows Server 2008 R2 with SP1 and later versions (including Windows 8.1) support ObjectCache.

Add-Type -AssemblyName System.Runtime.Caching
$Cache= [System.Runtime.Caching.MemoryCache]::«»Default
$Str = $Cache[\"Test\"]
if ($str -eq $null)
{
Write-Warning \"Add\"
$CacheItemPolicy= new-object System.Runtime.Caching.CacheItemPolicy
#L'entrée expire une fois la date et l'heure passée, qu'elle ait été ou non consultée
#pendant cette période (via Get() par exemple).
$CacheItemPolicy.AbsoluteExpiration = [System.DateTimeOffset]::Now.AddSeconds(5.0)
$Str = \"Cache avec durée de vie\"
$Cache.Set(\"Test\", $Str, $CacheItemPolicy);
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
}
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#\"Cache avec durée de vie\"
Sleep -Seconds 6
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Aucun affichage
#Une fois la date et l'heure expirée, l'entrée n'existe plus dans le cache.

#Une fois que la date déclarée dans $CacheItemPolicy est expirée, on ne peut plus ajouter d'entrée au cache,
#car sa suppression est automatique.
# A moins de redéclarer cette valeur
$Cache.Set(\"Test2\", $Str, $CacheItemPolicy);
$Cache.Get('Test2',[System.Management.Automation.Language.NullString]::Value)
#Aucun affichage
#La clé 'Test2' n'a pas été ajoutée

#Nouveau délai, on redéclare la valeur de l'expiration
$CacheItemPolicy= new-object System.Runtime.Caching.CacheItemPolicy
$CacheItemPolicy.AbsoluteExpiration = [System.DateTimeOffset]::Now.AddSeconds(3.0)

#Même clé, mais la date et l'heure d'expiration est actualisée
$Cache.Set(\"Test\", $Str, $CacheItemPolicy);
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Cache avec durée de vie
Sleep -Seconds 4
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Aucun affichage
#Une fois la date et l'heure expirée, l'entrée n'existe plus dans le cache.
$Cache.Dispose()
$Cache=$null
[/code:1]
L'exemple précédent se base sur le délai AbsoluteExpiration, le suivant sur celui nommé SlidingExpiration.
Dans ce cas chaque accès à une clé dans le délai configuré reconduit le délai d'autant:
[code:1]
Add-Type -AssemblyName System.Runtime.Caching
$Cache= [System.Runtime.Caching.MemoryCache]::«»Default
$Str = $Cache[\"Test\"]
if ($str -eq $null)
{
Write-Warning \"Add\"
$CacheItemPolicy= new-object System.Runtime.Caching.CacheItemPolicy
#L'entrée expire une fois le délai passé, sauf si elle est consultée pendant cette période (via Get() par exemple).
#Dans ce cas sa duréé de vie est reconduite d'autant.
$CacheItemPolicy.SlidingExpiration = [timespan]::FromSeconds(10)
$Str = \"Cache avec durée de vie\"
$Cache.Set(\"Test\", $Str, $CacheItemPolicy);
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Cache avec durée de vie
}
Sleep -Seconds 11
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Aucun affichage
#Délai expiré, car aucun accès pendant la période configurée

#Nouveau délai
$CacheItemPolicy.SlidingExpiration = [timespan]::FromSeconds(10)
Sleep -Seconds 4
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Cache avec durée de vie
Sleep -Seconds 6
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Cache avec durée de vie
Sleep -Seconds 8
#Après 18 secondes, l'objet n'est pas supprimé
#Le délai expirera dans 10 secondes
$Cache.Dispose()
$Cache=$null
[/code:1]
L'usage combiné de 'AbsoluteExpiration' et 'SlidingExpiration' n'est pas possible :
[code:1]
$CacheItemPolicy.AbsoluteExpiration = [System.DateTimeOffset]::Now.AddSeconds(5.0)
$CacheItemPolicy.SlidingExpiration = [timespan]::FromSeconds(10)
$Cache.Set(\"Test\", $Str, $CacheItemPolicy);
#Provoque l'erreur :
Exception lors de l'appel de «Set» avec «3» argument(s): «AbsoluteExpiration doit avoir la valeur
DateTimeOffset.MaxValue ou SlidingExpiration doit avoir la valeur TimeSpan.Zero.
Nom du paramètre : policy »
[/code:1]
Il est possible de configurer des callback lors d'une suppression OU d'une mise à jour explicite d'une entrée :
[code:1]
Add-Type -AssemblyName System.Runtime.Caching
function new-CIP {
$CacheItemPolicy= new-object System.Runtime.Caching.CacheItemPolicy
#une fois le temps atteint on ne peut plus ajouter d'entrée au cache
# à moins de redéclarer cette valeur
$CacheItemPolicy.RemovedCallback=[System.Runtime.Caching.CacheEntryRemovedCallback]{
param($Arguments) #CacheEntryRemovedArguments
$S=\"Delete into {0} {1} Reason :{2}\" -F $Arguments.Source,$Arguments.CacheItem.Key,$Arguments.RemovedReason
Write-Warning $S
$S|Set-Content C:\Temp\TestMemoryCache.txt
}#RemovedCallback
$CacheItemPolicy.SlidingExpiration = [timespan]::FromSeconds(10)
$CacheItemPolicy
}

$Cache= [System.Runtime.Caching.MemoryCache]::«»Default
$Cache[\"Test\"]
Write-Warning \"Add\"
$CacheItemPolicy=new-CIP
$Str = \"Cache avec durée de vie\"
$Cache.Set(\"Test\", $Str, $CacheItemPolicy);
#Add ne supporte pas $CacheItemPolicy.UpdateCallback
#$Cache.Add(\"Test\", $Str, $CacheItemPolicy);
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Cache avec durée de vie

#Déclenche RemovedCallback
#L'appel à Set() peut également déclencher RemovedCallback,
#car Set() supprime et remplace l'entrée
$Cache.Remove('Test',[System.Management.Automation.Language.NullString]::Value)
#AVERTISSEMENT : Delete into System.Runtime.Caching.MemoryCache Test Reason :Removed
#Cache avec durée de vie

Sleep -Seconds 11
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Aucun affichage
$Cache.Dispose()
$Cache=$null
[/code:1]
Notez que certaines propriétés ou méthodes ne peuvent être combinées, consultez la doc MSDN pour le détail.

Un exmple de configuration du délai du cache à partir d'une date et plus un intervalle:
[code:1]
Add-Type -AssemblyName System.Runtime.Caching
$Cache= [System.Runtime.Caching.MemoryCache]::«»Default
$Str = $Cache[\"Test\"]
Write-Warning \"Add\"
$DTO=New-object System.DateTimeOffset( [DateTime]::Now)
$DTO=$DTO.AddSeconds(4)
# $Cache.SlidingExpirations
$Str = \"Cache avec durée de vie\"
$Cache.Add(\"Test\", $Str, $DTO,[System.Management.Automation.Language.NullString]::Value) > $null
$cache
Sleep -Seconds 5
$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)
#Si $DTO est antérieur, l'ajout ne se fait pas
$Cache.Add(\"Test\", $Str, $DTO,[System.Management.Automation.Language.NullString]::Value) > $null
[/code:1]
Visualisation des détails :
[code:1]
#requires -Version 3.0
add-type -AssemblyName System.Runtime.Caching

#Utilise le cache par défault
#http://msdn.microsoft.com/fr-fr/library/system.runtime.caching.memorycache%28v=vs.100%29.aspx
$Cache= [System.Runtime.Caching.MemoryCache]::«»Default

#Obtient une description des fonctionnalités que fournit le cache.
$Cache.DefaultCacheCapabilities

#Obtient la quantité de mémoire, en octets, de l'ordinateur pouvant être utilisée par le cache.
$Cache.CacheMemoryLimit
#$Cache.CacheMemoryLimit / 1mb

#Obtient le pourcentage de mémoire physique que le cache peut utiliser.
$Cache.PhysicalMemoryLimit

#Obtient la durée maximale après laquelle le cache met à jour ses statistiques de mémoire.
$Cache.PollingInterval

# Test la présence d'un objet dont la clé est \"Test'
$Str = $Cache[\"Test\"]
if ($str -eq $null)
{
#l'objet n'existe pas ou plus dans le cache
#On l'insére

#Informations associées a la mise en cache de l'objet
#On utilse les valeurs par défaut
$CacheItemPolicy= new-object System.Runtime.Caching.CacheItemPolicy
$Str = \"Cache text\"

$Cache.Set(\"Test\", $Str, $CacheItemPolicy);
}

#La classe 'MemoryCache' n'implémente pas la notion de région,
#on doit donc préciser la valeur $null dans le second paramètre
$Cache.Get('test',$null)
# Exception lors de l'appel de « Get » avec « 2 » argument(s) : « Le paramètre regionName doit être null. »
#Une erreur est declenchée, car ici $null est transformé par PS en [String]::Empty


#A partir de la v3, PS propose la valeur suivante, qui
#force le parseur à émettre la valeur null (pas de transformation implicite)
$Cache.Get('test',[System.Management.Automation.Language.NullString]::Value)

#!!!!!!!! Pas d'entrée trouvée car le nom est Case Sensitive

$Cache.Get('Test',[System.Management.Automation.Language.NullString]::Value)

#Il n'existe pas de méthode Clear,
# de recréer l'objet cache est préférable.
$Cache.Dispose()

#$Cache.Dispose() ne 'dispose' pas les objets insérés dans sa liste interne.
#Seules les références sont supprimées de la liste
$Cache=$null
[/code:1]
A suivre...

michaelscodingspot.com/cache-implementations-in-csharp-net/
Message édité par: Laurent Dardenne, à: 30/09/14 23:53<br><br>Message édité par: Laurent Dardenne, à: 16/05/19 17:38

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 6 ans 1 mois #18188 par Laurent Dardenne
Pour configurer les options du cache on peut associer un fichier de configuration à Powershell.exe ou créer une nouvelle instance de MemoryCache :
[code:1]
#requires -Version 3.0
add-type -AssemblyName System.Runtime.Caching

#http://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection%28v=vs.100%29.aspx
$NVC=new-object system.collections.specialized.namevaluecollection
$NVC.Add('CacheMemoryLimitMegabytes',\&quot;50\&quot;«») #50 méga
$NVC.Add('PhysicalMemoryLimitPercentage', \&quot;30\&quot;«») #30 %
$myCache = new-Object System.Runtime.Caching.MemoryCache(\&quot;PowershellCache\&quot;,$NVC)
$myCache.psbase
#Valeur par défaut
$myCache.PollingInterval
$myCache.Dispose()
[/code:1]
MS recommande l'usage d'une seule instance de cache.

Un autre aspect lié au cache est le changement d'état des objets dans le cache.
Le framework propose deux classes permettant d'invalider un objet dans le cache si celui change, à savoir SqlChangeMonitor et FileChangeMonitor .
Nous utiliserons la dernière pour une courte démo, sachez que celle-ci est similaire à la classe FileSystemWatcher .
Bien qu'ici l'usage explicite d'un abonnement à un event est inutile.

Premier exemple :
[code:1]
#requires -Version 3.0
add-type -AssemblyName System.Runtime.Caching
$cachedFilePath='C:\Temp\TestCache.txt'
@'
test
'@ &gt; $cachedFilePath

function Get-ContentCacheFile{
$fileContents = $cache[\&quot;filecontents\&quot;]

#Si on type $fileContents en [string] son contenu ne sera jamais $null
if ($fileContents -eq $null)
{
Write-Warning 'filecontents absent du cache'
#Charge le contenu du fichier
$fileContents = [System.IO.File]::ReadAllText($cachedFilePath) + \&quot;`r`n\&quot; +[System.DateTime]::Now

$cache.Set(\&quot;filecontents\&quot;, $fileContents, $policy)
}
else
{ Write-Warning 'filecontents présent dans le cache' }
$cache[\&quot;filecontents\&quot;]
}#Get-ContentCacheFile


$Cache= [System.Runtime.Caching.MemoryCache]::«»Default
$policy = new-object System.Runtime.Caching.CacheItemPolicy
$policy.AbsoluteExpiration = [System.DateTimeOffset]::Now.AddMinutes(5)

#Construit une collection de String générique.
$filePaths = new-object System.Collections.Generic.List[string]

$filePaths.Add($cachedFilePath)

#Un monitor est un objet qui surveille les modifications apportées aux fichiers.
#La policy gère ces objets de surveillance
#Ajoute une surveillance sur le ou les fichiers indiqués
$policy.ChangeMonitors.Add((new-object System.Runtime.Caching.HostFileChangeMonitor(,$filePaths)))

Get-ContentCacheFile
$cache
#Invalide la clé 'filecontents' du cache
\&quot;Ajout \&quot; &gt;&gt; $cachedFilePath
$cache
[/code:1]
Une fois le fichier modifié, la clé est supprimé ce qui oblige une relecture du contenu du fichier :
[code:1]
Get-ContentCacheFile
# Exception lors de l'appel de «Set» avec «3» argument(s): «La méthode a déjà été appelée, et ne peut être appelée qu'une seule fois.»
# Au caractère Ligne:11 : 5
# + $cache.Set(\&quot;filecontents\&quot;, $fileContents, $policy)
$StackTrace
[/code:1]
Le code précédent est incorrecte car l'objet moniteur ne peut déclencher qu'une seule notification.
On doit donc le recréer après chaque notification :
[code:1]
add-type -AssemblyName System.Runtime.Caching
$cachedFilePath='C:\Temp\TestCache.txt'
@'
test
'@ &gt; $cachedFilePath

function Get-ContentCacheFile{
$fileContents = $cache[\&quot;filecontents\&quot;]

#Si on type $fileContents en [string] son contenu ne sera jamais $null
if ($fileContents -eq $null)
{
Write-Warning 'filecontents absent du cache'
$policy = new-object System.Runtime.Caching.CacheItemPolicy
$policy.AbsoluteExpiration = [System.DateTimeOffset]::Now.AddSeconds(5)

#Construit une collection de String générique.
$filePaths = new-object System.Collections.Generic.List[string]

$filePaths.Add($cachedFilePath)

#Un monitor est un objet qui surveille les modifications apportées aux fichiers.
#La policy gère ces objets de surveillance
#Ajoute une surveillance sur le ou les fichiers indiqué
$policy.ChangeMonitors.Add((new-object System.Runtime.Caching.HostFileChangeMonitor(,$filePaths)))

$fileContents = [System.IO.File]::ReadAllText($cachedFilePath) + \&quot;`r`n\&quot; +[System.DateTime]::Now

$cache.Set(\&quot;filecontents\&quot;, $fileContents, $policy)
}
else
{ Write-Warning 'filecontents présent dans le cache' }
$cache[\&quot;filecontents\&quot;]
}#Get-ContentCacheFile

$Cache= [System.Runtime.Caching.MemoryCache]::«»Default

Get-ContentCacheFile
Get-ContentCacheFile
sleep -s 1
#Invalide la clé 'filecontents' du cache
\&quot;Ajout 1\&quot; &gt;&gt; $cachedFilePath
Get-ContentCacheFile
sleep -s 1
\&quot;Ajout 2\&quot; &gt;&gt; $cachedFilePath
Get-ContentCacheFile
sleep -s 1
$cache[\&quot;filecontents\&quot;]
sleep -s 5
#délai dépassé, la clé est supprimée. On la recrée
Get-ContentCacheFile

del $cachedFilePath
$cache[\&quot;filecontents\&quot;]
Get-ContentCacheFile
#Erreur, car on ne peut manipuler un fichier inexistant
[/code:1]
La suppression du fichier invalide également la clé du cache qui lui est associé.
Il est possible de créer d'autres classes de monitor, un exemple sur Azure .

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 6 ans 1 mois #18234 par Arnaud Petitjean
Merci Laurent de nous avoir fait partager ton expérience sur les caches. C'est très intéressant.

Arnaud

MVP PowerShell et créateur de ce magnifique forum :-)
Auteur de 6 livres PowerShell aux éditions ENI
Fondateur de la société Start-Scripting
Besoin d'une formation PowerShell ou d'un conseil ?

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.202 secondes
Propulsé par Kunena