Posted on Leave a comment

Best Practices for Universal Dashboard Performance

Universal Dashboard is a web framework for PowerShell. It allows you to create websites and REST APIs with just PowerShell script. Unlike other managed languages, like C# and F#, PowerShell is not compiled completely to highly optimized IL code during a compilation step. Instead, it is parsed, tokenized, interpreted and compiled during runtime. This results in a huge difference in the performance of other IL-based languages. In a framework such as Universal Dashboard, this can be especially evident.

In this blog post, we will look at some performance considerations when looking at using Universal Dashboard as your platform for your next web project.

Base Line

The baseline performance for the Universal Dashboard web-server is below. Universal Dashboard is built on ASP.NET Core. Although ASP.NET Core is capable of extremely high requests per second, Universal Dashboard clocks in a bit lower. The reason for this is that each request must allocate a runspace from a runspace pool, set up the runspace for execution, parse and execute a PowerShell Script Block and then serialize any results to JSON.

You can see below that the web server was capable of about 1000 requests per second on a machine with 8 CPU cores. Your results may vary but this should suffice for most low-to-medium traffic internal tools.

PS C:\Users\adamr> Measure-Command { 1..8 | % { Start-ThreadJob { 1..1000 | % { Invoke-WebRequest http://localhost:10004/test  } }  } | Wait-Job }
 
 Days              : 0
 Hours             : 0
 Minutes           : 0
 Seconds           : 8
 Milliseconds      : 432

Performance Tips

Cache Whenever Possible

Most of the time, the performance issues people face with Universal Dashboard have nothing to do with Universal Dashboard. Running PowerShell scripts can be slow. The is especially true when accessing remote resources. Users expect quick response from websites. To aid in this, it’s suggested to utilize Scheduled Endpoints and the Cache scope to avoid having to load resources every time a page loads.

For example, assume that I’m calling a remote REST API to load some resources in an endpoint that then displays them in a grid.

New-UDGrid -Title 'Movies' -Endpoint {
     Invoke-RestMethod http://movieindex.io/api/movies | Out-UDGridData
}

Due to the nature of how Endpoint script blocks work, the above would call the movieindex.io REST API every time the page is loaded. If you have many users accessing your dashboard, this means that you will have many calls to movieindex.io.

In order to improve the performance of this type of component, you can instead load the movie data on an interval and show users cached data. Instead of each user reading directly from the movieindex.io API, they are now reading from the Universal Dashboard Cache scope’s memory.

$Schedule = New-UDEndpointSchedule -Every 10 -Minute
 $Endpoint = New-UDEndpoint -Schedule $Schedule -Endpoint {
     $Cache:Movies = Invoke-RestMethod http://movieindex.io/api/movies
 }
 New-UDGrid -Title 'Movies' -Endpoint {
     $Cache:Movies | Out-UDGridData
 }

You can cache any amount of data you’d like up until you run out of memory on your machine. Be careful with caching large database tables in memory.

Favor Content over Endpoint

Content and Endpoint script blocks can be confusing. The main difference between a Content and an Endpoint script block is that Content is executed at the time the cmdlet is run and an Endpoint script block is executed when a component is loaded on the page.

For example, if we have a New-UDElement with the content below, the script block itself is actually executed when the New-UDElement is called.

New-UDElement -Tag 'div' -Content {
     New-UDElement -Tag 'div' -Content {'I run right away!'}
}

Alternatively, if you use an Endpoint, something different happens. The Endpoint script block is not run when the cmdlet is executed. Instead, it is cached inside the Universal Dashboard Endpoint Service for execution at a later time. Typically, this later time is when the component is loaded on the page.

New-UDElement -Tag 'div' -Endpoint {
     New-UDElement -Tag 'div' -Content {'I run when the page is loaded!'}
}

There is a visible performance difference between these different methods. The first method returns all the data during the first HTTP request from the server. The second method requires a second HTTP method to call back to the server to execute the endpoint script block and return the resulting data.

This can be especially tricky when nesting many Endpoint script blocks together. The below example requires 5 HTTP requests to be made.

New-UDElement -Tag 'div' -Endpoint {
     New-UDElement -Tag 'div' -Endpoint {'I run when the page is loaded!'}
     New-UDElement -Tag 'div' -Endpoint {'I run when the page is loaded!'}
     New-UDElement -Tag 'div' -Endpoint {'I run when the page is loaded!'}
}

The benefit of the Endpoint script block is that it allows for dynamic data and controls to be generated when the page is loaded. This is a huge feature of UD. It’s recommended to wrap the outer most component, where it makes sense, in an endpoint and generate the inner components with the Content script block.

The below example creates an outer most div using the Endpoint script block. This means it will require an HTTP request to load the data for the content of component. The inner components use content so they will not require another HTTP request back to the server. They are still dynamic because they are nested within a dynamic Endpoint script block.

New-UDElement -Tag 'div' -Endpoint {
     $DateTime = Get-Date
     New-UDElement -Tag 'div' -Content {$DateTime.Hour}
     New-UDElement -Tag 'div' -Content {$DateTime.Minute}
     New-UDElement -Tag 'div' -Content {$DateTime.Second}
}

Avoid Overuse of New-UDElement

New-UDElement is a very versatile component that allows you to create any HTML element, hook up event handlers and set attributes. The downside with New-UDElement is it requires a lot of information to be sent from the server to the web browser. With each New-UDElement call, the tag, attributes, and any event handlers need to be communicated to the client machine.

Using purpose-built controls, such as New-UDChart, require only the data to be sent to the client rather than all the HTML information.

The previous version of Universal Dashboard used New-UDElement heavily for many of the standard components. Rather than defining JavaScript components and then sending data via purpose-built cmdlets, cmdlets such as New-UDButton, New-UDFab, and New-UDCollapsible, defined their entire structure and data using New-UDElement. This resulted in the JSON payload sent from the UD server to the browser to be very large.

For example, in Universal Dashboard 2.2.0, creating a basic UDCollapsible with a single collapsible item was 1203 characters.

PS C:\Users\adamr> (New-UDCollapsible -Items { New-UDCollapsibleItem -Title "Test" -Content { } } | ConvertTo-Json -Compress).Length
1203

In Univeral Dashboard 2.4.1, the same command returns a JSON payload of 397 characters.

PS C:\Users\adamr> (New-UDCollapsible -Items { New-UDCollapsibleItem -Title "Test" -Content { } } | ConvertTo-Json -Compress).Length
397

Due to this reason, many of the most common controls have now been built into React components. If you want to build your own React components for Universal Dashboard, check out this repository.

Posted on Leave a comment

Universal Dashboard – 2.4 Beta 1 – Dashboard Designer, Material UI Components and more!

Today, we’ve released PowerShell Universal Dashboard v2.4.0-beta1. You can download the latest from the PowerShell Gallery. There are tons of awesome features and bug fixes.

Install-Module UniversalDashboard -AllowPrerelease

Features

Universal Dashboard Designer

One of the first feature requests I received for Universal Dashboard has now started to take shape. The Universal Dashboard Designer is beginning life as a drop and drop page creator. You can select from components, edit their properties and drag and drop them on the design surface. A script is generated in the background that will run in both Enterprise and Community. There is no need to write PowerShell script by hand to layout your dashboards with the UD Designer.

There is a hosted demo version of the designer available for you to play with now.

Hosted Demo Site

Give it a shot and provide us with feedback. If you install the UniversalDashboard module, you can run the designer locally be using Start-UDPageDesigner.

For a more complete run down on how to use the designer, take a look at my YouTube channel.

Material UI Components

In an effort to provide even more options to Universal Dashboard users, we have decided to start to migrate from the Materialize library to the Material UI library. In addition to having many more options, the library is also built on React so it integrates much easier into Universal Dashboard. Thanks to Alon Gvili, there are now 10 of the Material UI controls available in UD.

  • Avatar
  • Button
  • Card
  • Chip
  • Icon Button
  • Link
  • List
  • Paper
  • PDF
  • Typography

For example, if you’ve been using the existing New-UDCard, you’ll be happy to find the New-UDMUCard provides many more options for creating exceedingly customizable and beautiful widgets for your dashboards.

To see how this example was produced, visit the UD GitHub page.

Grid Layout

The UD designer takes advantage of the the New-UDGridLayout command to organize components on the page. Unlike New-UDRow and New-UDColumn you don’t nest the layout and controls directly. The layout is defined via Hashtables or JSON. The leads to cleaner scripts that don’t nest deeply with scriptblocks. Additionally, you can provide the ability for users to drag and drop components themselves. Their state is saved to local browser storage so, whenever they load the dashboard, they have the same layout.

Simplified Custom React Components

Due to enhancements in custom component support, the entire Material UI component library is defined with only JavaScript and PowerShell. There is no need for C# code. This PowerShell to React binding makes it extremely easy to introduce new components. UD can load webpack chunks, CSS and even source maps for custom component libraries.

If you want to see what this looks like, head over to the Universal Dashboard GitHub repository. Full documentation for this feature is in the works and you’ll see many more React controls being brought into the UD ecosystem.

Access to Authorization Policies

You can now access Authorization Policies via Get-UDAuthorizationPolicy. Rather than just limiting which page a user has access to, you can now use this cmdlet in your Endpoints to adjust how the page itself behaves based on that authorization policy. For example, you can check the authorization policy to see if a user is an admin and then hide a control based on if they are not.

Here’s an example of doing just that.

 $AuthPolicy = Get-UDAuthorizationPolicy 

if ($AuthPolicy -contains 'AdminPolicy') {
     New-UDHeading -Text "Only Admins See This"
}  

For a full list of issues, please visit GitHub.