Skip to content

Shared State

Most things in Pode run in isolated runspaces: routes, middleware, schedules - to name a few. This means you can't create a variable in a timer, and then access that variable in a route. To overcome this limitation you can use the Shared State feature within Pode, which allows you to set/get variables on a state shared between all runspaces. This lets you can create a variable in a timer and store it within the shared state; then you can retrieve the variable from the state in a route.

You also have the option of saving the current state to a file, and then restoring the state back on server start. This way you won't lose state between server restarts.

You can also use the State in combination with Lock-PodeObject to ensure thread safety - if needed.

Tip

It's wise to use the State in conjunction with Lock-PodeObject, to ensure thread safety between runspaces.

Warning

If you omit the use of Lock-PodeObject, you might run into errors due to multi-threading. Only omit if you are absolutely confident you do not need locking. (ie: you set in state once and then only ever retrieve, never updating the variable).

Usage

Where possible use the same casing for the -Name of state keys. When using Restore-PodeState the state will become case-sensitive due to the nature of how ConvertFrom-Json works.

Set

The Set-PodeState function will create/update a variable in the state. You need to supply a name and a value to set on the state, there's also an optional scope that can be supplied - which lets you save specific state objects with a certain scope.

An example of setting a hashtable variable in the state is as follows:

Start-PodeServer {
    Add-PodeTimer -Name 'do-something' -Interval 5 -ScriptBlock {
        Lock-PodeObject -ScriptBlock {
            Set-PodeState -Name 'data' -Value @{ 'Name' = 'Rick Sanchez' } | Out-Null
        }
    }
}

Alternatively you could use the $state: variable scope to set a variable in state. This variable will be scopeless, so if you need scope then use Set-PodeState. $state: can be used anywhere, but keep in mind that like $session: Pode can only remap the this in scriptblocks it's aware of; so using it in a function of a custom module won't work. Similar to the example above:

Start-PodeServer {
    Add-PodeTimer -Name 'do-something' -Interval 5 -ScriptBlock {
        Lock-PodeObject -ScriptBlock {
            $state:data = @{ 'Name' = 'Rick Sanchez' }
        }
    }
}

Get

The Get-PodeState function will return the value currently stored in the state for a variable. If the variable doesn't exist then $null is returned.

An example of retrieving a value from the state is as follows:

Start-PodeServer {
    Add-PodeTimer -Name 'do-something' -Interval 5 -ScriptBlock {
        $value = $null

        Lock-PodeObject -ScriptBlock {
            $value = (Get-PodeState -Name 'data')
        }

        # do something with $value
    }
}

Alternatively you could use the $state: variable scope to get a variable in state. $state: can be used anywhere, but keep in mind that like $session: Pode can only remap the this in scriptblocks it's aware of; so using it in a function of a custom module won't work. Similar to the example above:

Start-PodeServer {
    Add-PodeTimer -Name 'do-something' -Interval 5 -ScriptBlock {
        $value = $null

        Lock-PodeObject -ScriptBlock {
            $value = $state:data
        }

        # do something with $value
    }
}

Remove

The Remove-PodeState function will remove a variable from the state. It will also return the value stored in the state before removing the variable.

An example of removing a variable from the state is as follows:

Start-PodeServer {
    Add-PodeTimer -Name 'do-something' -Interval 5 -ScriptBlock {
        Lock-PodeObject -ScriptBlock {
            Remove-PodeState -Name 'data' | Out-Null
        }
    }
}

Save

The Save-PodeState function will save the current state, as JSON, to the specified file. The file path can either be relative, or literal. When saving the state, it's recommended to wrap the function within Lock-PodeObject.

An example of saving the current state every hour is as follows:

Start-PodeServer {
    Add-PodeSchedule -Name 'save-state' -Cron '@hourly' -ScriptBlock {
        Lock-PodeObject -ScriptBlock {
            Save-PodeState -Path './state.json'
        }
    }
}

When saving the state, you can also use the -Exclude or -Include parameters to exclude/include certain state objects from being saved. Saving also has a -Scope parameter, which allows you so save only state objects created with the specified scope(s).

You can use all the above 3 parameter in conjunction, with -Exclude having the highest precedence and -Scope having the lowest.

By default the JSON will be saved expanded, but you can saved the JSON as compressed by supplying the -Compress switch.

Restore

The Restore-PodeState function will restore the current state from the specified file. The file path can either be relative, or a literal path. if you're restoring the state immediately on server start, you don't need to use Lock-PodeObject.

An example of restore the current state on server start is as follows:

Start-PodeServer {
    Restore-PodeState './state.json'
}

By default, restoring from a state file will overwrite the current state. You can change this so the restored state is merged instead by using the -Merge switch. (Note: if you restore a key that already exists in state, this will still overwrite that key).

Full Example

The following is a full example of using the State functions. It is a simple Timer that creates and updates a hashtable variable, and then a Route is used to retrieve that variable. There is also another route that will remove the variable from the state. The state is also saved on every iteration of the timer, and restored on server start:

Start-PodeServer {
    Add-PodeEndpoint -Address * -Port 8080 -Protocol Http

    # create the shared variable
    Set-PodeState -Name 'hash' -Value @{ 'values' = @(); } | Out-Null

    # attempt to re-initialise the state (will do nothing if the file doesn't exist)
    Restore-PodeState -Path './state.json'

    # timer to add a random number to the shared state
    Add-PodeTimer -Name 'forever' -Interval 2 -ScriptBlock {
        # ensure we're thread safe
        Lock-PodeObject -ScriptBlock {
            # attempt to get the hashtable from the state
            $hash = (Get-PodeState -Name 'hash')

            # add a random number
            $hash.values += (Get-Random -Minimum 0 -Maximum 10)

            # save the state to file
            Save-PodeState -Path './state.json'
        }
    }

    # route to return the value of the hashtable from shared state
    Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
        # again, ensure we're thread safe
        Lock-PodeObject -ScriptBlock {
            # get the hashtable from the state and return it
            $hash = (Get-PodeState -Name 'hash')
            Write-PodeJsonResponse -Value $hash
        }
    }

    # route to remove the hashtable from shared state
    Add-PodeRoute -Method Delete -Path '/' -ScriptBlock {
        # ensure we're thread safe
        Lock-PodeObject -ScriptBlock {
            # remove the hashtable from the state
            Remove-PodeState -Name 'hash' | Out-Null
        }
    }
}