Skip to content

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.

Saphira Linux dragon mascot

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.

What the wrapper runs
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

Shared DICOM knobs (all classic DICOM checks)
VariableDefaultMeaning
LB_SAPHIRA_DICOM_AETSAPHIRA-HCCalling AE title: who the check introduces itself as
LB_SAPHIRA_DICOM_AECANY-SCPCalled AE title: who it asks for
LB_SAPHIRA_DICOM_PATIENT_IDSAPHIRA-SYNTHETIC-HEALTHCHECKSynthetic query key: never a real patient
LB_SAPHIRA_DICOM_MODALITY-Modality filter (CT, MR…); only sent when configured
LB_SAPHIRA_DICOM_QUERY_LEVELPATIENTQuery level: PATIENT or STUDY
This check's extras
Variable / positionalDefaultMeaning
$6synthetic patient IDPatient ID to query for
$7-Modality filter
$8PATIENTQuery 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
# 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"
Per-install modality settings
# /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

Manual run
/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.

Ask about professional support →