Skip to content

fix(ui): converge Archive badge count after an async hard delete - #35446

Merged
Rohit0301 merged 6 commits into
open-metadata:mainfrom
Rohit0301:fix/context-center-archive-badge-async-delete
Oct 16, 2026
Merged

Rohit0301 merged 6 commits into
open-metadata:mainfrom
Rohit0301:fix/context-center-archive-badge-async-delete

Conversation

@Rohit0301

@Rohit0301 Rohit0301 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Describe your changes:

I fixed the Context Center Archive sidebar badge going stale after a document is hard-deleted from the Archive page.

A drive file's hard delete answers 202 Accepted and removes the row on a background thread (ContextFileResource.delete → deleteByIdAsync). ContextCenterArchivePage invalidated the archive-count query the moment that request resolved, so the refetch raced the background deletion:

  • background delete wins the race → count comes back decremented → fine.
  • refetch wins → React Query caches the pre-delete total, and nothing invalidates the key again (staleTime: 0, but no further trigger exists). The badge stays one too high for the rest of the session, until a page reload.

The page's own list was never affected, because it optimistically splices the row out and decrements its local total — only the server-backed sidebar badge went stale.

Replaced the single fire-and-forget invalidation with reconcileCountAfterAsyncDelete: drop the count optimistically (matching what the page already does to its list), then refetch until the server agrees or the attempts run out (5 × 1s).

Two details worth a reviewer's eye:

  • It uses refetchQueries, not invalidateQueries. Invalidation only marks an inactive query stale without fetching it, so the reconcile would silently no-op whenever the sidebar observing that key is unmounted — exactly the staleness this is meant to prevent.
  • The call is not awaited. The row is already gone from the list, and holding isDeleting through the poll would spin the modal's delete button for seconds.

Type of change:

  • Bug fix

High-level design:

N/A — small change.

Tests:

Use cases covered

  • Admin hard-deletes a document from the Context Center Archive page; the Archive sidebar badge decrements without requiring a page reload.
  • Soft-delete → restore of a document (both synchronous) continues to move the Documents/Archive counts as before.

Unit tests

  • I added unit tests for the new/changed logic.
  • Files added/updated: openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.test.ts

Seven cases on reconcileCountAfterAsyncDelete: optimistic drop applied synchronously; keeps polling while the server still reports the pre-delete total; stops at the first refetch once the server agrees; terminates after the configured attempts when the deletion never lands; keeps the corrected figure when the budget runs out; does not let a concurrent delete chain off a polluted cache value; no-ops when nothing is cached.

Test Suites: 2 passed, 2 total
Tests:       9 passed, 9 total

(7 new + the 2 existing ContextCenterArchivePage tests.)

Backend integration tests

  • Not applicable (no backend API changes).

Ingestion integration tests

  • Not applicable (no ingestion changes).

Playwright (UI) tests

  • Not applicable (no new spec added — this fixes a product bug that existing specs cover).

Run locally with --retries=0, so a retry-pass could not mask the flakiness being fixed.

playwright/e2e/Features/ContextCenterArchive.spec.ts — 3 workers, --retries=0

Running 6 tests using 3 workers
[3/6] [chromium] › ContextCenterArchive.spec.ts:68:7  › Archive Page › full document lifecycle: folder expand icon, upload, delete, restore, and permanent delete
[4/6] [chromium] › ContextCenterArchive.spec.ts:357:7 › Folder Delete › file in deleted folder is absent from search and not added to archive
[5/6] [chromium] › ContextCenterArchive.spec.ts:492:7 › Archive Page Lazy Loading › archive page lazy-loads more rows on scroll within its own scroll container
  6 passed (1.7m)

68:7 exercises the permanent-delete path this PR changes.

Downstream check — Collate AskCollateContextCenter.spec.ts, 20 repeats

The Collate spec Count updates › Archive: hard-deleting an archived document updates Archive count is the test that surfaced this bug; it was intermittently failing with the badge stuck at its pre-delete value.

npx playwright test --project=askcollate -g "Archive: hard-deleting an archived document updates Archive count" --repeat-each=20 --retries=0 --workers=1

Running 21 tests using 1 worker
  ✓   1 [setup] › src/openmetadata-ui/playwright/e2e/auth.setup.ts:121:6 › authenticate all users (26.4s)
  ✓   2 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (9.2s)
  ✓   3 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.8s)
  ✓   4 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.9s)
  ✓   5 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (8.5s)
  ✓   6 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (9.9s)
  ✓   7 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (9.2s)
  ✓   8 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (9.0s)
  ✓   9 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (11.2s)
  ✓  10 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.1s)
  ✓  11 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (9.4s)
  ✓  12 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.0s)
  ✓  13 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (8.8s)
  ✓  14 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (9.6s)
  ✓  15 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.7s)
  ✓  16 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.5s)
  ✓  17 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.8s)
  ✓  18 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.6s)
  ✓  19 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.8s)
  ✓  20 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.2s)
  ✓  21 [askcollate] › Archive: hard-deleting an archived document updates Archive count @askcollate (10.4s)
  21 passed (4.2m)

--retries=0, so a retry-pass cannot mask the flakiness being fixed. Run at 1 worker because this box carries a VM at ~59% CPU (load avg ~42); at 3 workers Chromium intermittently fails to start (browserType.launch: Timeout 180000ms exceeded), producing failures unrelated to the code under test.

Before the fix, that spec failed with:

Expected: < 7
Received:   7

— the badge never moving, with the assertion polling a value that could no longer change.

Manual testing performed

  1. Ran the Playwright suite above against a local stack, plus the Collate downstream spec 20× against a local Collate stack.
  2. Confirmed the Archive badge now decrements after a hard delete without a reload.

UI screen recording:

N/A — cache-reconciliation fix with no visual change; the badge simply reaches the correct value without a reload.

🤖 Generated with Claude Code

@github-actions github-actions Bot added the UI UI specific issues label Oct 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Comment thread openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts Outdated
Comment thread openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts Outdated
@Rohit0301 Rohit0301 self-assigned this Oct 14, 2026
@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Oct 14, 2026
@Rohit0301
Rohit0301 marked this pull request as ready for review October 14, 2026 12:38
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@Rohit0301
Rohit0301 force-pushed the fix/context-center-archive-badge-async-delete branch from 2bed353 to 4a34fec Compare October 14, 2026 12:41
@github-actions github-actions Bot removed the safe to test Add this label to run secure Github workflows on PRs label Oct 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Comment thread openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts Outdated
@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Oct 14, 2026
@Rohit0301
Rohit0301 force-pushed the fix/context-center-archive-badge-async-delete branch from 4a34fec to 8fccd0c Compare October 14, 2026 12:48
@github-actions github-actions Bot removed the safe to test Add this label to run secure Github workflows on PRs label Oct 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Comment thread openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts Outdated
@harsh-vador

Copy link
Copy Markdown
Contributor

I checked this against the failing merge-queue run on Collate (run 38528815785), and the traces match your explanation exactly.

In both attempts of Archive: hard-deleting an archived document updates Archive count:

Attempt 0 Attempt 1
DELETE …?hardDelete=true returns 202 08:17:28.122 08:18:07.678
Archive count refetch (include=deleted) 73 ms later, total 2 23 ms later, total 2
Any count request after that during the 30s poll none none

So the one refetch happened before the background delete finished, and nothing ever asked the server again. The fix targets the right thing. 👍

A few thoughts, none of them blocking:

  1. There may be a simpler, event-driven option. The backend already announces when an async delete finishes: deleteByIdAsync calls WebsocketNotificationHandler.sendDeleteOperationCompleteNotification(jobId, …). AsyncDeleteProvider already listens on deleteEntityChannel, but it only keeps a callback for failures (onDeleteFailure). If we add an onDeleteComplete next to it, the Archive page could invalidate the count when the delete actually finishes, instead of polling 5×1s with the module-level reconcileStates map. Your ponytail: comment already points at this, so it could just be a follow-up.
  2. The final clamp can hide things. When the attempts run out we write min(server, target) into the cache. That means the test passes even if the hard delete never actually happens on the backend. In normal use, the badge can also read lower than the server's real number (for example, if someone else archives a file in the meantime) until the next refetch. Probably fine for a badge, but worth knowing.
  3. A heads-up from the same run: the Memories count test flaked for a different reason. Other tests running in parallel created memories in the middle of it (the server total went 2 → 4), so after its own delete the count was 3 and < 2 could never pass. That one isn't a UI bug, and this PR doesn't need to handle it. I've left a note on the Collate side (#7485).

@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 77%
77.68% (125977/162170) 64.32% (79693/123883) 65.52% (26573/40552)

@Rohit0301

Copy link
Copy Markdown
Contributor Author

Thanks for checking it against the run — good to have the traces confirm it.

On (1): I traced the socket path and it holds up. ContextFileResource.delete goes through deleteByIdAsync, which fires sendDeleteOperationCompleteNotification on success, and both the 202 body and the socket message already carry the jobId — so no backend change is needed. The only gap is on our side: deleteDriveFile is typed Promise<void> and throws the response away. Adding onDeleteComplete beside onDeleteFailure and having the Archive page invalidate on it would be roughly +18/−317 (the whole reconcileCountAfterAsyncDelete helper and its tests go away), and it drops the clamp you flagged in (2) along with them.

One real caveat before we swap: the socket event only arrives if the socket is connected. If it drops mid-delete, the badge stays stale with no poll to catch it — strictly worse than today's bounded poll on that one path. So the event-driven version probably wants a single delayed refetch as a backstop, which is still a fraction of the current code.

Happy to do it as the follow-up the ponytail: comment points at, unless you'd rather it land here.

A drive file's hard delete answers 202 Accepted and removes the row on a
background thread. ContextCenterArchivePage invalidated the archive-count
query the moment that request resolved, so the refetch raced the background
deletion. When the refetch won, React Query cached the pre-delete total and
nothing invalidated the key again, leaving the sidebar badge one too high for
the rest of the session.

Replace the single fire-and-forget invalidation with reconcileCountAfterAsyncDelete:
drop the count optimistically (matching what the page already does to its own
list), then refetch until the server agrees or the attempts run out (5 x 1s).

Uses refetchQueries rather than invalidateQueries — invalidation only marks an
*inactive* query stale without fetching it, so the reconcile would silently
no-op whenever the sidebar observing the key is unmounted.

On timeout the corrected figure is written back: the last refetch leaves the
server's pre-delete total in the cache, and keeping that would restore the
staleness this fixes. Deletions still in flight are counted per key so a
concurrent delete derives its target from the server's number rather than a
cached value a sibling's refetch may have just overwritten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts Outdated
@Rohit0301
Rohit0301 force-pushed the fix/context-center-archive-badge-async-delete branch from 8fccd0c to bcc904f Compare October 15, 2026 05:39
@github-actions github-actions Bot removed the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
… as landed

The absence probe called getContextFileById without an include param, but
GET /contextCenter/drive/files/{id} defaults to include=non-deleted. Every row
on the Archive page is already soft-deleted (the list is loaded with
include=deleted), so the probe 404'd before the background hard delete even
started. The rejection handler mapped any error to "landed", so the first poll
ended the wait: the race this fixes was still there, only pushed back ~1s.

Let getContextFileById take an include argument and pass Include.All, so the
probe resolves true only once the row is genuinely purged. Narrow the predicate
to a 404 as well, leaving a transient error to the next attempt rather than
ending the poll early.

The helper's unit tests stub hasLanded, so neither gap was visible to them:
cover the include wiring in a new assetAPI test and the transient-error case
in the helper's own suite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot removed the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
@github-actions github-actions Bot removed the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
@github-actions github-actions Bot removed the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
Sonar flagged the unawaited reconcileCountAfterAsyncDelete call. The float is
deliberate — awaiting it would hold `isDeleting` through the ~5s poll and spin
the modal button — so `void` says so explicitly. Nothing escapes: the probe
handles both branches and refetchQueries resolves rather than throws.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot removed the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@gitar-bot

gitar-bot Bot commented Oct 15, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 7 closed / 7 findings

🟡 Medium risk · Adding bounded polling before refreshing the Archive badge changes UI cache behavior; slow deletes or failed probes could leave its count temporarily stale.

Fixes the Context Center Archive badge going stale after an async hard delete by reconciling the count optimistically and polling until the server agrees. Resolved this session: the poll window and timeout behavior, retry budget recording, back-to-back delete total computation, in-flight deletion accounting, baseline target handling across restore and soft-delete, and the absence probe now including deleted rows. No open issues found.

✅ 7 closed
✅ Bug: Poll window is 2s, not 5×1s, and a timeout puts the stale count back

📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:60-63 📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:76-88
The description says the helper polls "5 × 1s", but the defaults are attempts = 5, intervalMs = 400, which gives about 2s. Each refetchQueries writes the server value into the cache. If the background deleteByIdAsync takes longer than the window, the last refetch replaces the optimistic expectedTotal with the pre-delete total, and nothing triggers another fetch. The badge then stays one too high, which is the bug this PR is meant to fix. Either make the default match the stated budget (intervalMs = 1000), or, when attempts run out without the server agreeing, put expectedTotal back (or mark the query stale) so the optimistic value is not lost.

✅ Edge Case: Back-to-back hard deletes compute expectedTotal from a stale refetch

📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:65-74 📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:84-87 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterArchivePage/ContextCenterArchivePage.tsx:229-232
Each call takes current from the cache and subtracts 1. Suppose a second hard delete runs while the first call is still polling, after one of its refetches has written the stale pre-delete total (say 5) back into the cache. The second call then computes expectedTotal = 4, not 3. Both loops stop as soon as the server reports 4, which happens once only the first deletion has landed. The badge is then stuck at 4 while the real total is 3. To fix this, track pending deletions per key (for example a module-level counter subtracted from the refetched value), or keep polling until the refetched value stops changing.

✅ Quality: Unrecorded retry budget for async-delete badge reconciliation

📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:52-63
This PR sets a new retry budget: attempts = 5, intervalMs = 400. The description calls it a choice ("refetch until the server agrees or the attempts run out (5 × 1s)"), and the doc comment marks bounded polling as a stopgap until it is replaced by the DELETE_ENTITY_CHANNEL socket event. Nothing in docs/decisions/ covers it. A record would say: "Context Center badge counts after a 202 async hard delete are reconciled optimistically and then by a bounded refetch poll of N attempts × M ms; switch to the DELETE_ENTITY_CHANNEL socket event once a second surface needs the same polling." It should also settle whether the interval is 400ms or 1s, since the code and the description disagree.

✅ Bug: peakInFlight over-subtracts deletions the server has already applied

📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:89-96 📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:124 📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:136-150
correctedTotal subtracts every in-flight (and peak) deletion from the server's number, so it assumes none of them has landed yet. That breaks once one of them has. Example: two hard deletes from a cached 5, so the optimistic targets are 4 and 3. If the server then reports 4, the first call sees 4 <= 4 and returns. The second call sees 4 > 3 and writes 4 - peakInFlight(2) = 2. If the second background delete takes longer than the ~5s budget, the timeout branch (L155-159) keeps that 2 for the rest of the session, one below the real count. That is the same permanent-staleness problem this helper exists to fix, just in the other direction, and the loaded-box case the ADR describes makes it likely.

The test only covers a server stuck at 5, so the partial-landing case is never exercised. A simpler fix is to make the target absolute: record the pre-delete baseline from the first concurrent call (e.g. baseline - totalDeletesStarted per key) and write min(serverTotal, target) instead of subtracting a running count. Then deletions that have already landed are not subtracted a second time.

✅ Bug: Finished deletes leave started while baseline stays, so later targets run high

📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:112-119 📄 openmetadata-ui/src/main/resources/ui/src/utils/ContextCenterQueryKeys.ts:150-158
In the finally block, started goes down by one whenever a reconcile finishes, but baseline stays at the pre-delete count. After that, a delete that joins the existing state gets a target that ignores the deletion that already finished.

Example: cache is 10. Delete A starts (started=1, target 9). Delete B starts (started=2, target 8). A's deletion lands and the server reports 9, so A returns and finally sets started back to 1. Delete C now starts: started=2, so target = 10-2 = 8, but three deletes have been accepted and the right target is 7. B lands, the server reports 8, and both B and C exit early because 8 <= 8. The cache is left at 8. When C's background delete lands, the badge is one too high for the rest of the session, which is the bug this PR is meant to fix. The optimistic setQueryData(8) for C also doesn't move the badge at all.

The existing tests start every call together and finish them together, so this staggered case isn't covered.

Fix: count accepted deletes and in-flight polls separately. started should only ever go up, and the state should be cleared only when the in-flight count reaches zero. Add a test where one call finishes before another one starts.

...and 2 more closed from earlier reviews

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Oct 15, 2026
@sonarqubecloud

Copy link
Copy Markdown

@Rohit0301
Rohit0301 added this pull request to the merge queue Oct 15, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — failed_checks (2026-10-15T20:15:56Z)

Blocked the queue: playwright-summary

@Rohit0301
Rohit0301 added this pull request to the merge queue Oct 16, 2026
Merged via the queue into open-metadata:main with commit 34594ee Oct 16, 2026
120 of 127 checks passed
@Rohit0301
Rohit0301 deleted the fix/context-center-archive-badge-async-delete branch October 16, 2026 08:32
shah-harshit pushed a commit that referenced this pull request Oct 16, 2026
)

* fix(ui): converge Archive badge count after an async hard delete

A drive file's hard delete answers 202 Accepted and removes the row on a
background thread. ContextCenterArchivePage invalidated the archive-count
query the moment that request resolved, so the refetch raced the background
deletion. When the refetch won, React Query cached the pre-delete total and
nothing invalidated the key again, leaving the sidebar badge one too high for
the rest of the session.

Replace the single fire-and-forget invalidation with reconcileCountAfterAsyncDelete:
drop the count optimistically (matching what the page already does to its own
list), then refetch until the server agrees or the attempts run out (5 x 1s).

Uses refetchQueries rather than invalidateQueries — invalidation only marks an
*inactive* query stale without fetching it, so the reconcile would silently
no-op whenever the sidebar observing the key is unmounted.

On timeout the corrected figure is written back: the last refetch leaves the
server's pre-delete total in the cache, and keeping that would restore the
staleness this fixes. Deletions still in flight are counted per key so a
concurrent delete derives its target from the server's number rather than a
cached value a sibling's refetch may have just overwritten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): poll the deleted row's absence to converge the Archive badge

The reconcile targeted `baseline - started`, a count frozen when the first
delete started. Anything else moving the archive count inside the ~5s window
threw it off, and the page's own restore handler invalidates that very key: a
restore mid-window let the poll see its target early and exit, leaving the
badge one too high once the background delete landed — the bug this helper
exists to fix. A soft-delete in the window broke it the other way, with
`clampedTotal` capping the badge one too low.

Use the deleted entity's own absence as the completion signal instead: poll
`getContextFileById` until it stops resolving, then refetch once and keep
whatever the server reports. No precomputed target, so a concurrent restore or
soft-delete cannot skew it, and the baseline/in-flight bookkeeping and the
clamp all go away with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): probe the archive delete with Include.All and only treat 404 as landed

The absence probe called getContextFileById without an include param, but
GET /contextCenter/drive/files/{id} defaults to include=non-deleted. Every row
on the Archive page is already soft-deleted (the list is loaded with
include=deleted), so the probe 404'd before the background hard delete even
started. The rejection handler mapped any error to "landed", so the first poll
ended the wait: the race this fixes was still there, only pushed back ~1s.

Let getContextFileById take an include argument and pass Include.All, so the
probe resolves true only once the row is genuinely purged. Narrow the predicate
to a 404 as well, leaving a transient error to the next attempt rather than
ending the poll early.

The helper's unit tests stub hasLanded, so neither gap was visible to them:
cover the include wiring in a new assetAPI test and the transient-error case
in the helper's own suite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* lint fix

* fix(ui): mark the archive reconcile float with void

Sonar flagged the unawaited reconcileCountAfterAsyncDelete call. The float is
deliberate — awaiting it would hold `isDeleting` through the ~5s poll and spin
the modal button — so `void` says so explicitly. Nothing escapes: the probe
handles both branches and refetchQueries resolves rather than throws.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants