AI Agents · Performance

Acumatica Performance — Diagnosing Slow Disk I/O

Acumatica Performance — Diagnosing Slow Disk I/O is the Acumatica performance topic that nobody asks about until they have to.

John Kihiu12 min read

Disk I/O problems on Acumatica present as generalized slowness across everything at once — every screen, every report, every batch job runs at half the normal speed simultaneously — which is exactly why they get misdiagnosed as "the ERP is slow" rather than traced to storage, the layer nobody thinks to check because it usually just works.

Which disk is actually the bottleneck

Acumatica-relevant I/O happens in three separate places, and only one of them is usually the real culprit: the SQL Server data files (.mdf/.ndf), the SQL Server transaction log file (.ldf), and the app server's local disk (mostly irrelevant unless file attachments are stored on local disk rather than blob storage, or the app pool's temp directories are under pressure). Transaction log I/O is the one that catches people off guard — it is write-heavy and latency-sensitive in a way data file reads are not, because every write transaction must be durably logged before it commits, full stop, no shortcuts.

SQL — per-file I/O stall time, the single best first check
SELECT
    DB_NAME(vfs.database_id) AS db_name,
    mf.physical_name,
    vfs.io_stall_read_ms, vfs.num_of_reads,
    vfs.io_stall_write_ms, vfs.num_of_writes,
    (vfs.io_stall_read_ms + vfs.io_stall_write_ms) AS total_stall_ms
FROM sys.dm_io_virtual_file_stats(NULL, NULL) vfs
JOIN sys.master_files mf ON mf.database_id = vfs.database_id
    AND mf.file_id = vfs.file_id
WHERE DB_NAME(vfs.database_id) = 'AcumaticaProd'
ORDER BY total_stall_ms DESC;

Divide stall time by read/write count to get average latency per operation. Data file reads averaging over roughly 10-20ms, or log writes averaging over 1-2ms, indicate the storage layer is the constraint — those thresholds are conservative rules of thumb for spinning or shared network storage; NVMe-backed storage should be an order of magnitude faster and any sustained latency near those numbers on flash storage points at contention, not raw hardware capability.

The transaction log specifically

tempdb is Acumatica's quiet dependency

Generic Inquiries, report queries with sorting/grouping, and BQL queries with distinct or aggregate operations lean on tempdb heavily. A single tempdb data file (the SQL Server default) under real concurrency creates allocation page contention regardless of how fast the underlying disk is. Configure multiple tempdb data files — a common starting rule is one file per CPU core up to about eight, all pre-sized equally — before assuming a slow GI's disk I/O problem is about the user database at all.

If you are on cloud-provisioned storage

Provisioned IOPS storage (Azure managed disks, AWS EBS, and similar) has a hard ceiling based on the disk tier you selected, not the instance size — you can have a large, fast VM attached to an undersized disk tier and never see the VM's own limits. Check the cloud provider's disk metrics for throttling, not just SQL Server's own DMVs, because SQL Server reports the latency it experiences without necessarily surfacing that the cause is a provisioned IOPS cap being hit.

Wrapping up

Confirm disk I/O is the actual bottleneck with sys.dm_io_virtual_file_stats before spending money on faster storage. Separate log, data, and tempdb I/O in your analysis since they have different characteristics and different fixes, pre-size files to avoid autogrow stalls, and check cloud provisioned-IOPS limits separately from what SQL Server itself reports.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.