Skip to content

refactor: centralize user preference caching in the model layer#74

Merged
andrey-canon merged 2 commits into
open-release/teak.nelpfrom
and/test
May 15, 2026
Merged

refactor: centralize user preference caching in the model layer#74
andrey-canon merged 2 commits into
open-release/teak.nelpfrom
and/test

Conversation

@andrey-canon

@andrey-canon andrey-canon commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Pull Request: Refactor UserPreference Caching and Reduce Query Counts

Summary

This PR refactors the caching mechanism for user preferences. It migrates the caching responsibility from the API layer (api.py) and the request-scoped memory (@request_cached in models.py) to the Model layer itself using Django's standard caching framework via helper functions. This centralization improves cache utilization across the platform and results in a verifiable reduction in database queries.

To support this architectural change without breaking query-count tests in environments that default to DummyCache (like the LMS test suite), LocMemCache overrides were added to the affected test cases.

Key Changes

1. Architectural Refactoring (Caching)

  • Moved Cache Logic to Helpers: Extracted cache key generation, get, set, and invalidate methods from api.py and moved them to openedx/core/djangoapps/user_api/helpers.py.
  • Model Layer Caching: Removed the @request_cached() decorator from UserPreference.get_all_preferences() in models.py. The caching logic is now explicitly handled inside this method using the new helper functions.
  • API Simplification: Cleaned up openedx/core/djangoapps/user_api/preferences/api.py. Methods like has_user_preference, get_user_preference, and get_user_preferences now directly delegate to UserPreference.get_all_preferences(), relying on the model to handle the cache Hit/Miss logic.

2. Query Count Optimizations & Test Adjustments

  • Taxonomy Tags Tests: Reduced expected query counts by 1 across multiple tests in content_tagging/rest_api/v1/tests/test_views.py (e.g., test_list_taxonomy_query_count, test_object_tags_query_count, test_taxonomy_tags_query_count) due to the improved global caching of preferences.
  • Language Preference Middleware Tests: Reduced expected queries in lang_pref/tests/test_middleware.py (test_preference_update_noop) from 1 to 0 and 3 to 2.
  • Test Environment Overrides: Added @override_settings(CACHES={'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}) to IndexQueryTestCase (LMS) and test_preference_update_noop (Middleware). Because the code no longer relies on the request_cached thread-local memory, these tests require a functional cache backend (LocMemCache instead of the test environment's default DummyCache) to correctly assert the optimized query counts.

Impact

  • Centralized and more predictable caching behavior for user preferences.
  • Net reduction in database queries for preference lookups.
  • More resilient tests that explicitly declare their cache backend dependencies.

@andrey-canon andrey-canon force-pushed the and/test branch 22 times, most recently from fc4edbc to 27b8cda Compare May 13, 2026 18:04
@andrey-canon andrey-canon changed the title feat: testing unit tests refactor: centralize user preference caching in the model layer May 13, 2026
@andrey-canon andrey-canon force-pushed the and/test branch 7 times, most recently from 0b35779 to d84da9d Compare May 14, 2026 15:21
@andrey-canon andrey-canon force-pushed the and/test branch 5 times, most recently from 2add834 to 8619abf Compare May 14, 2026 17:08
Refactored caching logic by moving cache management from the API
layer to the `UserPreference` model. This change ensures that
`get_all_preferences` handles its own caching consistently,
preventing stale data issues previously caused by the
`@request_cached` decorator and manual API-level caching.

Changes:
- Moved cache helpers to `user_api/helpers.py`.
- Updated `UserPreference.get_all_preferences` to use the new
  caching logic.
- Cleaned up `user_api/preferences/api.py` to remove redundant
  cache checks, delegating to the model instead.
- Ensured cache invalidation is triggered correctly on
  create/update/delete operations.

@johanseto johanseto left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets move it to upstream. This reduce number of queries to the db with the cache management



def user_preferences_cache_key(user_id):
return f"{USER_PREFERENCE_CACHE_KEY_PREFIX}.{user_id}"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstringsini

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Added comprehensive Google-style docstrings to cache key generation,
retrieval, storage, and invalidation functions. Improved code
maintainability and clarified expected parameter types.
@andrey-canon andrey-canon merged commit d5dada3 into open-release/teak.nelp May 15, 2026
47 checks passed
@andrey-canon andrey-canon temporarily deployed to open-release/teak.nelp May 15, 2026 17:39 — with GitHub Actions Inactive
@andrey-canon andrey-canon temporarily deployed to open-release/teak.nelp May 15, 2026 17:39 — with GitHub Actions Inactive
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