fix: serve patient panel photos from the database instead of per-row FHIR calls - #411
Open
canvas-investigator[bot] wants to merge 1 commit into
Open
fix: serve patient panel photos from the database instead of per-row FHIR calls#411canvas-investigator[bot] wants to merge 1 commit into
canvas-investigator[bot] wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
KOALA-6442
🧵 Jessica Herbert cc @JessicaHerbert
Problem
The patient panel rendered one
<img>per patient row pointing at the app's ownGET /app/<patient_id>/photoendpoint. Each request made synchronous HTTPS calls back into the same instance — firstPOST /auth/token/(SDKHttp, 30s timeout), then a fumageGET /Patient/<id>. On a large page that's ~40–50 concurrent self-referential requests competing for the very web-worker pool that must serve them. The pool gets exhausted, a worker crashes (exited with code 1), and HAProxy returns 502 for the whole instance (photos,/auth/token/,/healthcheck,/patient/<id>,/graphql).The previous mitigation (in-memory token cache + a 4s photo-fetch timeout) was insufficient: the token cache is a per-process class dict that cold-starts empty after every plugin-runner restart, so a concurrent burst still stampedes
/auth/token/— and the short timeout was applied only to the fumage GET, never to the token POST, which kept the 30s timeout and drove aReadTimeoutdeadlock.Fix
1. Serve photos from the database — no self-HTTP. Patient photos are now read via
patient.photo_url(the SDK's presigned URL over thephotosrelation, tablecanvas_sdk_data_api_patientphoto_001) — exactly how this plugin already reads staff photos.photosis prefetched on the table queryset (ordered so.first()stays on the prefetch cache — no N+1) and the URL is resolved inservices/serialization.py. This deletes the/<patient_id>/photoendpoint,_get_fhir_token,_fetch_patient_photo_data,_ShortTimeoutHttp, andservices/fhir_photo.py— removing 100% of the plugin's self-referential HTTP. Photos now load from S3/CloudFront, never the instance web tier.2. Debounce the stats recompute. Every note/task/address/patient/protocol write triggered a full 6-query single-patient recompute, and one save cascades several events — each rerunning the whole thing.
handlers/panel_stats_sync.pynow coalesces per-patient recomputes within a short window (cache failures degrade to "recompute anyway", never "skip"). The 15-minute reconcile cron remains the correctness backstop.Scope notes
<img>already carriesloading="lazy"(static/table.html), so lazy-loading needed no change;PAGE_SIZEremains a per-instance secret for operators who want a smaller default page.FHIR_CLIENT_ID/FHIR_CLIENT_SECRET/CANVAS_INSTANCE_URLsecrets from the manifest and README; bumpedplugin_versionto2.1.0.Testing
Added/updated pytest coverage —
_patient_photo_url(property read + error fallback), the serialization default-avatar assertion, and a stats-handler debounce test; removed the obsolete FHIR-token and photo-endpoint tests. I could not execute the DB-backed suite in this environment (it needs the canvas-plugins pytest-django + Postgres harness); the tests follow the repo's existing factory /django_dbpatterns and should run in CI. All changed files passpy_compileand the manifest is valid JSON.Generated by the Investigator