Fix OpenSearch duplicate/multi-generation alias bugs; harden index lifecycle management - #455
Open
P4sca1 wants to merge 11 commits into
Open
Fix OpenSearch duplicate/multi-generation alias bugs; harden index lifecycle management#455P4sca1 wants to merge 11 commits into
P4sca1 wants to merge 11 commits into
Conversation
Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
…indices Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
Member
Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
…ndling Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
…ch prefix Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
Contributor
Author
|
I merged latest main into this branch and made a few more improvements:
|
Resolves conflicts from upstream's execution-code extraction (db-connector.go -> executions.go): reapplies the hot/cold live+archive write/read routing (writeExecutionDocument/getExecutionDocument) on top of upstream's new SetWorkflowExecution/GetWorkflowExecution in executions.go. Fixexecution taken as-is from upstream (our diff there was whitespace-only).
P4sca1
force-pushed
the
opensearch-index-hygiene
branch
from
August 18, 2026 09:27
f3bafc1 to
9538c9a
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
P4sca1
added a commit
to PROCYDE/shuffle-shared
that referenced
this pull request
Aug 18, 2026
Squash-merges github.com/Shuffle/shuffle-shared opensearch-index-hygiene (PR Shuffle#455) into procyde/main.
P4sca1
added a commit
to PROCYDE/shuffle-shared
that referenced
this pull request
Aug 18, 2026
Squash-merges github.com/Shuffle/shuffle-shared opensearch-index-hygiene (PR Shuffle#455) into procyde/main.
Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
Contributor
Author
|
Tried these changes on a live cluster with around 200GB of workflowexecution data. |
# Conflicts: # executions.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
OpenSearch aliases route writes/reads to whichever generation currently owns them, with no cross-generation
_idawareness. Any index that both (a) rolls over and (b) is updated in place by re-writing the same_id(rather than being pure append-only) will eventually split a logical record across two generations, or worse, leave its alias pointing at more than one index - which OpenSearch refuses to run single-document operations against:workflowexecution,notifications, andorg_statisticswere all rollover-enabled and keyed-write stores, and hit exactly this -org_statisticsis a real, observed production error, not a hypothetical.This PR closes that gap for both new writes going forward and for any deployment that already has the problem today, with no manual intervention required. Along the way it also hardens and simplifies the index lifecycle code itself, since a lot of related logic had accumulated as ad-hoc pieces inside
db-connector.go/health.go.What changed
1.
workflowexecution: hot/cold live + archive splitThe highest-volume index gets its own dedicated write path instead of sharing rollover with everything else:
workflowexecution_live- a single, non-rolling index. All in-flight execution updates land here, so in-place_idwrites are always safe.workflowexecution(same name, new role) - becomes the archive: append-only and still rollover-managed. A background sweep moves executions here about an hour after they reach a terminal status, and re-archiving an already-archived id updates the existing copy instead of duplicating it.GetWorkflowExecution,GetAllWorkflowExecutions,GetUnfinishedExecutions, run counts/search, etc.) transparently query both indices and dedupe results.2.
org_statistics/notifications: stop rolling stateful stores, self-heal existing deploymentsThese are keyed, stateful stores, not append logs, and are no longer rollover-enabled - each stays a single backing index going forward. For any deployment that already hit the multi-generation alias problem (or upgraded from a version that rolled these over), a new startup step automatically collapses all existing generations back into one, safely and without data loss, before detaching rollover so it can't happen again. This runs idempotently on every startup.
3. Index lifecycle management: reorganized and hardened
The index/mapping/rollover management code was split out of
db-connector.goandhealth.gointo two dedicated files (opensearch_indices.gofor index/mapping definitions,opensearch_lifecycle.gofor init/rollover/migration logic), and several real bugs found during hardening were fixed along the way:shuffle_shuffle_notificationsbehind) are now detected and cleaned up automatically, scoped safely to the configured index prefix so this can't accidentally touch other tenants' indices on a shared cluster.4. Notification retention (new, opt-in)
An optional background sweep that hard-deletes old, already-read/ignored notifications after a configurable number of days. Fully disabled by default.
Configuration
All new behavior is additive and off-by-default or safely defaulted; nothing here requires action to keep existing behavior. For full details, including OpenSearch permission requirements for the credential Shuffle uses (a few additional cluster-level grants are needed for the new background migration/task-polling behavior), see the docs. Relevant new environment variables:
SHUFFLE_SKIP_OPENSEARCH_INDEX_INITtrueSHUFFLE_SKIP_EXECUTION_LIVE_MIGRATIONworkflowexecution_livetrueSHUFFLE_SKIP_EXECUTION_ARCHIVAL_SWEEPtrueOPENSEARCH_EXECUTION_GRACE_PERIOD1h90mOPENSEARCH_EXECUTION_ARCHIVE_SWEEP_INTERVAL30m15mOPENSEARCH_NOTIFICATION_RETENTION_DAYS0keeps it off0(disabled)30OPENSEARCH_INDEX_ROLLOVER{"max_age":"90d","max_size":"40gb","max_docs":1000000}{"max_age":"30d"}OPENSEARCH_USE_ISM_ROLLOVERtruefalseOPENSEARCH_ISM_POLICY_NAMEshuffle-rollovermy-org-rolloverSHUFFLE_OPENSEARCH_INDEX_PREFIXprodTesting
Verified against a real OpenSearch cluster (fresh install, upgrade-from-existing-data, and multi-generation collision scenarios), including under an OpenSearch security-plugin role scoped to the minimum permissions documented for Shuffle, to confirm all migrations complete correctly and no data is lost or duplicated.