A client saw a stream of latch timeouts a few minutes after restarting SQL Server, all naming FGCB_ADD_REMOVE. It sounds like a deep engine problem. It is almost always a file taking too long to grow. I measured how long a file growth really takes on SQL Server 2025, and the difference between the data file and the log file is the whole story.

The Message
Timeout occurred while waiting for latch: class 'FGCB_ADD_REMOVE',
id 00000000807D59B8, type 2, Task 0x00000000C896FDC8 : 0, waittime 300,
flags 0x1a, owning task 0x00000000820BE608. Continuing to wait.Three parts of this matter. FGCB is the filegroup control block. It tracks the data files in a filegroup. ADD_REMOVE means something is changing those files: a growth, a shrink, a new file. And waittime 300 means a task has waited 300 seconds.
Then look at the owning task. In my client’s log, every waiting session named the same owner. One task held the latch. Everyone who needed that filegroup queued behind it. The line ends with “Continuing to wait”. That is SQL Server being honest: nothing has failed yet, it is just very slow.
What Was Holding It
We captured an Extended Events session and the owner turned out to be an autogrowth of a data file. The file needed more room, the growth started, and it took so long that everything else queued up behind it.
Why right after a restart? I cannot prove it for their server, but it is the usual pattern. A restart is when every application reconnects at once and the work that queued up during the outage arrives together. Whatever was going to fill the file fills it quickly.
What a Growth Costs, Measured
I created a fresh database and grew each file from 8 MB to 4 GB, timing each one.
ALTER DATABASE GrowLab MODIFY FILE (NAME = GrowLab, SIZE = 4096MB);
ALTER DATABASE GrowLab MODIFY FILE (NAME = GrowLab_log, SIZE = 4096MB);data file +4 GB 98 ms
log file +4 GB 2287 msSame size, same disk, same second. The log took twenty-three times longer.
The difference is zeroing. Before SQL Server uses new space, it normally writes zeros over every byte. The data file skipped that, because this instance has instant file initialization switched on. The log file cannot skip it. SQL Server zeroes a large log growth whatever the setting, because it must read the log back safely. That is documented behaviour. My 2.3 seconds is what it cost on my own test machine.
So the log growth is a fair picture of what the data file growth would cost with instant file initialization switched off. On a slow SAN, or a disk that is busy with the rush after a restart, multiply accordingly. That is how you reach 300 seconds.
Check Instant File Initialization
You no longer need to dig through the ERRORLOG or a trace flag to find out. One query:
SELECT servicename, instant_file_initialization_enabled
FROM sys.dm_server_services
WHERE servicename LIKE 'SQL Server (%';SQL Server (SQLDEV) YIf it says N, the service account does not have the Windows right Perform volume maintenance tasks. Grant it in the local security policy, or tick the box that setup offers during installation, and restart the service. My client had it switched off. That was half of their problem.
Check the Growth Setting
The other half was the growth size. Percentage growth looks harmless on a small file and becomes enormous on a big one. Ten percent of a 2 TB file is 200 GB, in one go, while everybody waits.
SELECT DB_NAME(database_id) AS database_name,
name AS file_name,
type_desc,
size * 8 / 1024 AS size_mb,
CASE WHEN is_percent_growth = 1
THEN CAST(growth AS varchar(10)) + ' percent'
ELSE CAST(growth * 8 / 1024 AS varchar(10)) + ' MB' END AS growth
FROM sys.master_files
ORDER BY is_percent_growth DESC, size DESC;A new database on this instance came out with 64 MB growth for both files, which is today’s default and a sensible one. Older databases, or ones restored from older servers, often still carry 10 percent. Those float to the top of that query.
I set growth in fixed megabytes. For data files with instant file initialization on, 512 MB or 1024 MB is comfortable, because the growth is nearly free. Keep log growth smaller. The log is not in a filegroup, so it never takes this latch. But every megabyte of it is zeroed, and writes to the database wait while that happens. Different wait, same pain.
The Real Fix Is Not to Grow at All
Autogrowth is a safety net, not a plan. If you know a file is going to need 200 GB next month, grow it now, deliberately, at a quiet hour. Then autogrowth only ever fires for the surprise, and nobody queues behind it in the middle of the morning.
You can see how often the latch has been a problem since the last restart:
SELECT latch_class, waiting_requests_count, wait_time_ms, max_wait_time_ms
FROM sys.dm_os_latch_stats
WHERE latch_class = 'FGCB_ADD_REMOVE';Mine stayed at zero through both of my growths, because nothing else was waiting on that filegroup at the time. On a busy server during a slow growth, that is the number that climbs.
FGCB_ADD_REMOVE is not a broken engine, it is everyone waiting for one file to finish growing.
Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.
Discover more from SQL Authority with Pinal Dave
Subscribe to get the latest posts sent to your email.




