Skip to content

ci: Run the sunspot and sunspot_rails specs on Solr 9.10.1 - #6

Merged
njakobsen merged 15 commits into
masterfrom
ci-production-solr
Oct 21, 2026
Merged

njakobsen merged 15 commits into
masterfrom
ci-production-solr

Conversation

@njakobsen

@njakobsen njakobsen commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Runs the sunspot and sunspot_rails specs on Solr 9.10.1, the version the fork's users run in production, as well as on the Solr 5.3.1 that sunspot_solr bundles. sunspot_solr's own specs run only on the bundled Solr.

  • ci/solr9 is sunspot_solr's configset ported to Solr 9: point fields with docValues, LatLonPointSpatialField, the removed schema elements taken out, luceneMatchVersion 9.0.0, and /select defaulting to mm=100% and the original highlighter.
  • A solr matrix dimension, bundled and 9.10.1, sets SOLR_IMAGE, which makes ci/sunspot_test_script.sh run that Docker image with the configset. That doubles the sunspot and sunspot_rails jobs. The script exits, with the container's log, if the container doesn't start within 120 seconds or its core can't be created.
  • index_time_boosts setting: Solr 7 removed index-time boosts, and Solr 9 rejects a JSON update that carries one (400 unknown field 'boost'), which failed 180 examples in every sunspot, json job. Sunspot.config.solr.index_time_boosts, or index_time_boosts under solr: in sunspot.yml, defaults to true, so nothing changes unless it's set. Set to false, Sunspot sends no field or document boosts, child documents included, in index, batch and atomic updates. It's read on every update, so changing it after the first index takes effect. The integration specs set it from the Solr they run against.
  • Join fix: Solr 7 dropped schema defaultOperator, so a join's type clause became optional and a join matched a container through a document of any type. The type clause is now required (+).
  • Skipped from a Solr major with skip_from_solr: index-time document and field boosts (removed in 7), facet.date (removed by 6), BM25 relative scores, and one boosted fulltext ranking (scoped_search_spec.rb:596) that fails on Solr 6.6.6 and 9.10.1 for a reason not yet found.
  • README: documents the setting, and adds an "Upgrading to Solr 9" section: the schema port, index-time boosts, mm, BM25 scores, time range facets, and _root_ for nested documents.

Testing

Locally, in both update formats:

sunspot integration sunspot_rails (Rails 7.0)
Solr 9.10.1 (ci/solr9) 408 examples, 0 failures, 4 pending 181 examples, 0 failures
Solr 5.3.1 (bundled) 408 examples, 0 failures, 2 pending 181 examples, 0 failures

These ran before the review fixes (2f879292, 16e91114) and the upstream sync. Since then, on #7, which contains all of this branch, the sunspot integration specs pass on both Solrs in both formats (the field boost example is now pending on 9.10.1), and sunspot_rails passes on both.

Without the indexer change, the JSON run on Solr 9.10.1 fails 171 examples with unknown field 'boost'. The new API specs fail without it too. The API specs have the same 12 local-only failures as master.

Merge order

This has #8 (upstream master) merged in. Merge that first, then this with a merge commit, not a squash, so #7, stacked on this, retargets cleanly.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY

sampokuokkanen and others added 3 commits September 9, 2026 21:56
ActiveSupport passes quirks_mode to JSON.generate and JSON.parse. json 3
made unknown options an ArgumentError, so Sunspot.remove_all! in
spec_helper blows up and every sunspot_rails spec fails with it.

Rails 8 dropped quirks_mode, but the Appraisals file stops at Rails 7.0, so
nothing in the matrix can run on json 3. Pinning it as a development
dependency, next to the psych pin that is already there for the same kind
of reason.
…type on Solr 7 and later

Root cause: `Sunspot::Query::Join#to_subquery` built `_query_:"{!field f=type}Photo"+_query_:"{!edismax …}keywords"`, marking only the keywords clause required. Solr 5's schema `defaultOperator="AND"` made the type clause required too, but Solr 7 removed that setting and the default operator is OR, so the type clause became optional. A join then matched a container through a document of any type with matching keywords: on Solr 9.10.1 `spec/integration/join_spec.rb` finds a `PhotoContainer` by a `Picture`'s description.

The type clause is now prefixed with `+` as well. Both join specs pass on Solr 6.6.6 and 9.10.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
Issue: CI ran the specs on the Solr 5.3.1 that `sunspot_solr` bundles, while the fork's users run Solr 9.10.1 in production. Behaviour that only newer Solr has, such as the `{!bool}` parser, couldn't be tested, and behaviour newer Solr changed went unnoticed.

With `SOLR_IMAGE` set, `ci/sunspot_test_script.sh` runs that Docker image with the configset in `ci/solr9`, and the workflow sets it to `solr:9.10.1` for the `sunspot` and `sunspot_rails` jobs. `sunspot_solr`'s own specs still test the Solr it bundles.

`ci/solr9` is `sunspot_solr`'s configset ported to Solr 9: point fields with docValues in place of trie fields, `LatLonPointSpatialField` in place of `LatLonType`, and the schema elements Solr 9 removed (`StandardFilterFactory`, `defaultSearchField`, `solrQueryParser`) taken out. In `solrconfig.xml`, `luceneMatchVersion` is 9.0.0, and `/select` defaults to `mm=100%`, which edismax defaulted to before Solr 7, and to the original highlighter, which Sunspot's `hl.simple.*` parameters are for.

Four integration specs depend on behaviour newer Solr changed, and `skip_from_solr` skips them there: index-time document boosts (removed in Solr 7), `facet.date` (removed by Solr 6), relative scores of term and range boost queries (BM25, the default similarity from `luceneMatchVersion` 6; the spec passes under `ClassicSimilarity`), and one ranking of boosted fulltext, which fails on Solr 6.6.6 and 9.10.1 under either similarity for a reason not yet found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
njakobsen and others added 7 commits October 20, 2026 01:43
…e can't be created

Root cause: `start_solr_container` runs under `set +e` and waited for Solr with an unbounded `until curl` loop. When `docker run` failed, for example on an image pull error or a Docker Hub rate limit, the job spun until the workflow's 60-minute timeout. When copying the configset or creating the core failed, it still printed "done.", and the run showed up only as every spec failing.

`docker run` failing now exits the script, the wait gives up after 120 tries and prints the container's log, and a failed `mkdir`, `docker cp`, `chown` or core `CREATE` prints the log and exits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
The quirks_mode incompatibility depends on the Rails version, so pin
json next to the other version-specific gems instead of in the gemspec,
and correct the comment: 8.0.x still passes quirks_mode; only 7.2.4+
and 8.1+ don't.
Pin `json` below 3 for the sunspot_rails suite
Root cause: Sunspot sends a field's `:boost` with its value, and with the JSON update format RSolr writes it as `"title_text":{"boost":2,"value":"pizza"}`. Solr 7 removed index-time boosts, and Solr 9 reads that object as a field named `boost`, rejecting the whole update with `400 unknown field 'boost'`. XML updates carry the boost as an attribute, which Solr 9 ignores, so only the `sunspot, json` CI jobs on Solr 9.10.1 failed: 180 integration examples.

`Sunspot.config.solr.index_time_boosts`, and `index_time_boosts` under `solr:` in `sunspot.yml`, defaults to true, which keeps what Sunspot sends unchanged for everyone. Set to false, the indexer removes field and document boosts from every document it sends, child documents included. The integration specs set it from the Solr they run against: false on Solr 7 and later. The README explains the setting, and gains an "Upgrading to Solr 9" section listing what changes for Sunspot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
…s well as 9.10.1

Issue: the `sunspot` and `sunspot_rails` jobs ran only on Solr 9.10.1, so nothing tested the gem on the Solr 5.3.1 that `sunspot_solr` bundles, which upstream's CI tests against.

Instead of choosing the Solr by gem, the matrix has a `solr` dimension, `bundled` and `9.10.1`, and `SOLR_IMAGE` is set from it. `sunspot_solr` runs only on `bundled`, since its specs test the Solr it bundles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
ContextBoundDelegate tries the receiver first and falls back to the calling
context in a rescue, so every DSL call that belongs to the calling context
raises a NoMethodError whose backtrace is then thrown away.

Raising captures the whole stack at roughly 3 objects per frame, and a
Rails request is already ~182 frames deep inside a search block, so each
raise costs ~600 objects. In our app a block calling a few model helpers
raised 70 times per request; dropping those took the request from 288,801
to 246,807 objects allocated, 14.6% less.

The fast path uses respond_to? to dispatch straight to the calling context.
Where respond_to? can't answer, method_missing without respond_to_missing?
being the obvious case, it falls through to the old path.
The respond_to? fast path let the calling context win over receivers
that handle calls in method_missing without respond_to_missing?, such
as DSL::Function (function { sum(...) } in a model class method) and
DSL::Fields (double inside an RSpec example). It also called
respond_to? on BasicObject receivers that don't have it.

Only take the fast path when the receiver has Kernel, doesn't respond
to the method and uses the default method_missing, so it could only
have raised. Add specs for each case, and only count raises on the
test thread.
@njakobsen
njakobsen marked this pull request as ready for review October 21, 2026 07:06
njakobsen and others added 5 commits October 21, 2026 00:08
…or-in-dsl-dispatch

Avoid NoMethodError when resolving DSL calls
…g it after the first index takes effect

Root cause: `Session#indexer` copied `config.solr.index_time_boosts` into the `Indexer` when it built it, on the first index. Setting it to false after that, as an app that indexes during boot before an initializer runs would, left boosts on for the life of the session (found in review).

Instead of copying the value, the indexer now keeps the session's config and reads the setting in `add_documents`. New API specs cover turning the setting off after indexing, a `batch`, and an atomic update, which sends a field's boost like a full update. All three, and the existing two, fail without the stripping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
…skip the field boost spec from Solr 7

The README, the setting's rdoc and the spec helper said Solr 9 rejects a JSON update carrying any index-time boost. On Solr 9.10.1 a document boost in a JSON update is accepted and ignored; only a field boost gets `400 unknown field 'boost'` (found in review).

The "with field boost" integration spec kept passing on Solr 9, where no field boost is sent, so it no longer tested the boost there. It's now skipped from Solr 7, as the document boost spec beside it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
Brings in sunspot#1057, which avoids a `NoMethodError` when the DSL resolves a call against a receiver, and sunspot#1058, which pins `json` below 3 in the `sunspot_rails` appraisals, ahead of an upstream gem release.

Upstream's json pin supersedes the fork's own from #5, which pinned only the Rails 6.1 and 7.0 appraisals, so the fork's line is dropped and `sunspot_rails/Appraisals` now equals upstream's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
@njakobsen njakobsen mentioned this pull request Oct 21, 2026
4 tasks done
@njakobsen
njakobsen merged commit 8e7288c into master Oct 21, 2026
93 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants