MAIN MENU
Devolutions Blog

Announcements, updates, and insights from Devolutions.

Devolutions PowerShell Universal optimizing API performance illustration for the blog.

Optimizing API performance in PowerShell Universal: key improvements and results

Walk through West Wind WebSurge tests of a /hello endpoint, then compare LiteDB defaults, SystemLogLevel, New-PSULoggingTarget, persistent runspaces, MaxRunspaces, and SQL persistence from 150 to about 2,900 requests per second.

PowerShell Universal allows users to create APIs using PowerShell. These APIs can be used to create webhooks, integrate with other systems, or provide a REST API for your PowerShell scripts. In this post, we’ll look at some changes you can make to improve API endpoint performance.

In this post, we’ve used West Wind WebSurge to test API performance. This tool allows you to send a number of requests to a URL and measure the performance of the requests. We are running a 60 second test with 10 threads.

West Wind WebSurge session sending GET requests to localhost:5000/hello.
West Wind WebSurge load test against the PowerShell Universal /hello endpoint

For this test, we are running an unlicensed version of PowerShell Universal with a single API endpoint. The API endpoint is a simple script that returns a string.

New-PSUEndpoint -Url "/hello" -Method @('GET') -Endpoint {
    "hello"
}

We are using LiteDB for persistence.

Update to 4.1.5 or later

The first thing you can do is update to PowerShell Universal 4.1.5 or later. This version includes a number of performance improvements to the API system. We see about a 300% improvement in API performance with this release with default settings.

Results:

  • 4.1.4 with default settings: 150 requests per second
  • 4.1.5 with default settings: 470 requests per second

Adjust logging targets and system log settings

By default, PowerShell Universal has verbose logs configured. We will make the following changes to the logging configuration.

Disable verbose system logs

In appsettings.json, set SystemLogLevel to Error rather than Verbose.

{
    "SystemLogPath": "%ProgramData%\\PowerShellUniversal\\systemLog.txt",
    "SystemLogLevel": "Error"
}

Disable user-scope logging for APIs

Rather than sending log messages to the database and file system, we will disable user-scope logging for APIs. You can send log messages for specific features, such as Apps.

New-PSULoggingTarget -Type "Database" -Properties @{
} -Feature "App"

New-PSULoggingTarget -Type "File" -Properties @{
    path = "C:\ProgramData\PowerShellUniversal\log.txt"
} -Feature "App"

It’s also possible to reduce the log level for targets rather than disable them completely.

New-PSULoggingTarget -Type "Database" -Properties @{
} -Level "Error"

New-PSULoggingTarget -Type "File" -Properties @{
    path = "C:\ProgramData\PowerShellUniversal\log.txt"
} -Level "Error"

Results:

  • 4.1.4: 980 requests per second
  • 4.1.5: 1,200 requests per second

Environment settings

Certain features of PowerShell Universal environments can impact performance. We will make the following changes to the environment settings.

Persistent runspaces

Resetting the runspace state takes time, so avoiding this can improve performance. Note that persistent runspaces keep the value of variables, functions, and aliases defined within the execution of the endpoint. Depending on the implementation of your endpoint, this may not be desirable.

Increase max runspaces

By default, PowerShell Universal will create 25 runspaces per environment. We will increase this to 100. This will increase memory usage but will slightly improve performance.

New-PSUEnvironment -Name "Integrated" -Version "7.3.6" -Path "Universal.Server" -Variables @('*') -PersistentRunspace -MaxRunspaces 100 -Description "An environment for running scripts directly in the PowerShell Universal server."

Results:

  • 4.1.4: 1,025 requests per second
  • 4.1.5: 1,300 requests per second

A note on external environments

Above, you may notice that we are using the Integrated environment. This is an environment that is built into PowerShell Universal. It is a PowerShell environment that runs directly in the PowerShell Universal process. Even if we were to adjust the endpoints to use an external environment, the results are similar. This is running within a PowerShell 7.3.7 environment.

  • 4.1.4: 1,015 requests per second
  • 4.1.5: 1,250 requests per second

Use SQL persistence

SQL persistence can greatly improve API throughput. This test was performed against a local SQL database with all the above settings configured and only run on version 4.1.5.

Results:

  • 4.1.5: 2,900 requests per second

Conclusion

With these changes, we’ve improved the performance of our API by about 1,900%. We’ve gone from 150 requests per second to about 2,900 requests per second. Using SQL persistence had the greatest increase in performance. Particularly combined with changes to the logging system, you can achieve a significant increase in performance.

Please reach out with feedback or questions on the forums.

Ready to build? Download PowerShell Universal.