-
Notifications
You must be signed in to change notification settings - Fork 231
feat(compass-indexes): add index build progress COMPASS-9495 #7200
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds index build progress tracking to the MongoDB Compass indexes table, showing a percentage indicator when indexes are being built. The implementation introduces a new hook for polling index progress via currentOp
command and updates the UI to display build progress and a spinner for in-progress indexes.
- Adds
useIndexProgress
hook for tracking index build progress via polling - Updates Redux state to track progress percentage for in-progress indexes
- Modifies UI components to display progress indicators and spinners for building indexes
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
test/setup-store.ts |
Adds mock currentOp method to test data service |
src/stores/store.ts |
Extends data service type to include currentOp for progress tracking |
src/modules/regular-indexes.ts |
Adds progress tracking logic, new actions, and getIndexesProgress thunk |
src/modules/regular-indexes.spec.ts |
Adds tests for index progress tracking functionality |
src/hooks/use-index-progress.ts |
New custom hook for managing index build progress polling |
src/components/regular-indexes-table/regular-indexes-table.tsx |
Integrates progress hook and updates index filtering logic |
src/components/regular-indexes-table/regular-indexes-table.spec.tsx |
Updates tests to mock the progress hook |
src/components/regular-indexes-table/regular-index-actions.tsx |
Adds progress display UI with spinner and percentage |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx
Outdated
Show resolved
Hide resolved
); | ||
|
||
const progress = index.buildProgress * 100; | ||
const isBuilding = progress > 0 && progress < 100; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I follow this logic, we either have these in "in-progress" bucket or in "regular", how are we expecting this UI to show up for both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps I've over engineered a bit, and I can confirm this again. IIUC the inprogress state is a "fake" one to make the UI show the user's index in the table right away after they create it. It changes to "ready" as soon as we see the index in a listIndexes response. I think the progress logic is left over here from when I would show users 0% when we had a true 0 value. I think I can remove this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, your understanding is totally right here, although I guess now that we actually include in progress in "real" ones, the "in progress" name doesn't make much sense anymore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't had a chance to track this down, but I'm definitely seeing some weird behaviors locally: if I start the index creation (so it first gets into the "in progress" bucket in the state) I never see the progress shown for it, it just shows "in progress" badge until the index finished building fully, if I look at the indexes for the same collection in a separate tab (so the index is fetched from the beginning, not created as a virtual "in progress" one) it correctly shows the building progress, but the badge shows ready. Screenshots below show how this looks, notice that title_1
is in progress but without the progress in one tab, and ready but with progress in another


if (buildProgress > 0 && buildProgress < 1) { | ||
return ( | ||
<div className={buildProgressStyles} data-testid="index-building-spinner"> | ||
<Body>Building... {(buildProgress * 100) | 0}%</Body> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: we're not using much of bitwise operators in the codebase, can we use truncate here instead for readability?
<Body>Building... {(buildProgress * 100) | 0}%</Body> | |
<Body>Building… {Math.trunc(buildProgress * 100)}%</Body> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
.filter((index) => !rollingIndexNames.has(index.name)) | ||
.filter((index) => !inProgressIndexNames.has(index.name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super nit, to avoid two passes
.filter((index) => !rollingIndexNames.has(index.name)) | |
.filter((index) => !inProgressIndexNames.has(index.name)) | |
.filter((index) => !rollingIndexNames.has(index.name) && !inProgressIndexNames.has(index.name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More than a nit to me, good catch.
Someday we'll use the new Iterator to stack complex set of filters and maps perhaps...
); | ||
|
||
const progress = index.buildProgress * 100; | ||
const isBuilding = progress > 0 && progress < 100; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, your understanding is totally right here, although I guess now that we actually include in progress in "real" ones, the "in progress" name doesn't make much sense anymore
expect(() => screen.getByTestId('index-building-spinner')).to.throw; | ||
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to be called to validate
expect(() => screen.getByTestId('index-building-spinner')).to.throw; | |
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw; | |
expect(() => screen.getByTestId('index-building-spinner')).to.throw(); | |
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed, dunno how I missed this. On the node team I recall we either had a lint rule or we monkey-patched this assertion to require it has an argument because you could start throwing something else and not realize.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, do you mind digging up what ruleset that was if you have time for this? I just did a quick search through the codebase and seems like we have a bunch of those uncalled ones, I didn't realize that this is easy to miss... 😬 "Worst" case we can probably just add our own custom rule for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it, we modified the chai assertion: https://github.com/mongodb/js-bson/blob/d32397730426cc8423b89ddc1dd20ef4180f1677/test/register-bson.js#L14-L41
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which I realize won't help bc you won't hit this unless you call it, I think I fixed those mistakes with search and replace back when I introduced this. No "throw;" was pretty easy to eliminate 😅
I am now seeing it display the build percentage correctly by undoing the filtering changes. This does mean the index now has a "ready" badge while there's a "building... x%" string right next to it. Not ideal 🤔 We also have a "building" state but for rolling indexes only, I'm not sure whats the best approach here because it seems like it would be a heavy lift to reframe the statuses from how they are currently used since they're bound to how the UI treats them (like how in-progress is really a dummy state until we get a ping from the server) Any thoughts on the best investments we can make here? Could the status badge also rely on |
Badge can depend on progress, this is probably the easiest way to deal with this quickly and we can do this if we want to resolve this ticket sooner rather than later. Just generally I think the problem here is that your change changes the definition of in progress completely for compass but we're not adjusting for that in a more general sense: what compass was calling in progress before is not in progress anymore, so if we're changing that, it would also make sense to either remove this confusion from the codebase or treat the raw-ish output of data service in a way that maps to this concept properly. Your changes are somewhere in between right now, we're getting the indexes as part of what we now incorrectly call "ready", then we update the in progress ones to get the build progress, but at the same time we're still rendering the "ready" ones, not the "in progress" ones. So either we change the code to make it clear that in progress is not in progress and ready is not always ready, or when receiveing data from data service we continue to map in progress to in progress group without mixing it with the ready one. If the ui wasn't so tied to the state, we'd probably have easier time going the first route (which is also preferable I'd say), but as that's not the case, we can probably try the latter and separate data service returned indexes before they end up in plugin state |
expect(() => screen.getByTestId('index-building-spinner')).to.throw( | ||
/Unable to find/ | ||
); | ||
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Free ellipsis to copy if you like: …
(it does look a small bit nicer than three separate dots)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! ty
I have started a new PR on top of this one to refactor the index status handling to be more accurate to reality. There is now a Maybe we can land this one as is, or if we're happy with the changes in that PR we can bundle them into one, I just didn't want to cloud the important bits of this work with refactorings. It seems to work pretty well! ⬇️ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still showing "Ready" while building:

This is confusing behavior so let's address that at least before this is considered done. This change is not behind a feature flag so whatever lands in main should not look broken / half-finished to the end users. Compass main branch should always be releaseable
return { ...index, compassIndexType: 'regular-index' }; | ||
}); | ||
|
||
// For in-progress indexes, merge in buildProgress from regular indexes if available |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't do anything at the moment, as soon as index shows up in regular, it's removed from in progress, why are we keeping this code here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gribnoysup Sorry this took so long to get back to! badge set correctly and unneeded in-progress merge logic removed.
I'll rebase the follow up now too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One switch we have to make I think, but otherwise looks good. Thanks for your patience with all the back and forth, maybe next time let's just fine some time to brainstorm together 😄
<StatusField | ||
status={ | ||
index.buildProgress > 0 && index.buildProgress < 1 | ||
? 'building' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let switch this to 'inprogress' instead of building
:
? 'building' | |
? 'inprogress' |
otherwise we are showing a tooltip that is saying that this is a rolling build.

I think "Building" was taken by Compass from DE where building only refered to rolling builds specifically, otherwise we use in progress for indexes that are being created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done! Yea did not grasp the complexity of the tech debt originally. We made it there though, lessons learned!
Description
Add a percentage to the index build row in the table if it is in progress.
https://jira.mongodb.org/browse/COMPASS-9495
Checklist
Motivation and Context
Open Questions
For reviewers:
useIndexProgress
?getInProgressIndexInfo
used to just list stub indexes because even in progress ones would be listed as "ready" while they continued to build on the server. We now will list "real" indexes via this function. Is that the right approach?Dependents
Types of changes
Screen.Recording.2025-08-15.at.2.29.25.PM.mov
index_finish.mov