Skip to content

feat: data loader#2237

Merged
andogq merged 2 commits intocanonical:mainfrom
andogq:feat/data-loader
Mar 3, 2026
Merged

feat: data loader#2237
andogq merged 2 commits intocanonical:mainfrom
andogq:feat/data-loader

Conversation

@andogq
Copy link
Member

@andogq andogq commented Feb 26, 2026

Done

  • Create Source<T> definition (WD-33878)
  • Implement createSource<T> to handle basic source house keeping (WD-33879)

@webteam-app
Copy link

@github-actions
Copy link

github-actions bot commented Feb 26, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 97.46% (🎯 95%) 17090 / 17535
🔵 Statements 97.46% (🎯 95%) 17090 / 17535
🔵 Functions 98.16% (🎯 95%) 695 / 708
🔵 Branches 92.06% (🎯 90%) 3386 / 3678
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/data/source.ts 0% 0% 0% 0% 1-70
src/data/sourceBase.ts 100% 97.61% 100% 100%
Generated in workflow #2607 for commit 26b4a28 by the Vitest Coverage Report Action

}

// This load is the latest one to complete successfully.
if (loadId >= latestSuccessfulLoad) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would we ever care about the response if it's not the very latest call? If not could it abort the other calls that are in progress?

Or another way to look at this might be: do we ever want to allow another call to be initiated if there's already a fetch in progress?

Copy link
Member Author

Choose a reason for hiding this comment

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

We mightn't care about the data that's returned, which is why this check is here. This lets us temporarily use data from an old request whilst waiting for the result of a newer request.

The initiator of the load can also pass an AbortSignal to the load function, so they're capable of cancelling their own requests if something superseding it comes in. I don't think it makes much sense for the load to automatically be cancelled if a new one starts, as the thing that triggers a new load has full control over the previous load too.

See https://github.com/canonical/juju-dashboard/pull/2237/changes#diff-c68dd5398fe4e6af6d1b13b0273295f7979c5d6ffc4fbb1d17cc3af184f5ba92R103 for an example.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we've already requested the data is there any need for another call to be made? For example, if we had three components on the page that all needed the same data, so on load they're all going to fire a request for the data and we'd hit the API three times.

Do you see an issue with preventing the second and third requests from firing if the source is already loading?

Copy link
Member Author

Choose a reason for hiding this comment

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

We may want to trigger a new request when there's already one pending.

For example, we have a request pending to list models. While it's loading, we create a new model. In this case, we'd immediately want to start loading a fresh list of models, as the original one could return an outdated result.

It's worth noting that the only way to "trigger a load" from the app is via the invalidate method, but this method doesn't directly trigger the load request (ie. spamming invalidate from a component will not spin up a heap of loads). Instead, invalidate just notifies the source implementation that it should try update the data.

Therefore, this pattern places all of the control into the source implementation. The source is orchestrate concurrent loads as it sees fit, this source base just does the bookkeeping.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah OK, that makes sense. Thanks!

})
.finally(() => {
// Track the latest load to complete, favouring the latest one.
latestCompletedLoad = Math.max(loadId, latestCompletedLoad);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this needs to check if it was aborted as this will always run so an abort would cause this to update the id (if I've read this correctly).

Copy link
Member Author

Choose a reason for hiding this comment

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

You've read it correctly, however we do want to track whenever a load finishes, whether it finishes successfully or not. That's why latestCompletedLoad is tracked in the finally, whilst the latestSuccessfulLoad is only tracked in the then branch.

@andogq andogq force-pushed the feat/data-loader branch from d9de123 to 26b4a28 Compare March 2, 2026 05:14
@andogq andogq marked this pull request as ready for review March 2, 2026 05:17
}

// This load is the latest one to complete successfully.
if (loadId >= latestSuccessfulLoad) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we've already requested the data is there any need for another call to be made? For example, if we had three components on the page that all needed the same data, so on load they're all going to fire a request for the data and we'd hit the API three times.

Do you see an issue with preventing the second and third requests from firing if the source is already loading?

Copy link
Contributor

@huwshimi huwshimi left a comment

Choose a reason for hiding this comment

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

Looks good. Thanks!

@andogq andogq merged commit 854f17a into canonical:main Mar 3, 2026
33 checks passed
@andogq andogq deleted the feat/data-loader branch March 3, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants