More Memory, Same Bottleneck — A Full-Day SQL Server Case Study on an AWS EC2 Upgrade

More Memory, Same Bottleneck — A Full-Day SQL Server Case Study on an AWS EC2 Upgrade

More Memory, Same Bottleneck — A Full-Day SQL Server Case Study on an AWS EC2 Upgrade


SQL Server 2016+ on Amazon EC2 · EBS gp2 / gp3 · NVMe instance store

A production SQL Server instance was suffering from slow queries and TempDB latency. The response was a familiar one: move to a bigger EC2 instance tier. RAM doubled from 64 GB to 128 GB, logical processors doubled from 8 to 16, the data volume doubled in size, and max server memory was raised from 48,000 MB to 100,000 MB on the first morning.

By mid-morning the picture looked alarming. Write latency on the data volume sat at 307 ms, four database files had logged 15 to 22 second I/O stalls, the buffer pool was growing at roughly 9.6 GB per hour toward the new cap, and the Availability Group secondary fell out of a healthy state. The obvious call, and the one the initial diagnostic report made with confidence, was to revert the memory cap to 48,000 MB immediately.

Then the afternoon data arrived and told a different story. Once the buffer pool reached the cap and stopped growing, the server found equilibrium. Page Life Expectancy climbed from 1,432 to 8,868. Write latency eased from 307 ms to 253 ms. Query response times on the busiest database recovered from a 50.78 ms ramp-up spike back to 9.29 ms, near its 22 ms multi-week baseline, under full afternoon load. The urgent rollback that seemed obvious at 09:40 would have been the wrong change by 14:17.

This article is a factual walkthrough of that entire day, with every number taken from captured diagnostics and every technical claim checked against Microsoft Learn and AWS documentation. Server names, database names, and identifying details have been replaced with generic ones. Two things make this incident worth studying: the parts of the analysis that were right, and the parts that confident, evidence-quoting reports (human and AI alike) got wrong in both directions by drawing conclusions before the data was done moving.

The one-paragraph version: a bigger instance does not fix scan-driven I/O, and the memory increase was not needed by the prior workload. But the real risk window was the ramp-up, while the buffer pool filled against a throttled EBS volume, not the steady state. Once at the cap, the larger cache delivered measurable benefit, and the root cause (heap scans and a missing index) remained exactly where it was at 48 GB, at 64 GB of RAM, and at 128 GB of RAM.

1The Environment and the Change

The instance in this case study, referred to here as SQLPROD01, is the primary replica of a two-node Always On Availability Group running SQL Server 2022 on Amazon EC2, hosting several OLTP databases. The workload had two long-standing complaints: intermittent query slowness dominated by disk reads, and TempDB write latency in the 130 to 151 ms range.

The remediation chosen was an instance tier upgrade:

AttributeBeforeAfter
Instance class8 vCPU / 64 GB RAMr8id.4xlarge, 16 vCPU / 128 GB RAM
CPU topology8 logical processors1 socket, 8 physical cores, 16 logical processors
max server memory48,000 MB100,000 MB (raised at 08:09 on day one)
Data volume (EBS)2.44 TB4.88 TB (doubled)
TempDB locationEBS data volumeLocal NVMe instance store
MAXDOP88 (unchanged)
Soft-NUMAOFF (single node)OFF (single node)

Everything below rests on diagnostics captured across the first full day on the new instance, from 06:43 to 14:17: error log entries, sys.dm_os_wait_stats, sys.dm_os_sys_info, sys.dm_os_nodes, sys.dm_os_memory_nodes, sys.dm_io_virtual_file_stats, sys.query_store_runtime_stats, plus the prior three weeks of daily health checks from the old hardware.

2The Week Before: Memory Was Healthy

The most important pre-upgrade evidence: for sixteen consecutive days on the 64 GB instance with a 48,000 MB memory cap, every daily health check told the same story:

DateSQL Memory In UsePLE (sec)Memory Pressure?
Day 147.53 GBNo
Day 247.65 GB1,143No
Day 547.74 GB833No
Day 1062.65 GB allocated391PLE dip, I/O driven
Day 1148.09 GBNo
Day 16~48 GBNo

Page Life Expectancy held between 833 and 1,143 seconds all week. There were zero memory grants pending, zero out-of-memory conditions, and zero low-memory warnings across the period. The single PLE dip to 391 coincided with a PAGEIOLATCH_SH burst at 21.7 million ms, a disk-read event, not memory starvation.

What this establishes: the workload fit inside a 48 GB buffer pool. Memory was not the constrained resource, and nothing about the prior data demanded a larger cap. That conclusion was, and remains, correct. What it does not establish, as the rest of the day proved, is that a larger cap would hurt at steady state.

3The Morning: What the First Captures Showed

On the first morning of the new 128 GB instance, before business-hours load arrived, the diagnostics captured the following state:

MetricValueAssessment
OS physical RAM128,991 MBNew hardware confirmed
max server memory100,000 MBRaised at 08:09, day one
SQL memory in use, 06:4355,460 MBOnly 55% of the new cap
SQL memory in use, 09:4075,552 MB+20 GB in 3 hours, off-peak
PLE at 06:431,432 secPost-restart; not yet meaningful
D: (data volume) write latency307 msWas sub-1 ms on good days prior
I/O stalls at 04:174 files, 15–22 secLogged before the memory change at 08:09
Dominant waitPAGEIOLATCH_SH, 8.67M msReading from disk, not cache
WRITELOG3.06M msLog flush pressure
HADR_SYNC_COMMIT1.85M msAG secondary NOT_HEALTHY

Two details in that table framed the morning’s alarm, and both were real. The 15 to 22 second I/O stalls were logged at 04:17, nearly four hours before the memory cap was raised, proving the data volume was already saturated independently of the memory change. And the buffer pool was growing fast toward a cap the volume seemed unable to afford. What the morning captures could not show was where that growth curve would land, because a filling buffer pool and a full one behave differently.

4The Ramp-Up Window: Why Filling a Bigger Buffer Pool Pressures I/O

The causal chain that made the morning genuinely risky is mechanical, and each step was visible in the evidence:

  1. A raised cap opens headroom the pool immediately fills. Raising max server memory from 48,000 MB to 100,000 MB invited SQL Server to grow the buffer pool by 52 GB. Population is read I/O, and it started at once: +20 GB in the first three hours, roughly 9.6 GB per hour.
  2. Growth-phase writes stack on top. As the pool grows, dirty pages accumulate in a larger and larger space, and checkpoint plus lazy writer flush volume rises with it, against a volume already at roughly 234 transfers per second with 307 ms write latency.
  3. User queries feel the contention. During the growth window, the busiest OLTP database’s average response climbed from a 22 ms baseline to 50.78 ms at 12:26. That degradation was real, measured, and ramp-up specific.
  4. Saturated writes cascade into the AG. WRITELOG waits climb, commits queue, and HADR_SYNC_COMMIT grows as the synchronous secondary falls behind.

The transient, correctly identified: a large one-day cap increase on a write-throttled volume creates a genuine turbulence window while the pool fills. On this server that window ran from roughly 08:09 to just past noon. Planning a large cap change for a quiet overnight window, rather than the first business morning on new hardware, avoids exactly this.

What the ramp-up chain does not predict is permanent degradation. Buffer pool growth is self-limiting: it stops at the cap. The open question at 09:40 was whether the volume would survive until then, and whether steady state at the cap would be better or worse than steady state at 48 GB. The morning report answered that question early. The afternoon answered it correctly.

5The Afternoon Reversal: Steady State at the Cap

The full-day capture timeline, from first capture to stable afternoon load:

TimeSQL Memory In UsePLE (sec)D: Write LatencyNotes
06:4355,460 MB1,432307 msFirst capture; cap set to 100,000 MB at 08:09
09:4075,552 MB3,900291 ms+20 GB in 3 hours; soft-NUMA confirmed OFF
11:4596,047 MB277 ms4 GB from cap; growth ~9.6 GB/hr
12:1599,821 MB270 msAt cap boundary; lazy writer activating
12:3999,405 MB2,998270 msHealth check: CPU 2.55%, no new stall events
13:2399,422 MB5,658266 msPLE doubled from 12:39; pool stable at cap
13:2799,596 MB5,880266 msServer self-managing at cap
14:1799,778 MB8,868253 msPLE 6x the morning; stable under afternoon load

And the query-level view from Query Store, before, during, and after the ramp:

Database (anonymized)Baseline (4hr avg)12:26 (ramp)13:1514:17Assessment
OLTP-A (busiest)22.0 ms50.78 ms9.29 ms14.71 msRecovered; stable under 502K afternoon executions
OLTP-B26.9 ms17.05 ms15.18 ms23.52 msNear baseline, stable
OLTP-C42.7 ms20.56 ms10.89 ms23.39 msNear baseline, stable
Reporting-D171.85 ms6.24 ms258.9 ms246.02 msStill elevated; monitor

The mechanics of why steady state stabilized are the mirror image of the ramp-up chain. Once the pool reached the cap, population reads ended and growth-driven flush pressure ended with them; the lazy writer throttled allocation at the boundary and dirty-page generation settled to the workload’s natural rate. Meanwhile the 90+ GB of now-warm cache raised the hit ratio, which is exactly what PLE climbing from 1,432 to 8,868 measures, and fewer physical reads took some load off the struggling volume, visible in write latency easing from 307 ms to 253 ms.

What did not improve: 253 to 266 ms of write latency is still more than 13x a healthy threshold, PAGEIOLATCH_SH continued accumulating (37,369 seconds across 3.68 million tasks in the afternoon window) because the heap scans run unchanged, and one reporting database remained elevated at ~246 ms. Steady state at the cap is stable, not fixed. The bottleneck did not move; the server simply stopped fighting itself while standing next to it.

The second lesson about diagnostic reports: the same AI-assisted report pipeline that misread the soft-NUMA default (section 7) also recommended an urgent revert to 48,000 MB based on the morning snapshot, then had to retract by mid-afternoon when the trend reversed. Both claims quoted real evidence; both drew conclusions before the data finished moving. A recommendation built on a filling buffer pool is a forecast, not a finding.

6Fact Check: Was the Memory Increase Justified?

The argument for it

The setting of 100,000 MB was defended with a widely circulated OS-reserve formula: reserve 1 GB of RAM for the first 4 GB, 1 GB per 4 GB from 4 to 16 GB, and 1 GB per 8 GB above 16 GB. For 128 GB of RAM that reserves roughly 18 GB, giving a ceiling near 110,000 MB. By that arithmetic, 100,000 MB is conservative. The afternoon data added a second argument after the fact: at steady state, the larger buffer pool delivered a measurable caching benefit.

The argument against it

The formula answers the wrong question. It defines the maximum safe ceiling before the OS is starved; it says nothing about what the workload requires. Microsoft’s current guidance on Microsoft Learn does not prescribe that formula. It recommends configuring an upper limit in all versions, notes that SQL Server Setup since SQL Server 2019 suggests a value based on a percentage of available system memory, and frames manual sizing as subtracting OS and other requirements from total physical memory. Sixteen days of evidence showed the workload needed about 48 GB. Committing 100,000 MB on the first morning, during business ramp-up, on a volume already logging 22-second stalls, converted an optional optimization into an unplanned stress test.

The verdict the full day supports

Both confident positions were wrong. “Raise it to 100,000 MB on day one” was wrong about timing and process: it skipped the baseline window and ran the turbulent fill during production hours. “Urgently revert to 48,000 MB” was wrong about the mechanism: it treated a self-limiting ramp as a runaway and would have discarded a warm 90 GB cache mid-afternoon, re-triggering the same turbulence in reverse for no benefit. The defensible position all along was the boring one: carry the proven cap through a baseline window, or if the cap is already raised, hold, monitor through a full day, and judge at steady state.

Better practice on a migration: carry the proven memory cap onto the new hardware, run a 24 to 48 hour baseline, and make cap changes in a quiet overnight window so the fill happens off-peak. If a larger cap is already live, evaluate it at steady state, not mid-ramp. Headroom can be granted, or reclaimed, in an hour; conclusions drawn during the fill are forecasts wearing the costume of findings.

7Terms: Physical Cores vs. Logical Processors

Two terms drive both the soft-NUMA behavior and the MAXDOP setting in this incident, and they are frequently conflated. This section defines them with no interpretation attached.

TermDefinitionValue on this instance
Physical coreA discrete hardware processing unit with its own execution engine.8
Simultaneous multithreading (SMT) / hyperthreadingA feature that splits each physical core into two logical processors, which share the core’s execution hardware and schedule two instruction streams against it.Enabled
Logical processorThe unit the operating system schedules threads onto. Equals physical cores multiplied by threads per core.16 (8 physical cores × 2)
Socket / NUMA nodeA physical CPU package. On this instance, one socket forms one hardware NUMA node.1 socket, 1 hardware NUMA node

Two documented behaviors depend on the physical core count specifically, not the logical processor count:

  • Automatic soft-NUMA. Per Microsoft Learn, SQL Server creates soft-NUMA nodes automatically when it detects more than eight physical cores per NUMA node or socket at startup, and simultaneous multithreading cores are not differentiated when counting physical cores. With 8 physical cores in 1 node, the count is not more than 8, so automatic soft-NUMA does not fire. The 16 logical processors do not change this evaluation, because the check counts physical cores, not logical ones.
  • MAXDOP alignment guidance. Common MAXDOP guidance aligns the setting to the NUMA node’s physical core count, since two logical processors on the same physical core share that core’s execution resources rather than adding independent capacity. On this instance, MAXDOP 8 equals the physical core count of the single socket; MAXDOP 16 would equal the logical processor count.

What the diagnostics confirmed directly on this instance:

-- Startup log, 2026-08-06 21:46:40
-- "SQL Server detected 1 sockets with 8 cores per socket
--  and 16 logical processors per socket, 16 total logical
--  processors; using 16 logical processors based on
--  SQL Server licensing."

SELECT cpu_count,            -- 16 (logical processors)
       hyperthread_ratio,    -- 2 (logical processors per physical core)
       socket_count,         -- 1
       cores_per_socket,     -- 8 (physical)
       numa_node_count,      -- 1
       softnuma_configuration_desc  -- OFF
FROM sys.dm_os_sys_info;

8Fact Check: Soft-NUMA and the Eight-Core Boundary

The post-upgrade diagnostics flagged soft-NUMA as a critical gap: softnuma_configuration_desc = OFF, one NUMA node, and all 16 schedulers sharing a single unpartitioned pool of memory structures. The initial incident report asserted that SQL Server should have enabled soft-NUMA automatically and that its absence was a configuration failure introduced by the upgrade.

That assertion does not survive a documentation check, and the correction matters.

What Microsoft Learn actually says

Automatic soft-NUMA fires when the Database Engine detects more than eight physical cores per NUMA node or socket at startup. Simultaneous multithreading (SMT) cores are not differentiated when counting physical cores. The r8id.4xlarge presents 1 socket with 8 physical cores, hyperthreaded to 16 logical processors. Eight is not more than eight. Automatic soft-NUMA therefore correctly did not activate, and its absence is expected behavior, not a missed default.

-- Verify the topology SQL Server sees
SELECT cpu_count,
       hyperthread_ratio,
       socket_count,
       cores_per_socket,
       numa_node_count,
       softnuma_configuration,
       softnuma_configuration_desc
FROM sys.dm_os_sys_info;

-- Scheduler distribution per node
SELECT node_id, online_scheduler_count, memory_node_id
FROM sys.dm_os_nodes
WHERE node_state_desc = 'ONLINE';

Why the concern was still legitimate

Dismissing the report entirely would also be wrong. The underlying scalability concern is real: the scheduler count doubled from 8 to 16, but the internal partitioning did not change. Free-page lists, checkpoint queues, lazy writer work, and lock manager structures are partitioned per node, and all 16 schedulers now contend on one node’s structures. The diagnostics showed LATCH_EX at 143,097 seconds cumulative over four days, and that contention grows with scheduler count and buffer pool size.

Soft-NUMA can be enabled manually on this topology as a tuning decision, using ALTER SERVER CONFIGURATION SET SOFTNUMA ON followed by a service restart. Microsoft Learn documents this statement and the restart requirement. The afternoon stability also recalibrated the urgency: with the server holding steady at the cap and no user impact, this became a planned correction for a quiet maintenance window with before-and-after measurement, not an emergency change.

The lesson for report reviewers: automated and AI-assisted diagnostic reports can be directionally right and factually wrong at the same time. The latch contention was real; the claim that a default misfired was not. Every “should have” in a diagnostic report deserves a documentation check before it becomes a change request.

9MAXDOP and Cost Threshold After a Core-Count Change

MAXDOP remained at 8 after the upgrade. On the old instance, 8 equaled the total logical processor count. On the new instance, 8 equals the physical core count of the single socket, which is a defensible alignment. The pressure to raise it to 16 should be resisted, for two reasons grounded in the captured state:

  • With soft-NUMA OFF and one node, every parallel query at DOP 8 already coordinates through a single node’s structures. Raising DOP before addressing node partitioning increases scheduler contention on exactly the structures already showing LATCH_EX pressure.
  • If soft-NUMA is later enabled and subdivides the instance into two nodes of 8 schedulers, MAXDOP 8 aligns with the node size, which is the alignment Microsoft’s MAXDOP guidance points toward.

Cost Threshold for Parallelism was still at the default of 5 on the new instance. For a workload of this size the default is too low; it promotes small queries into parallel plans and multiplies coordination overhead across 16 schedulers. A production OLTP starting point of 50, tuned from there against plan cache evidence, is the standard SQLYARD recommendation.

10What Worked: TempDB on NVMe Instance Store

One element of this migration was executed correctly and delivered exactly what it promised, all day long. TempDB moved from the EBS data volume to the local NVMe instance store that the r8id family includes:

MetricBefore (EBS)After (NVMe instance store)
TempDB write latency130–151 ms0.00 ms read / 0.07 ms write, unchanged through 13:15
All 9 TempDB filesOn shared data volumeConfirmed on local T: volume
I/O billingCharged as EBS I/OIncluded with instance

This matches AWS guidance directly: AWS Prescriptive Guidance for SQL Server on EC2 recommends placing TempDB on an instance store volume when the instance type provides one, both for performance and for cost, since instance store I/O is not billed separately. TempDB is the ideal candidate for ephemeral storage because it is rebuilt from scratch at every service start; nothing durable lives there.

The operational caveat: instance store contents are lost when the instance stops, terminates, or moves to a different host. The volume must be initialized and mounted before the SQL Server service starts, or startup fails when TempDB cannot be created. A startup task that prepares the volume, and a service dependency on that task, are mandatory parts of this design.

Verification query used to confirm placement and latency:

SELECT DB_NAME(vfs.database_id) AS database_name,
       mf.physical_name,
       vfs.num_of_reads,
       vfs.num_of_writes,
       CASE WHEN vfs.num_of_reads  = 0 THEN 0
            ELSE vfs.io_stall_read_ms  * 1.0 / vfs.num_of_reads  END AS avg_read_ms,
       CASE WHEN vfs.num_of_writes = 0 THEN 0
            ELSE vfs.io_stall_write_ms * 1.0 / vfs.num_of_writes END AS avg_write_ms
FROM sys.dm_io_virtual_file_stats(2, NULL) AS vfs
JOIN sys.master_files AS mf
  ON mf.database_id = vfs.database_id AND mf.file_id = vfs.file_id;

11The EBS Sizing Trap: Bigger Is Not Faster

The data volume was doubled from 2.44 TB to 4.88 TB during the migration, in part on the assumption that a larger volume performs better. Whether that assumption holds depends entirely on the volume type, and on gp3 it is simply false:

Volume typeIOPS behaviorEffect of doubling 2.44 TB to 4.88 TB
gp23 IOPS per GiB, max 16,000Baseline rises from ~7,500 to ~14,600 IOPS; throughput still capped at 250 MiB/s
gp3Baseline 3,000 IOPS and 125 MiB/s at any size; more must be provisioned separatelyZero performance change; capacity only
io2IOPS provisioned explicitlyWhatever is provisioned

The full-day evidence is consistent with a volume whose performance envelope did not move when its capacity did: write latency ran 253 to 307 ms across every capture, on both sides of the memory ramp. On gp3, size and performance are deliberately decoupled: that is the feature. Anyone migrating from gp2 habits must internalize that growing a gp3 volume buys space, not speed, and that IOPS and throughput are provisioned, and billed, independently.

The check that takes five minutes: before any storage-related migration decision, confirm the volume type and its provisioned IOPS and throughput in the EC2 console, then compare against the observed transfers per second and latency from sys.dm_io_virtual_file_stats. If observed demand already meets the provisioned ceiling, no amount of RAM, CPU, or capacity will move the latency number.

There is also a layout question this incident highlights. All data files and all transaction log files shared the single D: volume. Separating data and log onto separate volumes, each sized for its own I/O pattern (random for data, sequential for log), is long-standing guidance in AWS Prescriptive Guidance for SQL Server on EC2 and would have isolated log flushes from checkpoint bursts.

12The Constant: Heap Scans and Missing Indexes

Through every phase of this day, one thing never changed. The top query by impact read 525 million pages from a heap in a single execution pattern. The second-ranked query, a SELECT against a task table with no supporting index, consumed 1.63 million ms of CPU and 313 million reads in one day, and the missing-index recommendation for it had appeared in every daily health check for more than nineteen consecutive days. In the stable afternoon, with PLE at 8,868 and users happy, PAGEIOLATCH_SH was still accumulating and a 29.4-million-read heap scan was live in a session at the 13:15 capture.

A scan of a 525-million-page heap must read every page on every execution regardless of buffer pool size. A 90 GB cache can hold a larger fraction of the heap and soften repeat reads, which is part of the afternoon improvement, but it cannot eliminate the scan, cannot index it, and cannot help the first cold read of any page. This is why the I/O problem existed with identical wait signatures on the old 64 GB hardware, through the morning turbulence, and into the stable afternoon. The hardware changed twice over; the access paths never did.

  • PAGEIOLATCH_SH dominant on the old instance, the new instance at ramp, and the new instance at steady state.
  • Missing index with 53 million impact and 108,692 seeks recorded, recommended for 19+ consecutive days.
  • Heap tables at the center of the highest-traffic queries, generating forwarded-record and full-scan overhead that clustered indexes eliminate.

The actual fix: the scheduled heap-to-clustered-index conversion and the long-recommended nonclustered index. Those changes give the top queries a seek path, which removes the scan volume that no memory, topology, or instance-tier change can remove. SQLYARD covers the mechanics in depth in the SQL Server Heaps guide and the PostgreSQL Migration Trap article, which documents a 3.22-billion-forwarded-fetch production case of the same disease.

13Availability Group Fallout

The storage saturation did not stay contained to query latency. During the morning window the synchronous secondary reported synchronization_health_desc = NOT_HEALTHY, an alert active for more than nine hours at capture time, and one database seeding operation failed. HADR_SYNC_COMMIT at 1.85 million ms confirmed the primary was waiting on a secondary that could not keep up, which means the cluster ran without automatic failover protection during the highest-turbulence hours of the day.

This is the quiet cost of write-path saturation on an AG: every synchronous commit inherits the slowest link, and a ramp-up that increases flush volume on the primary propagates directly into log send and hardening delays. Replica health belongs on the same dashboard as storage latency during any infrastructure change, and its recovery belongs on the same checklist as query response times before a change is declared stable.

14The Upgrade Checklist This Day Produced

Compressed into a pre-flight and day-one discipline, the lessons of this incident form a checklist for any SQL Server EC2 tier change:

Before the change

  • Capture a full baseline: PLE trend, memory in use vs cap, wait statistics, per-file latency, top queries by impact, AG replica health.
  • Answer the constraint question honestly: is the bottleneck memory, CPU, or storage? A PLE of 800+ with zero grants pending rules out memory regardless of how slow queries feel.
  • Confirm the EBS volume types and their provisioned IOPS and throughput; compare against observed demand.
  • Resolve outstanding missing-index and heap findings first, or explicitly document why hardware is being changed while known access-path fixes wait.

Designing the target

  • Choose an instance family with NVMe instance store if TempDB latency is a complaint, and plan the initialization task and service dependency for the ephemeral volume.
  • Plan the memory cap to carry over from the proven workload value. If a larger cap is warranted, schedule the change for a quiet overnight window so the buffer pool fill happens off-peak.
  • On gp3, provision IOPS and throughput deliberately; do not assume size buys speed. Separate data and log volumes.
  • Check the new CPU topology against the soft-NUMA auto-enable rule (more than eight physical cores per node, SMT not counted) so expectations match documented behavior, and decide in advance whether manual soft-NUMA will be tested.
  • Re-evaluate MAXDOP and Cost Threshold for Parallelism against the new core count before the first business day, not after.

Day one on the new instance

  • Verify topology as SQL Server sees it: sys.dm_os_sys_info, sys.dm_os_nodes, socket and core counts in the startup log.
  • Verify TempDB file placement and latency with sys.dm_io_virtual_file_stats.
  • Distinguish ramp-up from steady state. While the buffer pool is still filling, expect elevated read I/O, growing flush volume, and degraded response times; judge the configuration only after memory-in-use flattens at the cap and PLE trends up.
  • Do not issue or accept configuration verdicts from a morning snapshot. Monitor through at least one full business cycle before committing to a cap change in either direction.
  • Watch AG synchronization health continuously; treat HADR_SYNC_COMMIT growth as a storage alarm, not a replication nuisance.
  • Hold one change at a time. A tier change plus a memory change plus a volume change on the same morning makes attribution nearly impossible; this day’s attribution was recoverable only because captures were frequent and timestamped.

15Key Takeaways

  • Sixteen days of healthy PLE at a 48 GB cap proved the memory increase was not needed; a full day of data proved it was also not the disaster the morning suggested. Both the day-one raise and the urgent revert recommendation were premature calls made from partial data.
  • The real risk window of a large cap increase is the ramp-up, while the pool fills against constrained storage. It is transient, self-limiting, and avoidable by scheduling cap changes off-peak.
  • At steady state the larger cache delivered measurable benefit: PLE 1,432 to 8,868, the busiest database back near baseline under 502K executions, write latency easing from 307 to 253 ms.
  • Stable is not fixed: write latency remained 13x over threshold and PAGEIOLATCH_SH kept accumulating, because a 525-million-page heap scan and a 19-day-old missing-index recommendation do not care how much RAM the server has.
  • Automatic soft-NUMA requires more than eight physical cores per node, with SMT cores not counted; on an 8-physical-core instance its absence is documented behavior, and enabling it manually is a planned, testable tuning step, not an emergency.
  • TempDB on NVMe instance store was the one unambiguous win, taking write latency from 130+ ms to near zero and holding there all day, exactly as AWS guidance predicts.
  • Doubling a gp3 volume changes capacity, not performance; IOPS and throughput are provisioned independently of size.
  • Diagnostic reports, human or AI-generated, deserve two checks before becoming change tickets: a documentation check on every “should have,” and a time check on every conclusion drawn while the data is still moving.

The technical information in this article was verified against Microsoft documentation at the time of publication. SQL Server features, cloud service capabilities, licensing terms, and configuration requirements can change between versions and cumulative updates. Always validate implementation details against current Microsoft Learn documentation before deploying to production. References in this article link directly to the authoritative Microsoft sources.

References


Discover more from SQLYARD

Subscribe to get the latest posts sent to your email.

Leave a Reply

Discover more from SQLYARD

Subscribe now to keep reading and get access to the full archive.

Continue reading