openedx / openedx/openedx-platform

production.py unconditionally overwrites an explicitly configured SEARCH_ENGINE, making non-Elasticsearch backends unreachable

Open
#38,991 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
8.2k
Forks
4.4k
Avg merge
6d 18h
Merged PRs (30d)
42

Description

Summary

lms/envs/production.py and cms/envs/production.py overwrite SEARCH_ENGINE with
search.elastic.ElasticSearchEngine whenever certain feature flags are on — even when the
operator has explicitly set SEARCH_ENGINE to something else in LMS_CFG / CMS_CFG. This
makes the documented Typesense backend (and Meilisearch, and any custom engine) unreachable on
any deployment that has courseware search enabled.

Affected versions

Present and identical on all three branches checked on 2026-08-14:

  • master @ 0c243f5a7
  • release/verawood @ 259473c5d
  • release/ulmo
Details

CMScms/envs/production.py (master :283-285, verawood :286-288):

if ENABLE_COURSEWARE_INDEX or ENABLE_LIBRARY_INDEX:
    # Use ElasticSearch for the search engine
    SEARCH_ENGINE = "search.elastic.ElasticSearchEngine"

LMSlms/envs/production.py (master :322-330, verawood :338-346):

if (
   ENABLE_COURSEWARE_SEARCH or
   ENABLE_DASHBOARD_SEARCH or
   ENABLE_COURSE_DISCOVERY or
   ENABLE_TEAMS
   ):
    # Use ElasticSearch as the search engine herein
    SEARCH_ENGINE = "search.elastic.ElasticSearchEngine"

Both run after the YAML config has been applied to the module namespace
(vars().update(...) over _YAML_TOKENS, cms/envs/production.py:70), so an explicitly
configured SEARCH_ENGINE is silently discarded.

The FeaturesProxy interaction makes this hard to work around

Since FEATURES = FeaturesProxy(globals()) (cms/envs/production.py:35,
lms/envs/production.py:39), and FeaturesProxy.__setitem__ writes straight into the
settings module's namespace:

def __setitem__(self, key, value):
    warnings.warn(...)
    self.ns[key] = value

...the YAML FEATURES ingest loop (cms/envs/production.py:154-155) writes those keys as
module globals. So setting ENABLE_COURSEWARE_INDEX under FEATURES: in CMS_CFG is
indistinguishable from setting the top-level name, and there is no way to enable courseware
indexing without also triggering the SEARCH_ENGINE override.

Minimal reproduction using the real proxy class:

from openedx.core.lib.features_setting_proxy import FeaturesProxy

ns = {'ENABLE_COURSEWARE_INDEX': False, 'ENABLE_LIBRARY_INDEX': False,
      'SEARCH_ENGINE': 'search.typesense.TypesenseEngine'}
FEATURES = FeaturesProxy(ns)

for feature, value in {'ENABLE_COURSEWARE_INDEX': True}.items():   # production.py:154-155
    FEATURES[feature] = value

if ns['ENABLE_COURSEWARE_INDEX'] or ns['ENABLE_LIBRARY_INDEX']:    # production.py:283
    ns['SEARCH_ENGINE'] = 'search.elastic.ElasticSearchEngine'

print(ns['SEARCH_ENGINE'])   # search.elastic.ElasticSearchEngine
Impact

An operator following
Use Typesense search backend
and setting SEARCH_ENGINE: search.typesense.TypesenseEngine in LMS_CFG/CMS_CFG will find
that:

  • the LMS reads from Elasticsearch as soon as any of ENABLE_COURSEWARE_SEARCH,
    ENABLE_DASHBOARD_SEARCH, ENABLE_COURSE_DISCOVERY or ENABLE_TEAMS is on — i.e. in
    virtually every real deployment;
  • the CMS writes to Elasticsearch as soon as courseware indexing is enabled.

There is no warning or log line; the configured value is simply gone.

Suggested fix

Only apply the Elasticsearch default when SEARCH_ENGINE was not explicitly configured, e.g.

if (ENABLE_COURSEWARE_INDEX or ENABLE_LIBRARY_INDEX) and 'SEARCH_ENGINE' not in _YAML_TOKENS:
    SEARCH_ENGINE = "search.elastic.ElasticSearchEngine"

or drop the override entirely and let SEARCH_ENGINE default in common.py, which would be
consistent with how other backends are selected.

I'm happy to open a PR for whichever shape maintainers prefer.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the SEARCH_ENGINE override blocks in lms/envs/production.py and cms/envs/production.py, then read the YAML token handling and the default in common.py. Verify how FeaturesProxy and _YAML_TOKENS affect explicit configuration. Done means an explicitly configured engine remains selected while the Elasticsearch default still applies when no engine was configured.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, search
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.