Kubernetes Resources Data Load Optimizations
In order to improve performance of Rancher Dashboard especially for systems with a large number of resources, several changes have been introduced to the codebase such as incremental loading of list views, manual refresh of list views and optimisation for "secondary" data load.
The resource-manager
mixin for secondary data load
This is the mixin responsible for optimizing the loading of "secondary" data (data that is used to complete information on a given page, ex: populating a select input) which doesn't need to be watched (updated via websocket) or stored in the Vue store (Vuex).
It fetches namespaced data directly from our API, avoiding the need to fetch all items and then filtering by a given namespace.
If you specify a namespace but any of the given resources you configured for your data fetch is not namespaced, the mixin will take care of that.
It will only return the data for successful network requests, throwing a console.error
for any requests that have failed.
It provides a way to fetch data only for namespaced resources, which is very useful on a CREATE screen scenario where if a user toggles the namespace selector, data will need to be fetched again, but only for those namespace-dependent resources.
NOTE: by default, data fetched using this mixin will NOT have a model applied to it. There's an option called classify that will apply models if needed.
Quick usage guide
Just import the resource-manager
mixin to the given page, create the configuration variable that tells which data it should load and use the method resourceManagerFetchSecondaryResources
to load the given data.
If you want a code example on Rancher Dashoard, check for a full implementation of the "secondary data load" on mixins/workloads.js
.
Example:
Importing mixin
import ResourceManager from '@shell/mixins/resource-manager';
Setting up a configuration variable
Allowed params for the configuration object:
- @param {String}
namespace
- Namespace identifier - @param {Object}
data
- Object containing info about the data needed to be fetched and how it should be parsed.
NOTE: The object KEY NEEDS to be the resource TYPE!
- @param {Array}
data[TYPE].applyTo
- The array of operations needed to be performed for the specific data TYPE - @param {String}
data[TYPE].applyTo[x].var
- The 'this' property name that should be populated with the data fetched - @param {Boolean}
data[TYPE].applyTo[x].classify
- Whether the data fetched should have a model applied to it - @param {Function}
data[TYPE].applyTo[x].parsingFunc
- Optional parsing function if the fetched data needs to be parsed
Example of a configuration object to be used with the resource-manager
:
this.secondaryResourceDataConfig = {
namespace: 'fleet-default',
data: {
'secrets': {
applyTo: [
{ var: 'namespacedSecrets' },
{
var: 'imagePullNamespacedSecrets',
parsingFunc: (data) => {
return data.filter(secret => (secret._type === 'docker' || secret._type === 'docker_json'));
},
classify: true
}
]
}
}
}
Looking at the above example, we configuring the secondary data load to get data for the namespace fleet-default
and with fetch secrets
from the API and apply it to two diferent variables:
-
Will apply the full set of results fetched for the resource type
secrets
to variablethis.namespacedSecrets
-
Will apply the parsed set of results (
parsingFunc
defines how data should be parsed) to the variablethis.imagePullNamespacedSecrets
which will apply the correct model to that parsed dataset because of the flagclassify
Initialize secondary data load
async fetch() {
this.resourceManagerFetchSecondaryResources(this.secondaryResourceDataConfig);
}
Methods available
resourceManagerFetchSecondaryResources
Function used to initialize the data loading procedure
- @param {Object}
dataConfig
- Configuration object - @param {Boolean}
onlyNamespaced
- Flag to enable the fetch from API ONLY for namespaced resources (will ignore requests for non-namespaced resources you have defined on the configuration object). Defaults tofalse
Example:
async fetch() {
this.resourceManagerFetchSecondaryResources(dataConfig, onlyNamespaced);
}
resourceManagerClearSecondaryResources
Function used to clear the results for the secondary resource data fetch. It's a very useful method in a CREATE screen scenario where a user can "create" a namespace on the UI, operation which will make all our previous namespaced results invalid and in need to be cleared.
- @param {Object}
dataConfig
- Configuration object - @param {Boolean}
onlyNamespaced
- Flag to enable the fetch from API ONLY for namespaced resources (will ignore requests for non-namespaced resources you have defined on the configuration object). Defaults tofalse
Example:
async fetch() {
this.resourceManagerClearSecondaryResources(dataConfig, onlyNamespaced);
}
The resource-fetch
mixin for incremental loading and manual refresh
The resource-fetch
mixin is the controller for all operations on a list view regarding the incremental loading and manual refresh features.
Both incremental loading and manual refresh are features that affect LIST views and are activated on the Performance
section under Global Settings
on Rancher Dashboard UI via a flag and also a configurable threshold of number of items for which is they are triggerable.
To understand better how the mixin works, one should understand first how list views are managed and rendered.
A list view of resources (table with a list of items of a given resource) can be of two types:
- a custom list, which are defined on
/shell/list
folder on the Rancher Dashboard project - a default list, which will apply to all resources that aren't defined on
/shell/list
folder with a dedicated file
Both have a common entry point: the /shell/components/ResourceList/index.vue
file. This is where all lists start their rendering process.
This is where it will decide to load a custom view (follow this.hasListComponent
) or render a simple ResourceTable
(check template part of the file).
For a default list, we should check for the last couple lines on the async fetch
method for the function called this.$fetchType(resource)
which is a method exposed by the mixin (where you can pass the resource type as argument) which will handle all data loading for that given resource type.
Under the hood it performs a findAll
but it append some specific flags for incremental loading (incremental
) and manual refresh (hasManualRefresh
&& watch
) which will tap into the normal flow of data request from our API.
For a default list view, all should be covered with defaults by the /shell/components/ResourceList/index.vue
file.
Two quick important notes:
- There is a
loading
computed prop in the mixin that is the default flag for the loading state of aResourceTable
- There is a
rows
computed prop in the mixin that is the default list of items loaded on the mixin
If any of these two are referenced on a custom list template part but aren't defined on the JS part of the same file, it's because it uses the defaults which come from the mixin itself.
Another important configuration part is about the incremental loader options (component name ResourceLoadingIndicator
).
For custom lists the ResourceTable
component is on the lookout for a method called $loadingResources
which is defined on your custom list methods. If it exists, it should return an object with two properties:
loadIndeterminate
- by default the incremental loader is of a determinate type, which means that it will show the current count of items already loaded / total items to be loaded.
loadResources
- by default the incremental loader will load the counts for the resource passed, but there are custom list views (ex: workloads) are comprised of multiple resources. You'll need to pass an array of all resource types for that list if you want to show the correct numbers on the incremental loader.
Incremental loader
component (ResourceLoadingIndicator
) is rendered inside the Masthead
component (ResourceList... there are two Mastheads).
Generally, custom lists don't have a Masthead
specified on it's template, but there's a Masthead rendered. That comes from /shell/components/ResourceList/index.vue
,
Implementation examples
-
simple custom list implementation, with it's own
async fetch
checkcatalog.cattle.io.app
custom list -
custom list implementation, without
async fetch
checkcatalog.cattle.io.clusterrepo
custom list -
custom list implementation, with
async fetch
and$loadingResources
checkfleet.cattle.io.bundle
custom list -
custom list implementation, with
async fetch
and it's own Masthead instance checkfleet.cattle.io.gitrepo
custom list -
multiple resources implementation check
workload
custom list