Rebuilding search
Two OpenSearch indexes on a CMS I had just finished moving to Laravel, replacing a search everyone had quietly given up on — built so that losing the cluster costs you results, not the site.
Two OpenSearch indexes on a CMS I had just finished moving to Laravel, replacing a search everyone had quietly given up on — built so that losing the cluster costs you results, not the site.
The platform is a content management system with a long memory — the same one I had spent the previous stretch moving off CodeIgniter onto Laravel, without ever taking it offline. Search was the oldest thing left in it: Zend_Search_Lucene, the pure-PHP port of Lucene that shipped with Zend Framework 1. The instinct was right — it was Lucene — but indexing and querying both ran in interpreted PHP, and the framework carrying it reached end of life in 2016. What that meant in practice was searches that got slower as sites grew, an index expensive enough to rebuild that it was rarely rebuilt, and the everyday failure of typing something you knew was there and not getting it back. Clients had been complaining for years, in the resigned way people complain about weather.
There was a quieter problem nobody had bothered to file: staff could not search the CMS at all. Finding a page, a user, a template, a setting or a report meant already knowing where it lived and clicking your way there.
Public content search and internal CMS search want opposite things: one is anonymous, cacheable and heavily read; the other is permission-bound and touches records the public must never see.
If permissions are applied after the query, a result count alone leaks the existence of records. Gating has to happen inside the query, per requesting user.
Adding a cluster to a working platform means adding a way for the platform to break. Search going down could not be allowed to mean the site going down.
Editors publish a page and immediately search for it. A nightly reindex would have been correct and useless.
Two indexes rather than one index with a visibility flag: the failure mode of a forgotten flag is a leak, and the failure mode of two indexes is a duplicate mapping.
The MySQL fallback path was built early, not bolted on at the end. When OpenSearch is unreachable the search service returns degraded results from the database instead of an exception, worse results, but working.
Syncing off Eloquent model events gives editors the immediacy they expect. But that sync is only ever as reliable as the queue carrying it, so a ten-minute incremental reindex runs regardless and reconciles whatever slipped through. Belt and braces, and cheap.
Health checks and a manual reindex live inside the CMS, so an administrator can see what search is doing and fix it without waiting on an engineer. Gated results are checked by running the same search as each role rather than assumed to be correct.
A long-standing client complaint closed. Administrators and editors got a way to find things across content, users, templates, settings and reports that had never existed on the platform. And the platform gained a dependency it can survive losing.
The mappings, the gating query, the day the fallback earned its keep.