AlertsPreferences.ensure checked for a subscription and then inserted one with no lock. ENSURED was only set afterwards, and a page load fires the home widgets and the bell together.
On kof22 prod, alerts_subscription now holds two identical alerts (bootstrap rule) rows for the owner, created 0.2 s apart. The owner is notified twice for each alert.
alerts_user_preference has a unique user_id, so a racing preference insert throws instead of duplicating.
Fix
First calls are serialized in the JVM (class lock, re-checking ENSURED). After the first call per user this costs nothing.
Replicas can still race, so every first call now runs removeDuplicateBootstrapSubscriptions. It keeps the oldest bootstrap subscription of the user and deletes the others. Subscriptions a person made (any other createdByUserId) are never touched.
The same pass heals existing duplicates, such as kof22's, on the user's next visit after a restart. No data changeset is needed.
Tests
testConcurrentFirstCallsBootstrapOnce: four threads call ensure at once and exactly one subscription and one preference result. Without the fix it fails on the preference unique key.
testDuplicateBootstrapSubscriptionsAreRemoved: two bootstrap rows and one personal row become the oldest bootstrap row plus the personal row. Without the fix it fails.
## Problem
`AlertsPreferences.ensure` checked for a subscription and then inserted one with no lock. `ENSURED` was only set afterwards, and a page load fires the home widgets and the bell together.
On kof22 prod, `alerts_subscription` now holds two identical `alerts (bootstrap rule)` rows for the owner, created 0.2 s apart. The owner is notified twice for each alert.
`alerts_user_preference` has a unique `user_id`, so a racing preference insert throws instead of duplicating.
## Fix
- First calls are serialized in the JVM (class lock, re-checking `ENSURED`). After the first call per user this costs nothing.
- Replicas can still race, so every first call now runs `removeDuplicateBootstrapSubscriptions`. It keeps the oldest bootstrap subscription of the user and deletes the others. Subscriptions a person made (any other `createdByUserId`) are never touched.
- The same pass heals existing duplicates, such as kof22's, on the user's next visit after a restart. No data changeset is needed.
## Tests
- `testConcurrentFirstCallsBootstrapOnce`: four threads call `ensure` at once and exactly one subscription and one preference result. Without the fix it fails on the preference unique key.
- `testDuplicateBootstrapSubscriptionsAreRemoved`: two bootstrap rows and one personal row become the oldest bootstrap row plus the personal row. Without the fix it fails.
- `./mvnw verify` is green locally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
AlertsPreferences.ensurechecked for a subscription and then inserted one with no lock.ENSUREDwas only set afterwards, and a page load fires the home widgets and the bell together.On kof22 prod,
alerts_subscriptionnow holds two identicalalerts (bootstrap rule)rows for the owner, created 0.2 s apart. The owner is notified twice for each alert.alerts_user_preferencehas a uniqueuser_id, so a racing preference insert throws instead of duplicating.Fix
ENSURED). After the first call per user this costs nothing.removeDuplicateBootstrapSubscriptions. It keeps the oldest bootstrap subscription of the user and deletes the others. Subscriptions a person made (any othercreatedByUserId) are never touched.Tests
testConcurrentFirstCallsBootstrapOnce: four threads callensureat once and exactly one subscription and one preference result. Without the fix it fails on the preference unique key.testDuplicateBootstrapSubscriptionsAreRemoved: two bootstrap rows and one personal row become the oldest bootstrap row plus the personal row. Without the fix it fails../mvnw verifyis green locally.🤖 Generated with Claude Code