Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance dev/pr4 - pagination #8507

Closed
wants to merge 12 commits into from
Closed

Performance dev/pr4 - pagination #8507

wants to merge 12 commits into from

Conversation

ghost
Copy link

@ghost ghost commented Mar 23, 2023

Full Description of changes and bits and pieces to test upcoming.

Edit - Associated issue #6541 (won't fix, pr merges to performance-dev

@github-actions github-actions bot assigned ghost Mar 23, 2023
@@ -15,7 +15,7 @@ import {
} from '@shell/config/types';
import { HCI } from '../types';
import ResourceSummary, { resourceCounts, colorToCountName } from '@shell/components/ResourceSummary';
import { colorForState } from '@shell/plugins/dashboard-store/resource-class';
import { colorForState } from '@shell/plugins/steve/resourceUtils/resource-class';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything from @shell/plugins/dashboard-store should still live within dashboard-store

Copy link
Member

@richard-cox richard-cox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To Do

  • Check comments
  • slow loading of secondary resource leads to eventual list automatically updated
  • force fetch primary / secondary resource and list automatically updates?
  • capture issue on duplication of computed/getter fields executions (done in cache and in ui thread)

To Test

  • build and load harvester plugin

}

/**
* Find the resource/s associated with the params in the cache. IF we don't have the resource/s for the params we'll fetch them
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs updating.

}

// spread the raw resource and the calculatedFields together for the filterCondition which doesn't care about the difference
const resources = Object.values(this.resources)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: RC can this be combined with below

@richard-cox richard-cox added this to the v2.7.next2 milestone Apr 4, 2023
Sean and others added 4 commits April 4, 2023 09:56
- cluster socket now works - url wasn't updated to contain url with cluster id
- fixed on instance of mgmtById
- added upstream fix of addSchemaIndexFields
Copy link
Member

@richard-cox richard-cox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • To make it a little clearer model base classes (shell/plugins/steve/hybrid-class.js, norman-class and steve-class) should move in to a shell/plugins/steve/models. that should make it cleared there's three resource based buckets - models, caches and utils. at some point we may want to look at moving advanced worker specific files into a single folder


return [sortBy, sortDesc];
},
setGroup: (groupBy) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this wired in, looks like it's missing due to @group-value-change="group = $event"

@@ -356,6 +356,10 @@ export default {
:loading="loading"
group-tooltip="resourceTable.groupBy.project"
key-field="_key"
:set-page-fn="resourceQueryMethods.setPage"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of plumbing to connect sortable table pagination changes to the resource-fetch-query. Could this be improved?

  • Could they be trimmed up into a pagination prop which receives an object of pagination setX fns, also covering listLength?
  • Could they be stored in vuex (pros and cons to this one though)?

advancedWorker() {
return !!this.perfConfig?.advancedWorker?.enabled;
},
listLength() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listLength and totalLength needs some jsdocs. is list length the size of filtered collection, and total length the unfiltered size?

if ( !this.isNamespaced || (isAll && !this.currentProduct?.hideSystemResources) || this.ignoreFilter) {
// If the resources isn't namespaced or we want ALL of them, there's nothing to do or if the resources are filtered in the fetched results.
// if ( !this.isNamespaced || (isAll && !this.currentProduct?.hideSystemResources) || this.ignoreFilter || this.setSearchFn) {
if ( this.setSearchFn || !this.isNamespaced || (isAll && !this.currentProduct?.hideSystemResources) || this.ignoreFilter) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a bit obfuscated, but can get away with tweak to the comment

// If the resources isn't namespaced or we want ALL of them, there's nothing to do or if the resources are filtered in the fetched results.

-->

// If the resources isn't namespaced or we want ALL of them, there's nothing to do or if the resources are filtered in the fetched results (setSearchFn will defined).

<template
v-for="(_, slot) of $scopedSlots"
v-slot:[slot]="scope"
<span>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

span shouldn't be needed?

/**
* Requests data from the cache's data source
*/
async request(params = {}) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on comment within method, we should consider changing this to find (aka make http request if needed and return values from cache) to align with verbage from vuex side?

/**
* Errors sent over the thread boundary must be clonable. The response to `fetch` isn't, so cater for that before we send it over
*/
const parseRequestError = (e) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need wiring in to a similar top level place (it handled both types of exception fetch throws)

updateCache: (type, payload, detail = false) => {
createCache: (type) => {
if (type && !caches[type]) {
caches[type] = resourceCache(type, getters, rootGetters, state.api, uiApi, workerActions.createCache);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resourceCache doesn't have this many args

}

/**
* Find the resource/s associated with the params in the cache. IF we don't have the resource/s for the params we'll fetch them
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this method now doesn't make the http request

  • jsdoc needs updating
  • find name might be better renamed to get (to avoid confusion with the find = http request + get from cache concept)

import ReplicationcontrollerCache from '@/shell/plugins/steve/caches/replicationcontroller';
import CronJobCache from '@/shell/plugins/steve/caches/batch.cronjob';
import JobCache from '@/shell/plugins/steve/caches/batch.job';
import WorkloadCombinedCache from '@shell/plugins/steve/caches/workload.combined';

export default {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs updating and testing with all the resource cache's

@ghost ghost marked this pull request as ready for review April 12, 2023 08:06
Copy link
Member

@richard-cox richard-cox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking

Bugs

  • General
    • Race condition on some systems means url used to connect to cluster web socket hasn't been changed from default (see updateWorker for potential fix)
  • User and Auth
    • Cannot Assign Group Role
      • Setup github provider --> assign group role. & create user
        • Cannot read properties of undefined (reading 'description')
        • Cannot read properties of undefined (reading 'description')
          at _getDescription (steve-description-class.js:2:1)
    • Cannot Create User
      • same as above
    • Cannot create any role
      • Create global role --> white page error
        • Cannot read properties of null (reading 'detailPageHeaderActionOverride')
        • Error in fetch(): TypeError: Cannot read properties of undefined (reading 'description')
          at _getDescription (steve-description-class.js:2:1)
  • Cluster Explorer
    • Nav to Projects/Namespace. Error in console
      • console.js:31 Error in fetch(): TypeError: this.namespaces is not a function
        at VueComponent.selectedNamespaces (resource-fetch-query.js:293:1)
      • set page size to 10, refresh on project/namespace list, list shows 10 but no pagination controls
      • set page size to 10, refresh on project/namespace list, search fails to do anything (works on pods)
        • 5a55fdea-26a5-4fea-86d8-148085bd705f:58435 Uncaught (in promise) TypeError: value.includes is not a function
          at 5a55fdea-26a5-4fea-86d8-148085bd705f:58435:70
      • pods page, create a pod, dashboard returns to pods list. pod appears... then disappears
      • pods page, create a pod in another window, pod does not appear in original window
      • pods page, changing from all namespaces to a single namespace does not change the list
      • pods page. sorting by IP, Ready, Restarts doesn't work (no visible change to list)
      • changing NS filter does not hit advanced worker requests
  • Fleet (continues delivery)
    • Bundles, Workspaces sort by any field doesn't work. Git Repos works
  • Cluster Manager
    • Clusters list with an rke2 instance coming up has error in console

General PR

  • Failing e2e tests

General PR/Comments

  • Lots of jsdoc / comments needed. some // need to
  • Lots of ToDos still
  • Any file that's resource cache related should be named with cache rather than class

Tests (once bugs are fixed and more code review comments resolved)

  • Pagination
    • Pages (nex/prev, first/last page)
    • Sorting
      • default-sort-by honoured
      • on normal + calc fields
    • Filtering
      • on normal + calc fields
    • intermedate loading / manual refresh / forced ns filtering
    • Resources to check
      • Workloads, each type + pods
      • config maps
      • secrets
  • Finds
    • params like load, force, etc are honoured
    • re-check namespace vs namespaces
    • error handling
      • caused by something we throw, connection errors, errors from backend (see parseRequestError)
    • repeat finds with don't kick off http requests
      • with exact same params
      • with different page number
    • repeat find do kick off http requests
      • force on
  • Re-pagination
    • Change a secondary resource, primary resource's dependent field is updated, re-pagination happens, new page sent to ui thread
    • Change a primary resource, primary resource's fields are updated and resource page is re-calculated, re-pagination happens, new page sent to ui thread
    • Change primary or secondary resouce that does not change the current page means ui thread not updated
  • Secondary resources
    • Only required secondary resources are passed back to ui thread, which are used by models to calc fields
      • only those required secondary resource are returned
      • how does ui thread recognise these lists, what if we go from deployment (requiring pods) to pods list?
  • Fetching non-cluster info from within worker thread
    • how often, how caching is managed
  • Lists
    • resource-fetch-query - how do we differentiate between local ns filtering and request ns filtering
    • live reload columns work when sorting, filtering, re-paginated, etc
    • cacluated fields that rely on l10n work correctly
  • Performance Setting
  • Disabling advanced worker ... everything works as expected
  • Description of advanced worker in perf settings still makes sense
  • podsByNamespace functionality still works fine
  • Deep dive in to mixins/resource-fetch-query
    • does namespaceFilter fire at same time as resourceQuery
  • Error handling
    • Errors caused by code

Pending In Depth Review

  • mixins/resource-fetch-query
    • including pagination functions
  • podsByNamespace

Nice to have / not really blockers

  • Tidying up places where loose functions are exported
    • resourceUtils would be converted to export default { calculatedFields, fields }
  • Move pagination settings into vuex store alongside list to reduce LOTS of plumbing from components through to advanced worker
  • Make advanced worker perf setting more granualar

Not Blocking

Improvements / Tech Debt

  • resource cache's calculated fields are calculated within worker (to paginate) but aren't passed by to ui thread. ui thread model then (re)calculated fields.
    • This could be an issue if calc field iterates through another list, for example lots of finding deployment's pods. If this is optimised needs testing
  • We now pretty much mock the ctx / store within the advanced worker. That could be used in conjuction with the models (which receive ctx) to allow us to use the same models. Using models would mean no utils classes and much more simpler calculated fields (we could possibly use decorators on getters instead)
  • There's lots of boilerplate code in resource caches

return !!this.perfConfig?.advancedWorker?.enabled;
},
listLength() {
const { params: { resource: type } } = this.$route;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type is already available via this.resource

return this.$store.getters['cluster/listLength'](type);
},
totalLength() {
const { params: { resource: type } } = this.$route;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type is already available via this.resource

@@ -150,6 +164,11 @@ export default {
if (neu && !this.hasFetch) {
this.$fetchType(this.resource);
}
},
resourceQuery(neu, old) { // TODO: RC test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is similar to namespaceFilter, there's a couple of different use cases that had to cover (see comment). are the same needed use cases needed here?

@@ -550,7 +569,9 @@ export default {
return classify(ctx, data);
},

clone(ctx, { resource } = {}) {
clone(ctx, payload) {
const { resource } = payload;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change not needed (and is less save)

@@ -68,6 +67,7 @@ const {
WATCH_PENDING, WATCH_REQUESTED, WATCHING, STOPPED, REMOVE_PENDING, REQUESTED_REMOVE
} = WATCH_STATUSES;

// TODO: RC jsdocs
export default class ResourceWatcher extends Socket {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsdoc definitely needed for this one

});

this.__requests[cacheKey] = {
...this.__requests[cacheKey],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we spread?

import cacheClasses from '@shell/plugins/steve/caches';
import ResourceRequest from '@shell/plugins/steve/api/resourceRequest';
import Trace from '@shell/plugins/steve/trace';
import { CACHE_STATES } from '@shell/plugins/steve/caches/base-cache';
import { hashObj } from '~/shell/utils/crypto/browserHashUtils';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All (new) imports with ~/ need fixing


if (cacheAge > 500 && (uiRequests[cacheGetter] || []).length < 1) {
// TODO: RC question If the cache of non-cluster resources changes... do we ned to bump anything that depends on them?
self.postMessage({ get: cacheGetter });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we ever rely on content from outside the cluster store in any of the calculated fields?

@@ -394,15 +484,79 @@ const workerActions = {

Object.values(caches).forEach(c => c.setDebug(cache));
},
updateBatch(type, id, change) {
singleUpdateBatch(type, id, change) {
if (!state.batchChanges[type]) {
state.batchChanges[type] = {};
}
state.batchChanges[type][id] = change;
},
sendBatch(requestHash) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v important code block, needs jsdoc and comments

@@ -394,15 +484,79 @@ const workerActions = {

Object.values(caches).forEach(c => c.setDebug(cache));
},
updateBatch(type, id, change) {
singleUpdateBatch(type, id, change) {
if (!state.batchChanges[type]) {
state.batchChanges[type] = {};
}
state.batchChanges[type][id] = change;
},
sendBatch(requestHash) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't re-paginate (byPage) .. looks like this just pushes in-page resource updates

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant