Private/Throttle/Set-ThrottleDefaults.ps1

<#
    Copyright (c) 2025-2026 Robert Faddes
    SPDX-License-Identifier: MPL-2.0
#>

<#
.SYNOPSIS
    Initialises the RMMThrottle script-scoped variable with safe static defaults.
.DESCRIPTION
    Populates $Script:RMMThrottle with default values for the multi-bucket throttle model.
    Called once at module load, before dot-sourcing private and public functions.
 
    The hash table serves two purposes:
 
    - Behaviour settings (DelayMultiplier, CalibrationBaseSeconds, etc.) provide working
      defaults that are overridden by Import-ThrottleProfile when a saved config is loaded.
 
    - Runtime state fields (ReadLimit, WriteLimit, timestamps, etc.) provide safe
      pre-connect fallback values. These are replaced with live-discovered values by
      Initialize-ThrottleState when Connect-DattoRMM is called.
 
    Read and write calibration timestamps are set to [datetime]::MinValue as pre-connect
    safe defaults only. Calibration is driven by Connect-DattoRMM via Initialize-ThrottleState
    and Update-Throttle, not by these sentinel values.
 
    The Datto RMM API tracks reads and writes as independent quotas:
    - accountCount / accountRateLimit → read (GET) operations only
    - accountWriteCount / accountWriteRateLimit → write (PUT/POST/DELETE) operations only
    Read requests are evaluated against the read bucket; write requests are evaluated against
    write buckets (global write + per-operation). They do not overlap.
#>

function Set-ThrottleDefaults {
    [CmdletBinding()]
    param ()

    $Script:RMMThrottle = [ordered]@{
        Profile = 'DefaultProfile'                                                      # Active throttle profile name
        DelayMultiplier = 500                                                           # Delay multiplier for all bucket throttling (read and write)
        ThrottleCutOffOverhead = 0.05                                                   # Safety margin below accountCutOffRatio for pause trigger
        ThrottleUtilisationThreshold = 0.3                                              # Utilisation ratio at which throttling activates
        CalibrationBaseSeconds = 8                                                      # Ceiling interval at high confidence and zero drift
        CalibrationMinSeconds = 0.5                                                     # Absolute floor to prevent excessive API calibration calls
        CalibrationConfidenceCount = 50                                                 # Local samples needed before interval reaches full base
        CalibrationMaxSeconds = 20                                                      # Ceiling for stability-extended calibration interval
        CalibrationStabilityThreshold = 4                                               # Consecutive stable calibrations before interval begins extending
        DriftThresholdPercent = 0.02                                                    # Drift gap at which accelerated calibration begins (2%)
        DriftScalingFactor = 2                                                          # How aggressively interval shrinks as drift exceeds threshold
        UnknownOperationSafetyFactor = 0.3                                              # Fractional delay for unmapped write operations
        WindowSizeSeconds = 60                                                          # Rolling window size (discovered from API)
        ReadLimit = 600                                                                 # Read (GET) rate limit (discovered from API as accountRateLimit)
        AccountCutOffRatio = 0.9                                                        # Account cut-off ratio (discovered from API)
        WriteLimit = 600                                                                # Write rate limit (discovered from API as accountWriteRateLimit)
        ReadLocalTimestamps = [System.Collections.Generic.List[datetime]]::new()        # Local timestamps for read (GET) requests
        WriteLocalTimestamps = [System.Collections.Generic.List[datetime]]::new()       # Local timestamps for write (PUT/POST/DELETE) requests
        OperationBuckets = @{}                                                          # Per-operation write buckets (discovered from API)
        ReadLastCalibrationUtc = [datetime]::MinValue                                   # Pre-connect safe default; overwritten by Initialize-ThrottleState on connect
        WriteLastCalibrationUtc = [datetime]::MinValue                                  # Pre-connect safe default; overwritten by Initialize-ThrottleState on connect
        ReadSamplesAtLastCalibration = 0                                                # Local read sample count at last calibration (for request gate)
        WriteSamplesAtLastCalibration = 0                                               # Local write sample count at last calibration (for request gate)
        ReadStableCalibrationCount = 0                                                  # Consecutive stable read calibrations since last instability reset
        WriteStableCalibrationCount = 0                                                 # Consecutive stable write calibrations since last instability reset
        ReadUtilisation = 0.0                                                           # Computed read utilisation (accountCount / accountRateLimit)
        WriteUtilisation = 0.0                                                          # Computed write utilisation (accountWriteCount / accountWriteRateLimit)
        ReadDelayMS = 0                                                                 # Current computed read delay in milliseconds
        WriteDelayMS = 0                                                                # Current computed write delay in milliseconds
        Pause = $false                                                                  # Hard pause flag
        Throttle = $false                                                               # Soft throttle flag
    }
}

# SIG # Begin signature block
# MIIF+wYJKoZIhvcNAQcCoIIF7DCCBegCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDsGGoKC9VpzrtA
# m83EUOsgr4fNv6wmSiJiV5obo+fMR6CCA04wggNKMIICMqADAgECAhB464iXHfI6
# gksEkDDTyrNsMA0GCSqGSIb3DQEBCwUAMD0xFjAUBgNVBAoMDVJvYmVydCBGYWRk
# ZXMxIzAhBgNVBAMMGkRhdHRvUk1NLkNvcmUgQ29kZSBTaWduaW5nMB4XDTI2MDMz
# MTAwMTMzMFoXDTI4MDMzMTAwMjMzMFowPTEWMBQGA1UECgwNUm9iZXJ0IEZhZGRl
# czEjMCEGA1UEAwwaRGF0dG9STU0uQ29yZSBDb2RlIFNpZ25pbmcwggEiMA0GCSqG
# SIb3DQEBAQUAA4IBDwAwggEKAoIBAQChn1EpMYQgl1RgWzQj2+wp2mvdfb3UsaBS
# nxEVGoQ0gj96tJ2MHAF7zsITdUjwaflKS1vE6wAlOg5EI1V79tJCMxzM0bFpOdR1
# L5F2HE/ovIAKNkHxFUF5qWU8vVeAsOViFQ4yhHpzLen0WLF6vhmc9eH23dLQy5fy
# tELZQEc2WbQFa4HMAitP/P9kHAu6CUx5s4woLIOyyR06jkr3l9vk0sxcbCxx7+dF
# RrsSLyPYPH+bUAB8+a0hs+6qCeteBuUfLvGzpMhpzKAsY82WZ3Rd9X38i32dYj+y
# dYx+nx+UEMDLjDJrZgnVa8as4RojqVLcEns5yb/XTjLxDc58VatdAgMBAAGjRjBE
# MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU
# H+B0vf97dYXqdUX1YMcWhFsY6fcwDQYJKoZIhvcNAQELBQADggEBAJmD4EEGNmcD
# 1JtFoRGxuLJaTHxDwBsjqcRQRE1VPZNGaiwIm8oSQdHVjQg0oIyK7SEb02cs6n6Y
# NZbwf7B7WZJ4aKYbcoLug1k1x9SoqwBmfElECeJTKXf6dkRRNmrAodpGCixR4wMH
# KXqwqP5F+5j7bdnQPiIVXuMesxc4tktz362ysph1bqKjDQSCBpwi0glEIH7bv5Ms
# Ey9Gl3fe+vYC5W06d2LYVebEfm9+7766hsOgpdDVgdtnN+e6uwIJjG/6PTG6TMDP
# y+pr5K6LyUVYJYcWWUTZRBqqwBHiLGekPbxrjEVfxUY32Pq4QfLzUH5hhUCAk4HN
# XpF9pOzFLMUxggIDMIIB/wIBATBRMD0xFjAUBgNVBAoMDVJvYmVydCBGYWRkZXMx
# IzAhBgNVBAMMGkRhdHRvUk1NLkNvcmUgQ29kZSBTaWduaW5nAhB464iXHfI6gksE
# kDDTyrNsMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKEC
# gAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwG
# CisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIK6es/uaCXXJFk3Yb/RFetPfJqkd
# iaVjYhMM2x578QInMA0GCSqGSIb3DQEBAQUABIIBAA7y7UQ/stsVlJpFk+Q0OtN1
# Ysw2jjuZWnWo3zdzy9GUXkLlpgDs0br9u/YepamRRkk9hU6HBr7JX9KSsHXD4M09
# hHQ16CX4in+PM+4T1hV64p6pVzLqLtjrFRHI163Be/eAj1Eo7tSy+TDzIZgahqMa
# E+FLmEv0LDPfD0pq5iYQtkmhv+QtKw3N0QRA3prQWGpzwk+ypYPUM8zl/qh9JYfW
# YJwzsk6hvltt/c9Bpy+FvlbbiT72VLMomlMTjLO/FjQvTO7X3CU3rhgLCosliZJ/
# v2e2bO0Zq18Neva/fScnPM6X9qxFyntGNBHirojUTnlMdVcBlj59EDvHTbP2hr4=
# SIG # End signature block