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

UI: Fix client sorting #6817

Merged
merged 8 commits into from
Dec 12, 2019
Merged

UI: Fix client sorting #6817

merged 8 commits into from
Dec 12, 2019

Conversation

backspace
Copy link
Contributor

This is a first pass at a fix for #6262. Here’s a GIF of it working; look for the fourth client to change state and become the third in the list:

client-sorting

Here’s a GIF of what happens in master; look for the fourth client changing state but not sort position:

client-sorting-broken

You can see it yourself by visiting the master Netlify deployment and running this in the console:

NomadUi.__container__.lookup('service:store').peekAll('node').find(client => client.id.startsWith('5d5')).set('isDraining', false)

There are two changes here, and some caveats/commentary:

  1. The “State“ table column was actually sorting only by status. The state was not an actual property, just something calculated in each client row, as a product of status, isEligible, and isDraining. This PR extracts that calculation into the model so it can be sorted on.

    • that’s why in the second GIF above, even before anything has changed, the three draining clients are separated by an initializing interloper
    • there‘s already a compositeStatus computed property that combines status and isEligible; it’s used to determine the colour of the circle on the individual client route
    • so state and compositeStatus are close relatives, and the nomenclature is confusing, not sure of the best way out of this
  2. The Sortable mixin declares dependent keys that cause the sort to be live-updating, but only if the members of the array change, such as if a new client is added, but not if any of the sortable properties change. This PR adds a SortableFactory function that generates a mixin whose listSorted computed property includes dependent keys for the sortable properties, so the table will live-update if any of the sortable properties change, not just the array members.

    • I kept the Sortable interface stable by making it call SortableFactory with no list of dependent keys
    • but this problem exists on other tables; you can see it on the Netlify deployment by visiting the job list and running this in the console: NomadUi.__container__.lookup('service:store').peekAll('job').find(job => job.name.startsWith('jbod')).set('type', 'system')
    • so maybe there shouldn’t be a sort-property-keyless version?
    • the test for this involves reaching into the store and changing a property on a model, which isn’t an exact representation of how client property changes arrive in the UI, but seems close enough to me vs having polling happen in an acceptance test, but opinions on this hackery will surely vary 🙃

- listSorted: a copy of listToSort that has been sorted
*/
export default function sortableFactory(properties) {
const eachProperties = properties.map(property => `listToSort.@each.${property}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with your concern that this problem should be fixed for all tables, and I also agree your resistance to dig into all of them at once.

Maybe a good middle ground just so the problem isn't forgotten about once this merges is to warn here if properties.length === 0. The warning could also include something like "The Sortable mixin is deprecated in favor of SortableFactory".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 thanks for the suggestion; I added something in 0131c36, it’s somewhat unwieldy because of when mixins are mixed in, but it works, thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like it'll do, but I agree it's unwieldy. What's preventing you from putting it right here, in the sortableFactory but not in the returned Mixin? Don't we have all the information at this point? It would also prevent the state tracking needed right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, I merged this 😳 I’m sorry

I think that would have the same problem though because the function is called when the mixin is created, which happens when the application loads, so the warnings all happen at the same time.

ui/app/mixins/sortable-factory.js Show resolved Hide resolved
Copy link
Contributor

@DingoEatingFuzz DingoEatingFuzz left a comment

Choose a reason for hiding this comment

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

Nice job chasing down this bug and good writeup too!

I agree with all the decisions you made, even if they fall in the "lesser of two evils" camp.

The only strong suggestions I have are the warning in SortableFactory and merging state and compositeStatus and glossing over the behavioral different in node-status-light.scss.

But I'd also be happy merging this as is if you disagree.

} else {
return this.status;
}
}),
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmmm. I think we can drop compositeStatus in favor of state (but maybe keep the compositeStatus name? idk) with little effort or risk. We could add a css class for the node status light for draining that is equivalent to ineligible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, I did this in ad3863f. One caveat is that I removed an assertion in a client detail test:

    assert.equal(
      ClientDetail.statusLight.objectAt(0).id,
      node.status,
      'Title includes status light'
    );

Being able to test that with randomness was unreliable and would require the duplication of the compositeStatus computed property in the test. Since there’s another test that specifically sets a node to ineligible and checks for that, it seemed to me safe to remove.

I did make .draining equivalent to .ineligible for the light, but I wonder whether it wouldn’t make sense to use the blue draining colour that shows in the client details boxed section and the state column of the client list? For consistency.

Also, I’ve been surprised that hovering over the status light doesn’t have any description of what the colour means; it’s true that it can usually be decoded by looking for the corresponding colour in the client details boxed section, but maybe a tooltip would be a good idea to be more explicit?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good thoughts on ways to improve that indicator. I know @joshklekamp noodled with it too as part of the drain work (added icons, thought through colors).

I forget if he added a draining state. Either way, worth discussing further but out of scope for this bug fix.

.lookup('service:store')
.peekAll('node')
.findBy('modifyIndex', 4);
readyClient.set('schedulingEligibility', 'ineligible');
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like this, but as you point out there isn't a good way to emulate blocking queries in tests, sooooooooo....

This less-than-elegant inclusion of the warning in the computed
property is necessary because the mixing-in happens as the
application is initialised, so having it outside of the computed
property causes it to unhelpfully with no context.
This seems unhelpful to have printed repeatedly in tests.
These fields only differed by the inclusion of draining,
and the existing compositeStatus was only being used in
one place.

This removes an assertion that was unreliable with
certain random circumstances; it checked that the
composite status was equal to the node status, but that
wasn’t true for draining nodes. It felt okay to remove
because testing a generalised replacement would
necessitate reproducing the entire computed property and
there’s already an assertion that tests the specific
“ineligible” composite status.
@backspace
Copy link
Contributor Author

Thanks for the feedback, @DingoEatingFuzz. I’ve made some followup commits and have a question about tooltips for the status light, let me know what you think?

@backspace backspace merged commit 83d9225 into master Dec 12, 2019
@backspace backspace deleted the b-ui/client-state-sort branch December 12, 2019 19:06
backspace added a commit that referenced this pull request Dec 12, 2019
I unintentionally introduced a flapping test in #6817. The
draining status of the node will be randomly chosen and
that flag takes precedence over eligibility. This forces
the draining flag to be false rather than random so the
test should no longer flap.

See here for an example failure:
https://circleci.com/gh/hashicorp/nomad/26368
backspace added a commit that referenced this pull request Dec 12, 2019
I unintentionally introduced a flapping test in #6817. The
draining status of the node will be randomly chosen and
that flag takes precedence over eligibility. This forces
the draining flag to be false rather than random so the
test should no longer flap.

See here for an example failure:
https://circleci.com/gh/hashicorp/nomad/26368
backspace added a commit that referenced this pull request Dec 13, 2019
I unintentionally introduced a flapping test in #6817. The
draining status of the node will be randomly chosen and
that flag takes precedence over eligibility. This forces
the draining flag to be false rather than random so the
test should no longer flap.

See here for an example failure:
https://circleci.com/gh/hashicorp/nomad/26368
backspace added a commit that referenced this pull request Dec 13, 2019
@github-actions
Copy link

I'm going to lock this pull request because it has been closed for 120 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants