RTFM · Medical healthchecks
lb.saphira.dicom.find: ask a question, get an answer (C-FIND)
An echo proves the door opens; a query proves someone is home. The check runs a real DICOM C-FIND cycle with deliberately synthetic, non-patient keys; asking for a patient that cannot exist is still a full query, and that is the point.
What it proves
The whole query pipeline: association negotiation, the Patient Root query information model, the C-FIND cycle, and the response path back to the requester. Even zero matches is a pass; the question was asked and answered, which is exactly what the check promises. Read-only and continuous-safe.
How it works
findscu (DCMTK) issues a Patient Root query with QueryRetrieveLevel and PatientID keys; synthetic by default, configurable per install for sites whose archive validates key shapes. An optional Modality key and PatientName key ride along only when configured. The query level is strictly validated (PATIENT or STUDY); anything else is exit 2 rather than a malformed query.
findscu -aet "$AET" -aec "$AEC" -ta "$LB_TIMEOUT" -P \
-k "QueryRetrieveLevel=$LEVEL" -k "PatientID=$PATIENT_ID" \
[ -k "Modality=$MODALITY" ] [ -k "PatientName=$NAME" ] \
"$LB_REAL" "${LB_RPORT:-104}"
Configuration
| Variable | Default | Meaning |
|---|---|---|
| LB_SAPHIRA_DICOM_AET | SAPHIRA-HC | Calling AE title: who the check introduces itself as |
| LB_SAPHIRA_DICOM_AEC | ANY-SCP | Called AE title: who it asks for |
| LB_SAPHIRA_DICOM_PATIENT_ID | SAPHIRA-SYNTHETIC-HEALTHCHECK | Synthetic query key: never a real patient |
| LB_SAPHIRA_DICOM_MODALITY | - | Modality filter (CT, MR…); only sent when configured |
| LB_SAPHIRA_DICOM_QUERY_LEVEL | PATIENT | Query level: PATIENT or STUDY |
| Variable / positional | Default | Meaning |
|---|---|---|
| $6 | synthetic patient ID | Patient ID to query for |
| $7 | - | Modality filter |
| $8 | PATIENT | Query level: PATIENT or STUDY (else exit 2) |
| LB_SAPHIRA_DICOM_FIND_NAME | - | Optional PatientName key |
Modality-specific per-install settings are the reason this check exists in this shape: /etc/saphira/lb-healthchecks.d/dicom.find.conf for the estate, dicom.find/<service>.conf for one VIP, and neither HAProxy nor ldirectord needs any new interface for it.
Wiring it up
# ldirectord.cf
virtual = 21
protocol = fwm
scheduler = rr
real = 192.168.20.21:104 gate 1
checktype = external
checkcommand = "/var/lib/lb/saphira/lb.saphira.dicom.find"
# /etc/saphira/lb-healthchecks.d/dicom.find.conf
LB_SAPHIRA_DICOM_PATIENT_ID=SAPHIRA-SYNTHETIC-HEALTHCHECK
LB_SAPHIRA_DICOM_MODALITY=CT
LB_SAPHIRA_DICOM_QUERY_LEVEL=PATIENT
LB_SAPHIRA_DICOM_AET=SAPHIRA-HC
LB_SAPHIRA_DICOM_AEC=MODALITY-SCP
Run it by hand
/var/lib/lb/saphira/lb.saphira.dicom.find ct-pool 104 192.168.20.21 104 SAPHIRA-SYNTHETIC-HEALTHCHECK CT PATIENT
echo $?
# 0 = query cycle completed (zero matches still passes)
# 1 = association or query path failed
# 2 = invalid query level
Failure modes
- Association opens, query fails; the archive's query model or key validation changed: the deepest honest signal short of storing data
- Timeout mid-cycle: the database behind the archive is straining: C-ECHO still passes here, which is why this check exists
Did we miss something?
If this page left something unanswered, found an error, or there is another subject you would like documented, tell us. Saphira’s documentation grows from real problems people need to solve.
Send feedback or request a new section →
Prefer not to do it yourself?
Everything needed to do the work yourself is documented here and remains free; we charge for human time, not for withholding knowledge. Sometimes the missing resource is simply time. The same people who build Saphira can provide paid professional help with implementation, migration, troubleshooting and administration.