Skip to content

C-Move request so slow with gunicorn #872

@novichikhin

Description

@novichikhin

Common DICOM Client code

PatientRootQueryRetrieveInformationModelMove = (
    sop_class._QR_CLASSES["PatientRootQueryRetrieveInformationModelMove"]
)


class DicomClient:

    def __init__(
        self,
        *,
        local_node: DcmNode,
        remote_node: DcmNode
    ):
        self._local_node = local_node
        self._remote_node = remote_node

    @contextmanager
    def _association(
        self,
        ae: ApplicationEntity,
        evt_handlers: Optional[List[EventHandlerType]] = None
    ) -> Association:
        assoc = ae.associate(
            addr=self._remote_node.host,
            port=self._remote_node.port,
            ae_title=self._remote_node.ae_title,
            evt_handlers=evt_handlers
        )

        if not assoc.is_established:
            raise FailedAssociationError

        try:
            yield assoc
        finally:
            assoc.release()

    def _handle_store(self, datasets: List[Dataset]) -> Callable[[Event], int]:

        def handle_store(event: Event) -> int:
            dataset = event.dataset
            dataset.file_meta = event.file_meta

            datasets.append(dataset)

            return 0x0000

        return handle_store

    def send_c_move(
        self,
        *,
        query: Dataset,
        datasets: List[Dataset]
    ) -> None:
        ae = ApplicationEntity(ae_title=self._remote_node.ae_title)

        ae.add_requested_context(
            PatientRootQueryRetrieveInformationModelMove,
            ImplicitVRLittleEndian
        )
        ae.supported_contexts = StoragePresentationContexts

        evt_handlers = [(events.EVT_C_STORE, self._handle_store(datasets=datasets))]

        scp = ae.start_server(
            address=(self._local_node.host, self._local_node.port),
            block=False,
            evt_handlers=evt_handlers
        )

        with self._association(ae=ae, evt_handlers=evt_handlers) as assoc:
            assoc.send_c_move(
                query,
                self._local_node.ae_title,
                PatientRootQueryRetrieveInformationModelMove
            )

        scp.shutdown()

Simple python script code

def main() -> None:
    dicom_client = DicomClient(
        local_node=DcmNode(host='localhost', ae_title='TSAMI', port=1111),
        remote_node=DcmNode(host='localhost', ae_title='CONQUESTSRV1', port=5678)
    )

    query = Dataset()
    query.QueryRetrieveLevel = "STUDY"
    query.StudyID = "433724515"

    datasets = []

    start_perf_counter = time.perf_counter()

    dicom_client.send_c_move(query=query, datasets=datasets)

    print(len(datasets), time.perf_counter() - start_perf_counter)


if __name__ == "__main__":
    main()

Output: 384 2.6142295409990766

Flask application with gunicorn with gthread worker (in one process and one thread)

app = Flask(__name__)
dicom_client = DicomClient(
    local_node=DcmNode(host='localhost', ae_title='MY-AE-TITLE', port=1111),
    remote_node=DcmNode(host='localhost', ae_title='CONQUESTSRV1', port=5678)
)


@app.route('/')
def hello():
    query = Dataset()
    query.QueryRetrieveLevel = "STUDY"
    query.StudyID = "433724515"

    datasets = []

    start_perf_counter = time.perf_counter()

    dicom_client.send_c_move(query=query, datasets=datasets)

    print(len(datasets), time.perf_counter() - start_perf_counter)

    return "ok"

gunicorn --bind "127.0.0.1:8000" --worker-class "gthread" test:app

Output: 384 5.645078083998669

Why is this happening and how to fix it?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions