Slow C-FIND with RadiAnt after upgrade to Orthanc 1.12.10 — issue isolated to NumberOfStudyRelatedInstances

Hello,

I would like to report a performance issue that appeared after upgrading Orthanc from 1.12.6 to 1.12.10.

Orthanc Statistics
“CountInstances”: 24183765,
“CountPatients”: 133704,
“CountSeries”: 671615,
“CountStudies”: 383134,
“TotalDiskSize”: “26209282329275”,
“TotalDiskSizeMB”: 24995119,
“TotalUncompressedSize”: “31635319468676”,
“TotalUncompressedSizeMB”: 30169791

Environment
Orthanc version: 1.12.10
Previous working version: 1.12.6
OS: Windows Server 2016
Database: PostgreSQL for Orthanc index
Storage: DICOM files on filesystem
Orthanc runs as a Windows service
Client: RadiAnt DICOM Viewer 2025.2

Problem
After upgrading to Orthanc 1.12.10, studies are no longer displayed properly in RadiAnt when querying the server.
RadiAnt can connect to Orthanc successfully, but the study search is very slow and eventually fails. In the Orthanc logs I see that the association is established, C-FIND starts, and then later ends with:
Find SCP Failed: DIMSE Failed to send message
Peer aborted Association
So the connection itself is fine, but the response to C-FIND becomes too slow and the client aborts.

What I tested
I tried multiple things already:
-Added ExtraMainDicomTags
-Ran reconstruct for studies
-Disabled hybrid S3 plugin usage
-Ran VACUUM on PostgreSQL
Unfortunately, none of these solved the issue.

Important finding
I isolated the problem using /tools/find.

Fast request
This request is fast:
POST /tools/find
{
“Level”: “Study”,
“Expand”: true,
“Query”: {
“StudyDate”: “20260313-20260313”
},
“RequestedTags”: [
“StudyInstanceUID”,
“StudyDate”,
“StudyTime”,
“StudyID”,
“StudyDescription”,
“AccessionNumber”,
“InstitutionName”,
“PatientName”,
“PatientID”,
“PatientBirthDate”,
“PatientSex”
]
}

Also fast
This request is also fast:
OST /tools/find
{
“Level”: “Study”,
“Expand”: true,
“Query”: {
“StudyDate”: “20260313-20260313”
},
“RequestedTags”: [
“StudyInstanceUID”,
“ReferringPhysicianName”,
“InstitutionalDepartmentName”,
“PerformingPhysicianName”,
“NameOfPhysiciansReadingStudy”
]
}

Fast
This request is fast too:
POST /tools/find
{
“Level”: “Study”,
“Expand”: true,
“Query”: {
“StudyDate”: “20260313-20260313”
},
“RequestedTags”: [
“StudyInstanceUID”,
“ModalitiesInStudy”
]
}

Slow / hangs
But this request becomes very slow / hangs:
POST /tools/find
{
“Level”: “Study”,
“Expand”: true,
“Query”: {
“StudyDate”: “20260313-20260313”
},
“RequestedTags”: [
“StudyInstanceUID”,
“NumberOfStudyRelatedInstances”
]
}

So the issue seems to be specifically related to:
NumberOfStudyRelatedInstances
This also matches RadiAnt behavior, because RadiAnt appears to request this field in its C-FIND query, and then the whole query becomes too slow.

Conclusion
At this point, it looks like the slowdown is specifically caused by calculating or returning NumberOfStudyRelatedInstances at Study level after upgrading to Orthanc 1.12.10.
On Orthanc 1.12.6 this workflow worked acceptably with the same client.

Questions

  1. Is there any known regression or performance change in Orthanc 1.12.10 related to NumberOfStudyRelatedInstances?
  2. Is there a recommended way to optimize this field for Study-level C-FIND?
  3. Is there any option to disable or shortcut this calculation for DICOM C-FIND responses?
  4. Would you recommend a specific PostgreSQL maintenance or schema-related action for this case?

If needed, I provide:
-Orthanc logs
-configuration excerpts
-full RadiAnt C-FIND request content

I really need any help, thank you!

radiant_cfind_request.txt (2.1 KB)

orthanc_log_summary.txt (8.3 KB)

orthanc_config.txt (1.7 KB)

Hi @OlegYuryev

We have completely re-worked the way this tag is being computed and the DB housekeeper needs time to populate a new table.

In verbose mode, if you are still seeing “Computed 50 missing ChildCount entries” displayed every seconds, this means that the new table is not fully updated.

Hope this helps,

Alain.

1 Like

@alainmazy , thank you for the clarification.

I would like to confirm one additional point regarding newly uploaded studies.

After the upgrade, I also tested with studies uploaded to Orthanc after the database/schema update. However, those tests may have been mixed with older studies in the same search results, so I am not sure whether that was a valid check.

Could you please clarify the following:

For studies uploaded after the database update, are the ChildCount values written immediately according to the new scheme, so that such studies should be immediately searchable?

Or can even newly uploaded studies still be affected until the background DB housekeeping has fully completed?

Thank you.

The newly uploaded studies should be correct and quick immediately.

1 Like

Hi @alainmazy thats sound good, could you tell me Where were we find this documentation about this , You recomend use this version Orthanc version: 1.12.10 if the setup is new on the Windows Server. There is a versión Linux ?

Thanks for you help!!!

Yes:

Note that if you plan to use Docker, the latest postgresql plugin is currently only available in the orthancteam/orthanc images; not the jodogne/orthanc-plugins images.

1 Like

Thank you for your help,

Currently, my situation is as follows: I have a nearly full 4TB storage unit, which is an SSD designed for I/O performance, and another SSD dedicated to the database and the Orthanc engine, along with 32GB of RAM. Here’s what’s happening:

Background: I use OHIF as a viewer, which is hosted on the same web server (Windows/IIS).

The loading of studies has worsened considerably; response times and image loading in the viewer have been negatively affected. In this context, I have considered some alternatives, where I would greatly appreciate your valuable experience and recommendations:

Store the files (DICOM) in compressed format. While researching, I found that the Orthanc engine supports compression as long as it is configured accordingly in the orthanc.json file, where I have already made some adjustments: {

"Name" : "Siperec",

"StorageDirectory" : "G:\\\\Pacs",

"StorageCompression" : false,

"MaximumStorageSize" : 0,

"MaximumPatientCount" : 0,

"MaximumStorageMode" : "Recycle",

"MaximumStorageCacheSize" : 1024,

"LuaHeartBeatPeriod" : 0,

"LuaScripts": \["Scripts\\\\SendToCharrua.lua"\],

"Plugins" : \[ "\\\\Orthanc Server\\\\Plugins"\],

"ConcurrentJobs" : 8,

"JobsEngineThreadsCount" : {

  "ResourceModification": 4

 },

"HttpServerEnabled" : true,

"OrthancExplorerEnabled" : true,

"HttpPort" : 8042,

"HttpDescribeErrors" : true,

"HttpCompressionEnabled" : true,

"WebDavEnabled" : true,

"WebDavDeleteAllowed" : false,

"WebDavUploadAllowed" : true,

"DicomServerEnabled" : true,

"DicomAet" : "PACS",

"DicomCheckCalledAet" : false,

"DicomPort" : 4242,

"BindAddress" : "0.0.0.0",

"DefaultEncoding" : "Latin1",

// “AcceptedTransferSyntaxes” : [ “1.2.840.10008.1.*” ],// validar si falla descomentar

“AcceptedTransferSyntaxes”: [

"1.2.840.10008.1.2",         // Implicit VR Little Endian

"1.2.840.10008.1.2.1",       // Explicit VR Little Endian

"1.2.840.10008.1.2.2",       // Explicit VR Big Endian

"1.2.840.10008.1.2.4.70",    // JPEG Lossless

"1.2.840.10008.1.2.4.80",    // JPEG-LS Lossless

"1.2.840.10008.1.2.4.90"     // JPEG 2000 Lossless

\],

"JpegLosslessTransferSyntaxAccepted": false,  // o simplemente omítelo

"JpegTransferSyntaxAccepted": false,

"Jpeg2000TransferSyntaxAccepted": false,

"UnknownSopClassAccepted" : false,

"DicomScpTimeout" : 60,

"RemoteAccessAllowed" : true,

"SslEnabled" : false,

"SslCertificate" : "certificate.pem",

"SslMinimumProtocolVersion" : 4,

"SslVerifyPeers" : false,

"SslTrustedClientCertificates" : "trustedClientCertificates.pem",

"HttpBindAddress": "0.0.0.0",

"AuthenticationEnabled" : false,

"DicomTlsEnabled" : false,

"DicomTlsRemoteCertificateRequired" : true,

"DicomTlsMinimumProtocolVersion" : 0,

"DicomAlwaysAllowEcho" : true,

"DicomAlwaysAllowStore" : true,

"DicomAlwaysAllowFind" : false,

"DicomAlwaysAllowFindWorklist" : true,

"DicomAlwaysAllowGet" : false,

"DicomAlwaysAllowMove" : false,

"DicomCheckModalityHost" : false,

"DicomModalities" : {

        "MAMOGRAGO": \["MAMOGRAFO","192.168.0.187",104\]

},

"DicomModalitiesInDatabase" : false,

"DicomEchoChecksFind" : false,

"DicomDefaultRetrieveMethod" : "C-MOVE",

"DicomScuTimeout" : 30,

"DicomScuPreferredTransferSyntax" : "1.2.840.10008.1.2.1",

"DicomThreadsCount" : 8,

"OrthancPeers" :  {},

"OrthancPeersInDatabase" : false,

"HttpProxy" : "",

"HttpVerbose" : true,

"HttpTimeout" : 120,

"HttpsVerifyPeers" : true,

"HttpsCACertificates" : "C:\\\\Program Files\\\\Orthanc Server\\\\Configuration\\\\ca-certificates.crt",

"UserMetadata" : {

},

"UserContentType" : {

},

"StableAge" : 60,

"StrictAetComparison" : false,

"StoreMD5ForAttachments" : false,

"LimitFindResults" : 200,

"LimitFindInstances" : 200,

"LogExportedResources" : false,

"KeepAlive" : true,

"KeepAliveTimeout" : 30,

"TcpNoDelay" : true,

"HttpThreadsCount" : 50,

"StoreDicom" : true,

"DicomAssociationCloseDelay" : 2,

"QueryRetrieveSize" : 100,

"CaseSensitivePN" : false,

"LoadPrivateDictionary" : true,

"Dictionary" : {

},

"SynchronousCMove" : true,

"JobsHistorySize" : 10,

"SaveJobs" : true,

"OverwriteInstances" : false,

"MediaArchiveSize" : 1,

"StorageAccessOnFind" : "Always",

"MetricsEnabled" : true,

"RestApiWriteToFileSystemEnabled": true,

"HttpRequestTimeout" : 60,

"DefaultPrivateCreator" : "",

"StorageCommitmentReportsSize" : 100,

"TranscodeDicomProtocol" : false,

"BuiltinDecoderTranscoderOrder" : "After",

"IngestTranscodingOfUncompressed" : false,

"IngestTranscodingOfCompressed" : false,

"DicomLossyTranscodingQuality" : 90,

"SyncStorageArea" : false,

"MallocArenaMax" : 2,

"DeidentifyLogs" : true,

"DeidentifyLogsDicomVersion" : "2023b",

"MaximumPduLength" : 32768,

"CheckRevisions" : false,

"SynchronousZipStream" : true,

"ZipLoaderThreads": 4,

"Warnings" : {

  "W001_TagsBeingReadFromStorage": true,

  "W002_InconsistentDicomTagsInDb": true,

  "W003_DecoderFailure": true,

  "W004_NoMainDicomTagsSignature": true,

  "W005_RequestingTagFromLowerResourceLevel": true,

  "W006_RequestingTagFromMetaHeader": true,

  "W007_MissingRequestedTagsNotReadFromDisk": true

},

"ReadOnly" : false,

"MaximumConcurrentDcmtkTranscoders" : 4

}

However, I would appreciate knowing if there is any other way to improve it. I understand that by compressing the files, first it will reduce storage space, and second, when the viewer requests a study, since the data will no longer be in raw format but compressed, the response times will be faster. If it is necessary to provide additional information for more detail and to improve your recommendations, I would be happy to share evidence.

@alainmazy, Hello again :slight_smile:

I’m sorry to bother you. I tried updating my Orthanc again.
Unfortunately, I am still missing two critical confirmations.

In the Orthanc log, I can see DB housekeeping activity running almost every second:

“DB HOUSEKEEPING … Calling service 31 from plugin OrthancPostgreSQLIndex.dll”

However, I do not see any in verbose / trace mode explicit “Computed 50 missing ChildCount entries” messages, so I cannot determine whether the ChildCount backfill is actually running, whether it has completed, or how much work is left.

Also, I tested with a newly uploaded study only. The C-FIND query was narrow (current StudyDate + PatientID), and Orthanc log shows:

“Number of candidate resources after fast DB filtering on main DICOM tags: 1”

Despite that, the response still took about 50 seconds before returning NumberOfStudyRelatedInstances, and RadiAnt aborted the association.

Could you please clarify:

  1. Is “DB HOUSEKEEPING … service 31” the ChildCount backfill process?
  2. How can I reliably verify whether the ChildCount migration is running and whether it has finished?
  3. Is it expected that, while this process is running, even a newly uploaded study with only 1 candidate can take ~50 seconds to answer a C-FIND request with NumberOfStudyRelatedInstances?
  4. After the backfill is fully complete, should this latency disappear for such narrow queries?

Thank you.

No that will be slower because the data needs to be uncompressed from disk first.
BTW, please, open a new thread since these questions are unrelated to the “NumberOfStudyRelatedInstances” that is the subject of this thread

1 Like

If you don’t see “Computed 50 missing ChildCount entries”, it means it is complete.

Of course not. Check/share your verbose logs … (multiple COMPLETE lines !)

Thank you for your response. Done—I created a new case so it’s separate and doesn’t cause confusion. Sorry about that.

@alainmazy
I believe this log clearly shows the problem timeline.

  • 00:24:05.552 - Orthanc receives the C-FIND request from RadiAnt (SRVRADIANT7)
  • 00:24:05.563 - The request already includes NumberOfStudyRelatedInstances
  • 00:24:06 through 00:24:56 - continuous “DB HOUSEKEEPING … Calling service 31” activity from OrthancPostgreSQLIndex.dll
  • 00:24:56.854 - only after ~51 seconds Orthanc reaches:
    “Number of candidate resources after fast DB filtering on main DICOM tags: 1”
  • 00:24:56.855 - Orthanc tries to send “C-FIND Response 1/1”
  • 00:24:56.855 - RadiAnt has already aborted the association
  • 00:24:56.861 - DIMSE timeout is logged

So the query is narrow, filtering results in only 1 candidate, but Orthanc still spends about 51 seconds before sending the first response.

OrthancLog.txt (676.0 KB)

Hi @OlegYuryev

This means that the SQL servers needs 51 seconds to execute the “find” query which is very unusual especially if the same query with NumberOfStudyRelatedInstances is fast.

image

You should check on your PostgreSQL server side if it has reached some kind of limit wrt e.g memory when executing this query.

You can possibly set EnableVerboseLogs in PostgreSQL, capture the find queries (they are very long !), replace the arguments by real value and execute them directly in postgresql with EXPLAIN ANALYZE in front to understand why it is so slow…

Best,

Alain.

1 Like

Hi @alainmazy

Thank you for your guidance.

We enabled PostgreSQL verbose logs and extracted the exact SQL query generated by Orthanc for the C-FIND request with NumberOfStudyRelatedInstances.

We then executed this query manually with:

EXPLAIN (ANALYZE, BUFFERS, VERBOSE)

Key findings:

  1. The lookup phase is fast. It returns only 2 studies almost instantly using index scans.

  2. Almost all execution time is spent in ValidGrandChildCounts

  3. Total execution time:
    ~58,727 ms

PostgreSQL spends most of the time materializing ValidGrandChildCounts for tens of millions of rows and then scanning it almost entirely to retrieve just 2 needed studies.

This makes C-FIND with NumberOfStudyRelatedInstances extremely slow in our environment.
We are attaching the SQL Query and full EXPLAIN ANALYZE this query.

SqlQuery.txt (4.2 KB)

ExplainAnalyze.txt (35.8 KB)

Hi @OlegYuryev

Thanks a lot for that.

I’ll need some time in order to analyze that. I’ll keep you posted of any findings.

Best regards,

Alain.

1 Like

Hi @OlegYuryev

Well, I ran the same query on a DB with 350.000 studies and 150M instances and the execution plan is really very different from yours. The execution plan can depend on lots of stuff like the distribution of data in indexes. On my side, the plan does not expect a lot of rows and therefore it is efficient to use the indexes while your plan expects a lot more rows and therefore, a full table scan is sometimes considered faster than using indexes.

SqlQuery-ot.txt (4.2 KB)
ExplainAnalyze-ot.txt (16.9 KB)

Here are “visual representations” of

FYI, I’m running PG 17.6 with the default values for

show shared_buffers;        --> 128 MB
show wal_buffers;           -->   4 MB
show effective_cache_size;  -->   4 GB
show work_mem;              -->   4 MB
show maintenance_work_mem;  -->  64 MB

Usually, we do increase these values following these recommandations but here, since it’s a test server, I kept the defaults … The PG test server is running on a dedicated VM with 24 GB RAM used together with the PG prod server.

I’m afraid there is not much more I can do on this. When we refactored the way ChildCounts where computed, we did it together with one of our customer who has many large DBs too, and the performance where stable on all of their setup although the execution plans where not always exactly the same as the one I had on our test server.

What version of PG are you running ?

Alain.

Hi @alainmazy

Thank you so much, this is very helpful.

There is one important difference in our setup:
We are running PostgreSQL 11, while you are using PostgreSQL 17.6.

Is it possible that the problem is in the old 11 version of PostgreSQL?

I guess upgrading can help…
Note that, when upgrading such a big database, you need to first dump the DB and then re-import it and this can take 5-6 hours in total …

1 Like

Thank you @alainmazy

We will try to update the SQL version to 17. In the Internet, it is recommended to do this through pg_upgrade, as it is faster than dump/restore :face_with_monocle:
But before updating, we will take a snapshot of the server if something goes wrong.

In any case, we will inform you about the results after updating the PostgreSQL.

1 Like

Hi @alainmazy

I’m reporting the results. We have successfully upgraded PostgreSQL to version 17. And now we can say for sure that the problem was in the old 11 version of PostgreSQL..
After the update, Orthanc 1.12.10 started working stably, the C-FIND speed is fast, everything works fine.

Thank you again so much for your help :handshake:

1 Like