Skip to content

fix: Fix race conditions in repository refresh and Space field visibility#375

Merged
tkuhn merged 1 commit intomasterfrom
fix/374-race-condition-maintained-resources
Mar 5, 2026
Merged

fix: Fix race conditions in repository refresh and Space field visibility#375
tkuhn merged 1 commit intomasterfrom
fix/374-race-condition-maintained-resources

Conversation

@tkuhn
Copy link
Contributor

@tkuhn tkuhn commented Mar 5, 2026

Summary

Fixes two related race conditions that caused maintained resources to fail loading on live instances:

  • Repository refresh race condition: In MaintainedResourceRepository and SpaceRepository, the volatile sentinel field (resourceList/spaceList) was assigned at the start of refresh(), before the lookup maps were populated. Concurrent readers could see the non-null sentinel, skip ensureLoaded(), and then access null/empty/partially-populated maps — causing NPEs or missing data. Fixed by building all data structures with local variables and assigning the volatile field last, establishing a happens-before guarantee for all prior writes. Also added double-check in ensureLoaded() to prevent redundant refreshes, and made SpaceRepository.runRootUpdateAfter volatile.

  • Space field visibility: Space.dataInitialized and Space.dataNeedsUpdate were non-volatile shadow fields written by background threads and read by the AjaxLazyLoadPanel polling thread without synchronization. Under the Java Memory Model, the polling thread could cache the stale false value indefinitely, causing isDataInitialized() to never return true. This was previously masked by the first bug (pages would crash before reaching the polling stage). Fixed by making both fields volatile.

Test plan

  • Verify maintained resource pages load correctly (e.g. /resource?id=https://w3id.org/fair/fip/terms/FIP-Ontology)
  • Verify space pages load correctly under concurrent access
  • Verify no "stuck" loading spinners on initial page loads

Closes #374

🤖 Generated with Claude Code

…lity

In MaintainedResourceRepository and SpaceRepository, the volatile
sentinel field (resourceList/spaceList) was set at the start of
refresh(), making partially populated maps visible to concurrent
readers. Build all data structures with local variables and assign the
volatile field last, establishing a happens-before guarantee. Add
double-check in ensureLoaded() to prevent redundant refreshes. Make
SpaceRepository.runRootUpdateAfter volatile for cross-thread visibility.

In Space, the shadow fields dataInitialized and dataNeedsUpdate were
non-volatile, so writes from background update threads had no
happens-before relationship with reads in the AjaxLazyLoadPanel polling
thread, potentially causing isDataInitialized() to never return true.

Closes #374

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tkuhn tkuhn merged commit 68d7731 into master Mar 5, 2026
8 checks passed
@tkuhn tkuhn deleted the fix/374-race-condition-maintained-resources branch March 5, 2026 19:45
@github-actions
Copy link

github-actions bot commented Mar 5, 2026

🎉 This PR is included in version 4.15.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maintained resources don't load anymore on live instances

2 participants