Thread Safety
Write workflow scripts that remain safe under concurrent processing.
Processing hooks may be invoked concurrently for different messages. Treat fields on the active script generation as shared state and keep message-specific state local to each hook invocation.
Concurrency model
Values stored in fields of Main.groovy may be observed by multiple concurrent message-processing calls. A MessageEnvelope and the FHIR resources for one message belong to that processing operation and should not be retained after the hook returns.
Thread-safety rules
- Avoid mutable shared state whenever possible.
- Prefer local variables for per-message data.
- Do not keep
MessageEnvelopeor per-message resource references in script fields. - If shared mutable state is truly required, use appropriate thread-safe structures.
- Use SDK cache abstractions instead of custom static maps.
- Initialize generation-wide resources in
onStartup(). - Release generation-owned external resources in
onShutdown()when appropriate.
Safe and unsafe state
@Override
Bundle postConvert(MessageEnvelope envelope, Bundle bundle) {
String messageId = envelope.messageControlId
return enrich(bundle, messageId)
}private List<String> processedMessageIds = []
@Override
Bundle postConvert(MessageEnvelope envelope, Bundle bundle) {
processedMessageIds << envelope.messageControlId
return bundle
}Message-local state
It is safe to modify the parsed HL7 message for the current message inside preConvert(). Do not store the MessageEnvelope, parsed HAPI message, generated Bundle, or resource references in generation-wide fields for later reuse.
HTTP client reuse
ScriptHttpClient instances are thread-safe and should normally be created once during onStartup() and reused across hook invocations.
ScriptHttpClient notificationClient
@Override
void onStartup() {
notificationClient = httpClientFactory.create("notifications", notificationHttpProps)
}
Async callbacks
Keep asynchronous callback logic lightweight and independent. Do not mutate unsynchronized shared collections or rely on message-specific state after the original hook has continued. Logging, metrics, or self-contained result handling are safer callback responsibilities.
Generation-wide resources
Clients, immutable configuration, and SDK-managed caches are appropriate generation-wide objects. Create or prepare them during onStartup(), reuse them while the generation is active, and clean up resources owned by the script during onShutdown() when required.
