Merge pull request #828 from Icinga:feature/adds_cache_update_for_performance_counters

Feature: Adds caching update for added/removed performance counter instances

Adds feature to update the cache for performance counter instances to keep track of system changes.

This will ensure that while we are running as a daemon with Icinga for Windows, newly added or removed information for performance counters will be updated whenever required.
This commit is contained in:
Lord Hepipud 2025-11-20 15:52:31 +01:00 committed by GitHub
commit ad59bb9a3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 109 additions and 25 deletions

View file

@ -20,6 +20,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
### Enhancements
* [#838](https://github.com/Icinga/icinga-powershell-framework/pull/838) Enhances Icinga for Windows to never load and user PowerShell profiles
* [#11](https://github.com/Icinga/icinga-powershell-framework/pull/11) Adds feature to update the cache for performance counter instances to keep track of system changes
## 1.13.4 (tbd)

View file

@ -26,6 +26,9 @@
if multiple counters are fetched during one call with this function if the sleep
is done afterwards manually. A sleep is set to 500ms to ensure counter data is
valid and contains an offset from previous/current values
.PARAMETER NoCache
Set this if no caching of the counter is intended. This will prevent from adding
single counters to the internal cache during update phase
.INPUTS
System.String
.LINK
@ -36,7 +39,8 @@ function New-IcingaPerformanceCounter()
{
param(
[string]$Counter = '',
[boolean]$SkipWait = $FALSE
[boolean]$SkipWait = $FALSE,
[switch]$NoCache = $FALSE
);
# Simply use the counter name, like
@ -45,7 +49,7 @@ function New-IcingaPerformanceCounter()
return (New-IcingaPerformanceCounterNullObject -FullName $Counter -ErrorMessage 'Failed to initialise counter, as no counter was specified.');
}
[array]$CounterArray = $Counter.Split('\');
[array]$CounterArray = $Counter.Split('\');
[string]$UseCounterCategory = '';
[string]$UseCounterName = '';
[string]$UseCounterInstance = '';
@ -72,6 +76,13 @@ function New-IcingaPerformanceCounter()
# At last get the actual counter containing our values
$UseCounterName = $CounterArray[2];
if ($NoCache -eq $FALSE) {
# If we are not skipping the cache, we will update the cache
# with the current counter path. This will ensure that we
# have a valid cache for the counter and can return it later
Update-IcingaPerformanceCounterCache -Counter $Counter;
}
# Now as we know how the counter path is constructed and has been split into
# the different values, we need to know how to handle the instances of the counter
@ -80,11 +91,13 @@ function New-IcingaPerformanceCounter()
# which contains the parent name including counters for all instances that
# have been found
if ($UseCounterInstance -eq '*') {
# In case we already loaded the counters once, return the finished array
$CachedCounter = Get-IcingaPerformanceCounterCacheItem -Counter $Counter;
if ($NoCache -eq $FALSE) {
# In case we already loaded the counters once, return the finished array
$CachedCounter = Get-IcingaPerformanceCounterCacheItem -Counter $Counter;
if ($null -ne $CachedCounter) {
return (New-IcingaPerformanceCounterResult -FullName $Counter -PerformanceCounters $CachedCounter);
if ($null -ne $CachedCounter) {
return (New-IcingaPerformanceCounterResult -FullName $Counter -PerformanceCounters $CachedCounter);
}
}
# If we need to build the array, load all instances from the counters and
@ -116,7 +129,9 @@ function New-IcingaPerformanceCounter()
# Add the parent counter including the array of Performance Counters to our
# caching mechanism and return the New-IcingaPerformanceCounterResult object for usage
# within the monitoring modules
Add-IcingaPerformanceCounterCache -Counter $Counter -Instances $AllCountersInstances;
if ($NoCache -eq $FALSE) {
Add-IcingaPerformanceCounterCache -Counter $Counter -Instances $AllCountersInstances;
}
return (New-IcingaPerformanceCounterResult -FullName $Counter -PerformanceCounters $AllCountersInstances);
} else {
# This part will handle the counters without any instances as well as
@ -124,16 +139,22 @@ function New-IcingaPerformanceCounter()
# In case we already have the counter within our cache, return the
# cached informations
$CachedCounter = Get-IcingaPerformanceCounterCacheItem -Counter $Counter;
if ($NoCache -eq $FALSE) {
$CachedCounter = Get-IcingaPerformanceCounterCacheItem -Counter $Counter;
if ($null -ne $CachedCounter) {
return $CachedCounter;
if ($null -ne $CachedCounter) {
return $CachedCounter;
}
}
# If the cache is not present yet, create the Performance Counter object,
# and add it to our cache
$NewCounter = New-IcingaPerformanceCounterObject -FullName $Counter -Category $UseCounterCategory -Counter $UseCounterName -Instance $UseCounterInstance -SkipWait $SkipWait;
Add-IcingaPerformanceCounterCache -Counter $Counter -Instances $NewCounter;
if ($NoCache -eq $FALSE) {
Add-IcingaPerformanceCounterCache -Counter $Counter -Instances $NewCounter;
} else {
return $NewCounter;
}
}
# This function will always return non-instance counters or

View file

@ -40,13 +40,6 @@ function New-IcingaPerformanceCounterArray()
# NumOfCounters * 500 milliseconds for the first runs. This will speed
# up the general loading of counters and will not require some fancy
# pre-caching / configuration handler
$CachedCounter = Get-IcingaPerformanceCounterCacheItem -Counter $Counter;
# Remove this for now to ensure our CPU metrics will not be cached
# and represent correct values, not exceeding 200% and beyond
#if ($null -ne $CachedCounter) {
# $RequireSleep = $FALSE;
#}
$obj = New-IcingaPerformanceCounter -Counter $counter -SkipWait $TRUE;
if ($CounterResult.ContainsKey($obj.Name()) -eq $FALSE) {
@ -54,12 +47,6 @@ function New-IcingaPerformanceCounterArray()
}
}
# TODO: Add a cache for our Performance Counters to only fetch them once
# for each session to speed up the loading. This cold be something like
# this:
#
# $Global:Icinga.Private.PerformanceCounter.Cache += $CounterResult;
# Above we initialise ever single counter and we only require a sleep once
# in case a new, yet unknown counter was added
if ($RequireSleep) {

View file

@ -35,7 +35,7 @@ function Show-IcingaPerformanceCounterInstances()
return;
}
$PerfCounter = New-IcingaPerformanceCounter -Counter $Counter -SkipWait $TRUE;
$PerfCounter = New-IcingaPerformanceCounter -Counter $Counter -SkipWait $TRUE -NoCache;
foreach ($entry in $PerfCounter.Counters) {
$Instances.Add(

View file

@ -0,0 +1,75 @@
<#
.SYNOPSIS
Updates the cached instances of a specified performance counter in the Icinga PowerShell Framework.
.DESCRIPTION
The Update-IcingaPerformanceCounterCache function synchronizes the cached instances of a given performance counter.
It removes instances that no longer exist and adds new instances that are not yet cached.
This ensures that the cache accurately reflects the current state of the performance counter instances.
.PARAMETER Counter
The name of the performance counter whose cache should be updated.
.EXAMPLE
Update-IcingaPerformanceCounterCache -Counter "\Processor(_Total)\% Processor Time"
Updates the cache for the specified performance counter.
.NOTES
This function is intended for internal use within the Icinga PowerShell Framework.
It requires that the global cache variable and related functions are available.
#>
function Update-IcingaPerformanceCounterCache()
{
param (
$Counter
);
if ([string]::IsNullOrEmpty($Counter)) {
return;
}
if ($Global:Icinga.Private.PerformanceCounter.Cache.ContainsKey($Counter) -eq $FALSE) {
# If there is no cache entry for the provided counter, we don't need to do anything yet
return;
}
# First we need to prepare some data by fetching the current instances of the counter
# and the cached instances. We will then compare them and update the cache accordingly
[array]$CounterInstances = Show-IcingaPerformanceCounterInstances -Counter $Counter;
[array]$CachedInstances = $Global:Icinga.Private.PerformanceCounter.Cache[$Counter];
[array]$UpdatedInstances = @();
[array]$CachedInstanceNames = $CachedInstances.FullName;
# We will now iterate over the cached instances and check if they are still present in the current
# counter instances. If they are not, we will remove them from the cache.
# If they are present, we will keep them in the updated instances array.
for ($index = 0; $index -lt $CachedInstances.Count; $index++) {
$cachedInstance = $CachedInstances[$index];
$instanceName = $cachedInstance.FullName;
# If the instance is not in the current list, we remove it
if ($CounterInstances.Value -contains $instanceName) {
$UpdatedInstances += $cachedInstance;
}
}
# Now we will iterate over the current counter instances and check if they are already cached.
# If they are not, we will add them to the updated instances array.
# This ensures that we only add new instances that are not already cached.
for ($index = 0; $index -lt $CounterInstances.Count; $index++) {
$instanceName = $CounterInstances[$index].Value;
if ($CachedInstanceNames -notcontains $instanceName) {
# If the instance is not cached, we create a new performance counter object
# and add it to the updated instances array
$UpdatedInstances += (New-IcingaPerformanceCounter -Counter $instanceName -SkipWait $TRUE -NoCache);
}
}
# Finally, we update the cache with the new instances
# This will ensure that the cache is up-to-date with the current state of the performance
# counter instances
$Global:Icinga.Private.PerformanceCounter.Cache[$Counter] = $UpdatedInstances;
}