Two of the central pillars of the new UI are incremental updates and fine-grained subscriptions.
By reducing the about of data we send to the UI we reduce the load on the browser enabling us to do more with the UI and to make it more responsive.
We have three data stores, at the Scheduler, the UIS and the UI:
All three represent exactly the same data model. The latter two must be kept in-sync with the former.
A subscription look something like this:
subscription {
a {
b
c {
d
e
}
f
}
}
Deltas look something like this:
{
b: 1,
}
{
b: 1,
c {
d: 2
}
}
A subscription is a set
, a delta is a dict
which is a subset of the subscription.
The UI consists of different Vues which have their own subscriptions.
For example a simple Tree Vue might have the following subscription:
subscription {
workflows(ids: [<id>]) {
taskProxies {
name
status
firstParent {
id
}
}
familyProxies {
id
name
firstParent {
}
}
}
}
Whereas an un-grouped Graph Vue might have this subscription:
subscription {
workflows(ids: [<id>]) {
taskProxies {
name
id
status
}
nodesEdges {
nodes {
id
}
edges {
source
target
}
}
}
}
These subscriptions are different but they have a large overlap. If we were to register both subscriptions with the UIS we would end up with two similar but different data stores in the UI. This would be in-efficient and opens up discontinuity issues.
Users may want to open many similar but different views on the same workflow so we need to be able to handle this in an elegant manner.
One solution to this problem is to “merge” subscriptions together.
Subscriptions are effectively “sets” of data fields, two subscriptions can be “merged” by taking their union. For example the union of the previous two subscriptions is this:
subscription {
workflows(ids: [<id>]) {
taskProxies {
name
id
status
firstParent {
id
}
}
familyProxies {
id
name
firstParent {
}
}
nodesEdges {
nodes {
id
}
edges {
source
target
}
}
}
}
As an added bonus we can query the UI to find out exactly what it is subscribing to and exactly which view has asked for what data making optimisation/debugging much nicer.
When events occur the Scheduler sends “deltas” containing only the changed information.
For example if a job has changed state from submitted to running we might expect a delta of the form:
taskProxies {
id: <id>,
status: "running"
jobs {
id: <id>,
status: "running"
}
}
When subscriptions are registered with the UIS they are stored locally in a mapping so we can associate subscriptions with open websockets.
When deltas arrive from the Scheduler we loop through the subscriptions and take the intersection of the delta and subscription fields, if there is an overlap, this is the delta we send to the UI.
For example using the previous delta and subscription:
Delta | Subscription | Intersection | |
Example 1 |
workflows(ids: [ |