Skip to content

Instantly share code, notes, and snippets.

@sean-m
Created March 23, 2021 23:39
Show Gist options
  • Save sean-m/bcf150a103122eba41fe602ab4566c02 to your computer and use it in GitHub Desktop.
Save sean-m/bcf150a103122eba41fe602ab4566c02 to your computer and use it in GitHub Desktop.
Simple static cache for PowerShell. Intend to add an object limit and generational eviction mechanism. For right now it's basically a cleaner way to use a hashtable for caching inside a script.
class CacheResolverException : Exception {
CacheResolverException ([string]$Message, [Exception]$InnerException) : base ($Message, $InnerException) { }
}
class HotCache {
hidden static [Hashtable]$Records = @{}
hidden [UInt64] $Hit = 0
hidden [UInt64] $Miss = 0
hidden [int] $Limit = 0
HotCache () {
$this.Init()
}
HotCache ($Limit) {
$this.Init()
# If this limit is exceeded, the least used record will be evicted from the cache.
if ($Limit -gt $this.Limit) {
$this.Limit = $Limit
}
}
hidden [void] Init() {
# Allows accessing Record.Count without exposing the colleciton publically
$this | Add-Member -MemberType ScriptProperty -Name 'Count' -Value { return [HotCache]::Records.Count }
}
[void] Add ([string]$Key, $Value) {
if ([String]::IsNullOrEmpty($Key)) {
throw '$Key is null. Key must be a non-empty string.'
}
$_key = $Key.ToLower()
if ([HotCache]::Records.ContainsKey($_key)) {
[HotCache]::Records[$_key] = $Value
}
else {
[HotCache]::Records.Add($_key, $Value)
}
}
[bool] ContainsKey ([String]$Key) {
if ([String]::IsNullOrEmpty($Key)) {
throw '$Key is null. Key must be a non-empty string.'
}
$_key = $Key.ToLower()
return [HotCache]::Records.ContainsKey($_key)
}
[object] Get ($Key, [Func[string,object]]$Resolve=$null) {
if ([String]::IsNullOrEmpty($Key)) {
throw '$Key is null. Key must be a non-empty string.'
}
$_key = $Key.ToLower()
if ([HotCache]::Records.ContainsKey($_key)) {
# Return resolved key
$this.Hit++
return [HotCache]::Records[$_key]
}
else {
# If a resolution Func was passed, attempt to resolve the object
$this.Miss++
if ($Resolve) {
try {
$result = $Resolve.Invoke($_key)
if ($result) {
[HotCache]::Records.Add($_key, $result)
return $result
}
}
catch {
throw [CacheResolverException]::new('Exception encountered while resolving key: {0}' -f $Key, $_.Exception)
}
}
throw "Cannot resolve value for key: $Key"
}
}
[object] GetStats () {
return [PSCustomObject]@{
Count=[HotCache]::Records.Count
Hit=$this.Hit;
Miss=$this.Miss;
}
}
[void] Clear() {
[HotCache]::Records.Clear()
$this.Hit = 0
$this.Miss = 0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment