Fix notification read and dismiss under Mongoid 9 - #909
Merged
Conversation
This was referenced Sep 9, 2026
suttondemlong
force-pushed
the
fix/notifications-mongoid-9-regression
branch
from
September 9, 2026 00:15
6457c02 to
4093e1f
Compare
`NotificationsController#update` and `#destroy` raise on every request:
Mongoid::Errors::InvalidQuery: Expression must be a Hash:
#<ActionController::Parameters {"notificateable_id"=>...}>
`notification_params` returns ActionController::Parameters, which is not a Hash, and
Mongoid 9 requires a query expression to be one. Mongoid 8 accepted it. This is a
regression from the Mongoid 8 -> 9 upgrade in #892, already on master, so marking
notifications as read and dismissing them are both broken right now. Fixed by calling
`to_h`, which is safe because the parameters have been through `permit`.
I audited the rest of app/ and lib/ for the same shape. Every other case is either
ActiveRecord or ActiveModel, which still accept permitted Parameters, or a plain Hash
built by hand -- `ReactionsController#reaction_params` is the latter. This was the only
Mongoid one.
Two N+1s went with it, both of which made the index action untestable under
`Bullet.raise`:
- `after_initialize :set_defaults` recomputed `post_id` by reaching through
`notificateable` on *every* instantiation, including records read back from the
database that already had the value stored. Now guarded on `post_id.blank?`, which
still backfills documents written before the field existed while costing nothing for
the rest.
- The index aggregation titles each group through `notificateable`, so the query now
eager loads it. Mongoid 9 does handle `includes` for this polymorphic belongs_to;
I checked before relying on it.
469 examples, 0 failures.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All three actions, including the grouping that collapses repeated notifications on one subject into a single entry with a count. This is what surfaced both the Mongoid 9 regression and the N+1s fixed in the preceding commit -- update and destroy raised on the first request a spec ever made to them. 469 examples, 0 failures. app/ and lib/ coverage 87.85% -> 88.94%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
suttondemlong
force-pushed
the
fix/notifications-mongoid-9-regression
branch
from
September 9, 2026 01:09
4093e1f to
b86b093
Compare
CoryMCodes
approved these changes
Sep 9, 2026
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.
Fixes #908.
Marking a notification as read and dismissing one both raise on every request on
mastertoday. This is a regression from the Mongoid 8 → 9 upgrade in #892, not something waiting on the rest of the upgrade work, so it is worth taking on its own rather than behind anything else.Two commits: the fix, then the spec that guards it.
The regression
NotificationsController#notification_paramsreturnsActionController::Parameters, andupdateanddestroyfeed it straight intoNotification.where(...).Parametershas not been aHashsince Rails 5; Mongoid 8 accepted it, Mongoid 9 requires a real Hash and raises:In production
ExceptionLogger'srescue_from "Exception"turns that into a 422 quoting a Mongoid internal error, which is likely why it has read as noise rather than as two broken endpoints.indexis unaffected — it builds its own criteria.The fix is
to_h, which is safe because the parameters have already been throughpermit.I audited
app/andlib/for the same shape and this is the only Mongoid case. The others are Active Record or Active Model, which still accept permittedParameters, or plain hashes built by hand —ReactionsController#reaction_paramsis the latter.Two N+1s in the same controller
Not scope creep:
config/environments/test.rbsetsBullet.raise = true, so these made the endpoint untestable. Any spec touchingindexdied onUnoptimizedQueryErrorbefore reaching an assertion. Fixing them is what allowed the regression spec to exist at all.Notification'safter_initialize :set_defaultsrecomputedpost_idby reaching throughnotificateableon every instantiation — including records read back from the database that already had the value stored. One extra query per notification loaded, anywhere in the app, to recompute something it was already holding. Now guarded onpost_id.blank?, which still backfills documents written before the field existed while costing nothing for the rest.indexaggregation titles each group throughnotificateable, so the query now eager loads it. Mongoid 9 does supportincludesfor this polymorphicbelongs_to; I checked before relying on it rather than assuming.Verification
master, plus the 7 new ones),standardrbcleanMongoid::Errors::InvalidQueryonupdateanddestroy,Bullet::Notification::UnoptimizedQueryErroronindexmaster, so it carries none of the in-flight upgrade workNote on overlap
These same two commits also appear on the branch for #912, which is where they were found. They are cherry-picked here so this can merge first without waiting on that much larger review. Once this lands, #912 rebases and git drops the duplicates automatically.
Please rebase-and-merge or use a real merge commit — not squash, so the fix stays separable from its spec.
🤖 Generated with Claude Code