Skip to content

adding cache to RoleReadSpec#3584

Open
praffq wants to merge 4 commits intodevelopfrom
prafful/feat/adding-caching-to-role-read-spec
Open

adding cache to RoleReadSpec#3584
praffq wants to merge 4 commits intodevelopfrom
prafful/feat/adding-caching-to-role-read-spec

Conversation

@praffq
Copy link
Copy Markdown
Contributor

@praffq praffq commented Mar 22, 2026

Proposed Changes

  • Adding cache for RoleReadSpec

Merge Checklist

  • Tests added/fixed
  • Update docs in /docs
  • Linting Complete
  • Any other necessary step

Only PR's with test cases included and passing lint and test pipelines will be reviewed

@ohcnetwork/care-backend-maintainers @ohcnetwork/care-backend-admins

Summary by CodeRabbit

  • Refactor
    • Optimized role data retrieval in user organization and facility organization specs for improved performance and consistency.

@praffq praffq requested a review from a team as a code owner March 22, 2026 16:54
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 22, 2026

📝 Walkthrough

Walkthrough

The changes introduce caching for role serialization across multiple resource specifications. RoleReadSpec is now decorated with @cacheable, while FacilityOrganizationUserReadSpec and OrganizationUserReadSpec are updated to retrieve role data from cache via model_from_cache rather than direct serialization.

Changes

Cohort / File(s) Summary
Role Caching Foundation
care/emr/resources/role/spec.py
Added cacheable decorator to RoleReadSpec class; imported cacheable from base resources module.
User Spec Cache Integration
care/emr/resources/facility_organization/facility_organization_user_spec.py, care/emr/resources/organization/organization_user_spec.py
Updated serialization logic in both specs to retrieve role data from cache using model_from_cache(RoleReadSpec, id=obj.role_id) instead of direct serialization; added model_from_cache import to facility spec.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete, missing the 'Associated Issue' section entirely and providing only minimal detail about the proposed changes without explaining the rationale or impact. Add the 'Associated Issue' section with a link to the related issue, and expand 'Proposed Changes' with more detail about why caching was added and what files were modified.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'adding cache to RoleReadSpec' directly and accurately summarizes the main change in the pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch prafful/feat/adding-caching-to-role-read-spec

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 22, 2026

Greptile Summary

This PR adds caching to RoleReadSpec by applying the existing @cacheable decorator pattern (already used by UserSpec) and switching role lookups in OrganizationUserReadSpec and FacilityOrganizationUserReadSpec to use model_from_cache. The approach is consistent with the codebase's established caching infrastructure.

  • @cacheable is added to RoleReadSpec, wiring post_save-based cache invalidation for RoleModel via Django signals.
  • RoleReadSpec.serialize(obj.role).to_json() is replaced with model_from_cache(RoleReadSpec, id=obj.role_id) in both user-read specs.
  • Cache invalidation gap: The cacheable decorator only hooks post_save, not post_delete. A deleted role's cache entry will persist until it naturally expires, causing stale data to be served — this affects all @cacheable models and should be fixed in base.py.
  • FacilityOrganizationUserReadSpec inconsistently still uses UserSpec.serialize(obj.user).to_json() for the user field instead of the cached model_from_cache(UserSpec, id=obj.user_id) pattern.

Confidence Score: 3/5

  • Safe to merge functionally, but the missing post_delete cache invalidation is a correctness concern that could serve deleted-role data.
  • The caching pattern is correctly applied and consistent with existing usage of UserSpec. However, the infrastructure-level gap — post_delete not being handled — means deleted roles can linger in cache and be returned as valid data. This is a pre-existing issue but is worth fixing before or alongside expanding cache usage.
  • care/emr/resources/base.py — the cacheable decorator and delete_model_cache function need a post_delete signal connection to prevent stale data after role deletion.

Important Files Changed

Filename Overview
care/emr/resources/role/spec.py Adds @cacheable decorator to RoleReadSpec. Consistent with the existing pattern used for UserSpec. The decorator wires post_save signal invalidation for RoleModel, but post_delete is not handled — deleted roles can remain in cache.
care/emr/resources/organization/organization_user_spec.py Replaces RoleReadSpec.serialize(obj.role).to_json() with model_from_cache(RoleReadSpec, id=obj.role_id), aligning role lookup with the already-cached user lookup in the same method. Change is correct and consistent.
care/emr/resources/facility_organization/facility_orgnization_user_spec.py Role serialization updated to use model_from_cache, but user serialization still calls UserSpec.serialize(obj.user).to_json() directly instead of model_from_cache(UserSpec, id=obj.user_id), creating an inconsistency with OrganizationUserReadSpec.

Comments Outside Diff (1)

  1. care/emr/resources/base.py, line 293-297 (link)

    P2 Cache not invalidated on role deletion

    The cacheable decorator connects only to the post_save signal. If a RoleModel instance is deleted, its cache entry is never removed, meaning stale (now-deleted) role data could be returned from model_from_cache(RoleReadSpec, id=obj.role_id) until the entry naturally expires.

    The delete_model_cache signal handler should also be wired to post_delete:

    post_save.connect(
        delete_model_cache,
        sender=db_model,
        dispatch_uid=f"delete_model_cache_save:{model_string(db_model)}",
    )
    post_delete.connect(
        delete_model_cache,
        sender=db_model,
        dispatch_uid=f"delete_model_cache_delete:{model_string(db_model)}",
    )

    Note: dispatch_uid values would need to differ between post_save and post_delete connections to avoid Django deduplication removing one of them.

Reviews (1): Last reviewed commit: "adding cache to RoleReadSpec" | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
care/emr/resources/facility_organization/facility_orgnization_user_spec.py (1)

55-56: Caching applied to role but not user — intentional?

The role field now uses model_from_cache, while user on line 55 still uses UserSpec.serialize(obj.user).to_json(). Just checking if this inconsistency is deliberate, or if UserSpec should also be made cacheable for consistency. I'm sure you've thought this through already.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@care/emr/resources/facility_organization/facility_orgnization_user_spec.py`
around lines 55 - 56, The mapping for "role" uses model_from_cache(RoleReadSpec,
id=obj.role_id) but "user" is serialized with
UserSpec.serialize(obj.user).to_json(), causing inconsistency; update the "user"
side to use the same caching pattern by replacing
UserSpec.serialize(obj.user).to_json() with a model_from_cache call (e.g.,
model_from_cache(UserSpec, id=obj.user_id or appropriate identifier) or ensure
UserSpec has a cacheable read spec similar to RoleReadSpec) so both fields use
consistent cached lookup via model_from_cache; locate mapping["user"],
UserSpec.serialize, model_from_cache, RoleReadSpec, obj.user and obj.role_id to
implement this change.
care/emr/resources/organization/organization_user_spec.py (1)

67-73: OrganizationUserExtendedReadSpec still uses direct serialization.

Line 73 uses RoleReadMinimalSpec.serialize(obj.role).to_json() rather than caching. This is presumably intentional since RoleReadMinimalSpec isn't decorated with @cacheable. If you wanted to cache minimal role data too, you'd need to apply @cacheable to RoleReadMinimalSpec as well — but I trust you've decided this isn't worth caching.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@care/emr/resources/organization/organization_user_spec.py` around lines 67 -
73, The perform_extra_serialization method in OrganizationUserExtendedReadSpec
serializes role via RoleReadMinimalSpec.serialize(...).to_json() without using
the cache; to enable caching, decorate RoleReadMinimalSpec with `@cacheable` and
then update perform_extra_serialization to use the same cache-aware
serialization pattern used for OrganizationReadSpec (i.e., call the cached
serializer for obj.role and convert to JSON), so RoleReadMinimalSpec is
cache-decorated and the serialization call in perform_extra_serialization uses
that cached serializer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@care/emr/resources/facility_organization/facility_orgnization_user_spec.py`:
- Around line 55-56: The mapping for "role" uses model_from_cache(RoleReadSpec,
id=obj.role_id) but "user" is serialized with
UserSpec.serialize(obj.user).to_json(), causing inconsistency; update the "user"
side to use the same caching pattern by replacing
UserSpec.serialize(obj.user).to_json() with a model_from_cache call (e.g.,
model_from_cache(UserSpec, id=obj.user_id or appropriate identifier) or ensure
UserSpec has a cacheable read spec similar to RoleReadSpec) so both fields use
consistent cached lookup via model_from_cache; locate mapping["user"],
UserSpec.serialize, model_from_cache, RoleReadSpec, obj.user and obj.role_id to
implement this change.

In `@care/emr/resources/organization/organization_user_spec.py`:
- Around line 67-73: The perform_extra_serialization method in
OrganizationUserExtendedReadSpec serializes role via
RoleReadMinimalSpec.serialize(...).to_json() without using the cache; to enable
caching, decorate RoleReadMinimalSpec with `@cacheable` and then update
perform_extra_serialization to use the same cache-aware serialization pattern
used for OrganizationReadSpec (i.e., call the cached serializer for obj.role and
convert to JSON), so RoleReadMinimalSpec is cache-decorated and the
serialization call in perform_extra_serialization uses that cached serializer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5d9f5bcd-0619-41ae-b17c-9c442a62bc1b

📥 Commits

Reviewing files that changed from the base of the PR and between 1f14f3d and 8ec047c.

📒 Files selected for processing (3)
  • care/emr/resources/facility_organization/facility_orgnization_user_spec.py
  • care/emr/resources/organization/organization_user_spec.py
  • care/emr/resources/role/spec.py

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.19%. Comparing base (2645cb0) to head (c13cd12).

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #3584      +/-   ##
===========================================
- Coverage    77.20%   77.19%   -0.01%     
===========================================
  Files          474      474              
  Lines        22421    22422       +1     
  Branches      2348     2348              
===========================================
- Hits         17310    17309       -1     
- Misses        4531     4532       +1     
- Partials       580      581       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

1 participant