Skip to content

Commit

Permalink
Merge pull request #417 from Badgerati/Issue-415
Browse files Browse the repository at this point in the history
Adds support functions to invoke a schedule/timer adhoc
  • Loading branch information
Badgerati committed Nov 27, 2019
2 parents e97d9c5 + 8343dfe commit 787af95
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 83 deletions.
5 changes: 5 additions & 0 deletions examples/schedules.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ Start-PodeServer {
# logic
} -EndTime ([DateTime]::Now.AddHours(2))

# adhoc invoke a schedule's logic
Add-PodeRoute -Method Get -Path '/api/run' -ScriptBlock {
Invoke-PodeSchedule -Name 'predefined'
}

}
7 changes: 6 additions & 1 deletion examples/timers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Start-PodeServer {
# runs forever, looping every 5secs
Add-PodeTimer -Name 'forever' -Interval 5 -ScriptBlock {
'Hello, world' | Out-PodeHost
}
} -Limit 5

# runs forever, but skips the first 3 "loops" - is paused for 15secs then loops every 5secs
Add-PodeTimer -Name 'pause-first-3' -Interval 5 -ScriptBlock {
Expand Down Expand Up @@ -44,4 +44,9 @@ Start-PodeServer {
}
}

# adhoc invoke a timer's logic
Add-PodeRoute -Method Get -Path '/api/run' -ScriptBlock {
Invoke-PodeTimer -Name 'forever'
}

}
2 changes: 2 additions & 0 deletions src/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,13 @@
'Add-PodeSchedule',
'Remove-PodeSchedule',
'Clear-PodeSchedule',
'Invoke-PodeSchedule',

# timers
'Add-PodeTimer',
'Remove-PodeTimer',
'Clear-PodeTimers',
'Invoke-PodeTimer',

# middleware
'Add-PodeMiddleware',
Expand Down
149 changes: 83 additions & 66 deletions src/Private/Schedules.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,106 +17,123 @@ function Start-PodeScheduleRunspace
}

$script = {
function Invoke-PodeInternalSchedule($Schedule, $Now)
{
$Schedule.OnStart = $false
$remove = $false

# increment total number of triggers for the schedule
if ($Schedule.Countable) {
$Schedule.Count++
$Schedule.Countable = ($Schedule.Count -lt $Schedule.Limit)
}

# check if we have hit the limit, and remove
if (($Schedule.Limit -ne 0) -and ($Schedule.Count -ge $Schedule.Limit)) {
$remove = $true
}

# trigger the schedules logic
try {
$parameters = @{
Event = @{
Lockable = $PodeContext.Lockable
}
}

foreach ($key in $Schedule.Arguments.Keys) {
$parameters[$key] = $Schedule.Arguments[$key]
}

Add-PodeRunspace -Type Schedules -ScriptBlock (($Schedule.Script).GetNewClosure()) -Parameters $parameters -Forget
}
catch {
$_ | Write-PodeErrorLog
}

# reset the cron if it's random
$Schedule.Crons = Reset-PodeRandomCronExpressions -Expressions $Schedule.Crons
return $remove
}

function Remove-PodeInternalSchedules([string[]] $Schedules)
{
# add any schedules to remove that have exceeded their end time
$Schedules += @($PodeContext.Schedules.Values |
Where-Object { (($null -ne $_.EndTime) -and ($_.EndTime -lt $_now)) }).Name

if (($null -eq $Schedules) -or ($Schedules.Length -eq 0)) {
return
}

# remove any schedules
foreach ($name in $Schedules) {
if ($PodeContext.Schedules.ContainsKey($name)) {
$PodeContext.Schedules.Remove($name) | Out-Null
}
}
}

# select the schedules that trigger on-start
$_remove = @()
$_now = [DateTime]::Now

$PodeContext.Schedules.Values |
Where-Object {
$_.OnStart
} | ForEach-Object {
if (Invoke-PodeInternalSchedule -Schedule $_ -Now $_now) {
$_remove += $_.Name
if (Invoke-PodeInternalSchedule -Schedule $_) {
$_.Completed = $true
}
}

# remove any schedules
Remove-PodeInternalSchedules -Schedules $_remove
Remove-PodeInternalSchedules -Now $_now

# first, sleep for a period of time to get to 00 seconds (start of minute)
Start-Sleep -Seconds (60 - [DateTime]::Now.Second)

while ($true)
{
$_remove = @()
$_now = [DateTime]::Now

# select the schedules that need triggering
$PodeContext.Schedules.Values |
Where-Object {
(($null -eq $_.StartTime) -or ($_.StartTime -le $_now)) -and
(($null -eq $_.EndTime) -or ($_.EndTime -ge $_now)) -and
(Test-PodeCronExpressions -Expressions $_.Crons -DateTime $_now)
(Test-PodeCronExpressions -Expressions $_.Crons -DateTime $_now) -and !$_.Completed
} | ForEach-Object {
if (Invoke-PodeInternalSchedule -Schedule $_ -Now $_now) {
$_remove += $_.Name
if (Invoke-PodeInternalSchedule -Schedule $_) {
$_.Completed = $true
}
}

# remove any schedules
Remove-PodeInternalSchedules -Schedules $_remove
Remove-PodeInternalSchedules -Now $_now

# cron expression only goes down to the minute, so sleep for 1min
Start-Sleep -Seconds (60 - [DateTime]::Now.Second)
}
}

Add-PodeRunspace -Type 'Main' -ScriptBlock $script
}

function Remove-PodeInternalSchedules
{
param(
[Parameter(Mandatory=$true)]
[datetime]
$Now
)

# add any schedules to remove that have exceeded their end time
$Schedules = @($PodeContext.Schedules.Values |
Where-Object { (($null -ne $_.EndTime) -and ($_.EndTime -lt $Now)) })

if (($null -eq $Schedules) -or ($Schedules.Length -eq 0)) {
return
}

# set any expired schedules as being completed
$Schedules | ForEach-Object {
$_.Completed = $true
}
}

function Invoke-PodeInternalSchedule
{
param(
[Parameter(Mandatory=$true)]
$Schedule
)

$Schedule.OnStart = $false
$remove = $false

# increment total number of triggers for the schedule
if ($Schedule.Countable) {
$Schedule.Count++
$Schedule.Countable = ($Schedule.Count -lt $Schedule.Limit)
}

# check if we have hit the limit, and remove
if (($Schedule.Limit -ne 0) -and ($Schedule.Count -ge $Schedule.Limit)) {
$remove = $true
}

# trigger the schedules logic
Invoke-PodeInternalScheduleLogic -Schedule $Schedule

# reset the cron if it's random
$Schedule.Crons = Reset-PodeRandomCronExpressions -Expressions $Schedule.Crons
return $remove
}

function Invoke-PodeInternalScheduleLogic
{
param(
[Parameter(Mandatory=$true)]
$Schedule
)

try {
$parameters = @{
Event = @{
Lockable = $PodeContext.Lockable
}
}

foreach ($key in $Schedule.Arguments.Keys) {
$parameters[$key] = $Schedule.Arguments[$key]
}

Add-PodeRunspace -Type Schedules -ScriptBlock (($Schedule.Script).GetNewClosure()) -Parameters $parameters -Forget
}
catch {
$_ | Write-PodeErrorLog
}
}
38 changes: 22 additions & 16 deletions src/Private/Timers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ function Start-PodeTimerRunspace
$script = {
while ($true)
{
$_remove = @()
$_now = [DateTime]::Now

$PodeContext.Timers.Values | Where-Object { $_.OnStart -or ($_.NextTick -le $_now) } | ForEach-Object {
$PodeContext.Timers.Values | Where-Object {
($_.OnStart -or ($_.NextTick -le $_now)) -and !$_.Completed
} | ForEach-Object {
$run = $true
$_.OnStart = $false

Expand All @@ -35,31 +36,36 @@ function Start-PodeTimerRunspace
# check if we have hit the limit, and remove
if ($run -and ($_.Limit -ne 0) -and ($_.Count -gt $_.Limit)) {
$run = $false
$_remove += $_.Name
$_.Completed = $true
}

if ($run) {
try {
$_event = @{ Lockable = $PodeContext.Lockable }
$_args = @($_event) + @($_.Arguments)
Invoke-PodeScriptBlock -ScriptBlock $_.Script -Arguments $_args -Scoped -Splat
}
catch {
$Error[0]
}
Invoke-PodeInternalTimer -Timer $_
}

$_.NextTick = $_now.AddSeconds($_.Interval)
}

# remove any timers
$_remove | ForEach-Object {
$PodeContext.Timers.Remove($_)
}

Start-Sleep -Seconds 1
}
}

Add-PodeRunspace -Type 'Main' -ScriptBlock $script
}

function Invoke-PodeInternalTimer
{
param(
[Parameter(Mandatory=$true)]
$Timer
)

try {
$_event = @{ Lockable = $PodeContext.Lockable }
$_args = @($_event) + @($Timer.Arguments)
Invoke-PodeScriptBlock -ScriptBlock $Timer.Script -Arguments $_args -Scoped -Splat
}
catch {
$_ | Write-PodeErrorLog
}
}
64 changes: 64 additions & 0 deletions src/Public/Core.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -753,9 +753,41 @@ function Add-PodeTimer
Script = $ScriptBlock
Arguments = $ArgumentList
OnStart = $OnStart
Completed = $false
}
}

<#
.SYNOPSIS
Adhoc invoke a Timer's logic.
.DESCRIPTION
Adhoc invoke a Timer's logic outside of its defined interval. This invocation doesn't count towards the Timer's limit.
.PARAMETER Name
The Name of the Timer.
.EXAMPLE
Invoke-PodeTimer -Name 'timer-name'
#>
function Invoke-PodeTimer
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]
$Name
)

# ensure the timer exists
if (!$PodeContext.Timers.ContainsKey($Name)) {
throw "Timer '$($Name)' does not exist"
}

# run timer logic
Invoke-PodeInternalTimer -Timer ($PodeContext.Timers[$Name])
}

<#
.SYNOPSIS
Removes a specific Timer.
Expand Down Expand Up @@ -912,9 +944,41 @@ function Add-PodeSchedule
Script = $ScriptBlock
Arguments = (Protect-PodeValue -Value $ArgumentList -Default @{})
OnStart = $OnStart
Completed = $false
}
}

<#
.SYNOPSIS
Adhoc invoke a Schedule's logic.
.DESCRIPTION
Adhoc invoke a Schedule's logic outside of its defined cron-expression. This invocation doesn't count towards the Schedule's limit.
.PARAMETER Name
The Name of the Schedule.
.EXAMPLE
Invoke-PodeSchedule -Name 'schedule-name'
#>
function Invoke-PodeSchedule
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]
$Name
)

# ensure the schedule exists
if (!$PodeContext.Schedules.ContainsKey($Name)) {
throw "Schedule '$($Name)' does not exist"
}

# run schedule logic
Invoke-PodeInternalScheduleLogic -Schedule ($PodeContext.Schedules[$Name])
}

<#
.SYNOPSIS
Removes a specific Schedule.
Expand Down

0 comments on commit 787af95

Please sign in to comment.