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 secretsfrom the API and apply it to two diferent variables:
-
Will apply the full set of results fetched for the resource type
secretsto variablethis.namespacedSecrets -
Will apply the parsed set of results (
parsingFuncdefines how data should be parsed) to the variablethis.imagePullNamespacedSecretswhich 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/listfolder on the Rancher Dashboard project - a default list, which will apply to all resources that aren't defined on
/shell/listfolder 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
loadingcomputed prop in the mixin that is the default flag for the loading state of aResourceTable - There is a
rowscomputed 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 fetchcheckcatalog.cattle.io.appcustom list -
custom list implementation, without
async fetchcheckcatalog.cattle.io.clusterrepocustom list -
custom list implementation, with
async fetchand$loadingResourcescheckfleet.cattle.io.bundlecustom list -
custom list implementation, with
async fetchand it's own Masthead instance checkfleet.cattle.io.gitrepocustom list -
multiple resources implementation check
workloadcustom list