Remove the orphaned v5/v6/v7 search indexes - #3524
Conversation
|
Claude finished @jonathangreen's task in 3m 34s —— View job SummaryNicely scoped change — the new Details
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## chore/remove-old-search-schema-revisions #3524 +/- ##
=========================================================================
Coverage 93.50% 93.51%
=========================================================================
Files 509 509
Lines 46663 46685 +22
Branches 6378 6382 +4
=========================================================================
+ Hits 43633 43656 +23
+ Misses 1959 1958 -1
Partials 1071 1071 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
e3fd60f to
e5d49cb
Compare
Greptile SummaryThis PR adds
Confidence Score: 5/5Safe to merge — the alias guard reliably blocks deletion of any live index, and the startup runner correctly retries on failure. The alias-safety guard in remove_search_indices is sound: it queries both read and write pointers before touching any index, so it cannot accidentally remove the live index. index_remove uses ignore=[404] to handle concurrent deletes without spurious retries. The startup runner's try/except means any OpenSearch outage at boot simply defers the task to the next startup without affecting other tasks. No logic issues were found across the four changed files. Files Needing Attention: No files require special attention.
|
| Filename | Overview |
|---|---|
| src/palace/manager/search/service.py | Adds abstract index_remove(name) -> bool to SearchService and its implementation in SearchServiceOpensearch1; adds module-level remove_search_indices helper with alias-safety guard. Logic is correct: exists() + delete(ignore=[404]) handles the TOCTOU window; the protected-set construction correctly prevents deletion of any index an alias still targets. |
| startup_tasks/2026_06_30_remove_old_search_indexes.py | One-shot startup task that calls remove_search_indices inline for v5-v7. Exceptions propagate to the startup runner which leaves the task unrecorded and retries on the next boot — consistent with the framework's error-handling contract. |
| tests/manager/search/test_service.py | Adds test_index_remove for the new method and TestRemoveSearchIndices with three cases (happy path, alias guard, no-op). All use a live OpenSearch fixture, providing real integration coverage. |
| tests/mocks/search.py | Adds _created_indices tracking to SearchServiceFake so index_remove can correctly return True/False; base_revision_name returns the same base_name used in index_create, so the fake is internally consistent with remove_search_indices. |
Sequence Diagram
sequenceDiagram
participant SR as StartupRunner
participant ST as StartupTask (run)
participant RSI as remove_search_indices
participant SVC as SearchServiceOpensearch1
participant OS as OpenSearch
SR->>ST: run(services, session, log)
ST->>RSI: remove_search_indices(service, [5,6,7], log)
RSI->>SVC: read_pointer()
SVC->>OS: get_alias(read)
OS-->>SVC: alias → index name
RSI->>SVC: write_pointer()
SVC->>OS: get_alias(write)
OS-->>SVC: alias → index name
note over RSI: Build protected set (live index names)
loop for each version in [5,6,7]
RSI->>SVC: index_remove(name)
SVC->>OS: indices.exists(name)
OS-->>SVC: True / False
alt exists and not protected
SVC->>OS: "indices.delete(name, ignore=[404])"
OS-->>SVC: OK
SVC-->>RSI: True
else
SVC-->>RSI: False
end
end
RSI-->>ST: removed names
ST-->>SR: None
SR->>SR: record task as DONE
Reviews (5): Last reviewed commit: "Address review feedback on the search in..." | Re-trigger Greptile
|
This is ready for review, but I'll keep it in draft until its ready to be merged |
1558880 to
2b54d93
Compare
Reindex migrations re-point the search read/write aliases at each new index
but never delete the index they replaced, so the retired v5/v6/v7 revisions
leave orphaned circulation-works-v{5,6,7} indexes behind in the cluster. No
existing code ever removes them.
This adds a SearchService.index_remove(name) primitive and a remove_search_indices
helper that deletes the named old versions (skipping any index a read or write
alias still points at, so it can't drop the live index). A one-shot startup task
calls the helper inline for v5/v6/v7; removing a handful of indexes is a fast
metadata operation that needs no background task.
Pass ignore=[404] to indices.delete so the small exists()/delete() race in index_remove resolves as a no-op instead of a spurious NotFoundError. Note in the startup task why the cleanup runs inline rather than via a Celery task, and drop the remove-me TODO: completed startup tasks are recorded and never re-run, and old task files are cleaned up periodically.
dedf921 to
ca017c8
Compare
Description
Stacked on top of #3523. Cleans up the orphaned
circulation-works-v5,-v6and-v7OpenSearch indexes that earlier schema revisions left behind in the cluster.When the search schema migrates to a new revision, the reindex flow creates the new index and re-points the read/write aliases at it, but it never deletes the index it replaced — so every retired revision leaves an orphaned
{base}-v{n}index sitting in the cluster, consuming shards and disk. Nothing in the codebase has ever removed them. #3523 removes the v5–v7 revision code; this PR removes the leftover indexes.This adds:
SearchService.index_remove(name)— deletes an index if it exists and reports whether it did. The service had no delete-index capability before.remove_search_indices(service, versions, *, log)helper — deletes the named old versions, with a safety guard that never deletes an index a read or write alias still points at, so it cannot drop the live index even if asked to. It's idempotent (delete-if-exists) and returns the names it removed.startup_tasks/2026_06_30_remove_old_search_indexes.py) that calls the helper inline for versions[5, 6, 7]on the next boot.Removing a handful of indexes is a fast metadata operation, so this runs inline in the startup task rather than via a background job. If OpenSearch is briefly unavailable the helper raises, the startup runner declines to record the task as done, and it simply retries on the next boot.
Motivation and Context
Removing the v5–v7 revision modules (#3523) stops the code from referencing those schemas, but the actual indexes remain in the cluster forever because the migration flow only swaps aliases and never drops the old index. This reclaims that cluster state (shards, disk) as the final step of retiring those revisions.
How Has This Been Tested?
New tests, all passing under the docker tox environment:
tests/manager/search/test_service.py::TestService::test_index_remove—index_removedeletes a real index and is a no-op (returnsFalse) when it's absent.tests/manager/search/test_service.py::TestRemoveSearchIndices— three cases against the real OpenSearch fixture: the happy path (old indexes removed, the live index preserved), the alias guard (an index a read alias points at is skipped), and a no-op when the indexes don't exist.Also ran the startup-task and initialization suites to confirm the new task file is discovered and the boot wiring is unaffected.
Checklist