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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions