Skip to content

Invoke-DbaDbShrink - Add WAIT_AT_LOW_PRIORITY support#10307

Merged
potatoqualitee merged 4 commits intodevelopmentfrom
claude/issue-10193-20260403-1505
Apr 4, 2026
Merged

Invoke-DbaDbShrink - Add WAIT_AT_LOW_PRIORITY support#10307
potatoqualitee merged 4 commits intodevelopmentfrom
claude/issue-10193-20260403-1505

Conversation

@andreasjordan
Copy link
Copy Markdown
Collaborator

Adds WAIT_AT_LOW_PRIORITY support to Invoke-DbaDbShrink by replacing the SMO $file.Shrink() calls with direct DBCC SHRINKFILE T-SQL.

New parameters:

  • -WaitAtLowPriority (switch)
  • -AbortAfterWait (Self or Blockers, default Self) — no None option as DBCC SHRINKFILE does not support it

Requires SQL Server 2022+; a version guard via Stop-Function is included for older instances.

Closes #10193

Generated with Claude Code

…FILE T-SQL

Replaces SMO $file.Shrink() calls with direct DBCC SHRINKFILE T-SQL to enable
WAIT_AT_LOW_PRIORITY support (SQL Server 2022+). Adds -WaitAtLowPriority switch
and -AbortAfterWait parameter (Self|Blockers). Includes version guard with
Stop-Function for older instances. Updates parameter validation tests.

Closes #10193

(do Invoke-DbaDbShrink)

Co-authored-by: Andreas Jordan <andreasjordan@users.noreply.github.com>
@ReeceGoding
Copy link
Copy Markdown
Contributor

ReeceGoding commented Apr 3, 2026

Haven't tested, but it looks to be pretty much what I expected. My only nitpicks are:

  • I don't know if error 49516 is really the error that gets thrown. However, the SQL documentation claims it so I suppose we can blindly trust that.
  • The test doesn't test that WAIT_AT_LOW_PRIORITY is used. Locking a database and checking the relevant DMV for a LCK_M_SCH_M_LOW_PRIORITY lock should be pretty easy? My first stupid idea would be to target a database with sp_addextendedproperty while in a transaction. The same trick would also work for a filegroup.

@andreasjordan
Copy link
Copy Markdown
Collaborator Author

@claude Test fail with:

Failed tests summary:
Path           : Invoke-DbaDbShrink/Verifying Database is shrunk/Shrinks with WaitAtLowPriority on SQL Server 2022+
Name           : It Shrinks with WaitAtLowPriority on SQL Server 2022+
Result         : Failed
Message        : StdOut:  @{ComputerName=appveyor-vm; InstanceName=SQL2022; SqlInstance=appveyor-vm\SQL2022; Database=dbatoolsci_shrinktest; Status=Dropped} | ErrorRecord: Exception calling "Query" with "1" argument(s): "An 
                 exception occurred while executing a Transact-SQL statement or batch.MODIFY FILE failed. Specified size is less than or equal to current size.
                 MODIFY FILE failed. Specified size is less than or equal to current size."
StackTrace     : 
RawErrorRecord : Exception calling "Query" with "1" argument(s): "An exception occurred while executing a Transact-SQL statement or batch.MODIFY FILE failed. Specified size is less than or equal to current size.
                 MODIFY FILE failed. Specified size is less than or equal to current size."

Also read the comment from Reece and try to implement his suggestion.

@claude
Copy link
Copy Markdown

claude bot commented Apr 4, 2026

Claude finished @andreasjordan's task in 8m 49s —— View job


Fixing test failure and implementing Reece's suggestion

  • Read current test and implementation files
  • Fix test failure (remove redundant ALTER DATABASE statements)
  • Implement Reece's suggestion (DMV lock verification test)
  • Commit and push changes

What was done

Fix: Test failure (MODIFY FILE failed. Specified size is less than or equal to current size)

The BeforeEach block already grows the database files to 16384KB. The "Shrinks with WaitAtLowPriority" test then tried to ALTER DATABASE ... MODIFY FILE ... SIZE = 16384KB — setting the same size that already existed. Removed those redundant lines (the test's BeforeEach setup is sufficient).

New test: Verifies LCK_M_SCH_M_LOW_PRIORITY appears in DMV when blocked (Reece's suggestion)

Added a test that:

  1. Starts a background job using sp_addextendedproperty in an open transaction to hold a DDL lock in the test database for ~20 seconds
  2. Starts Invoke-DbaDbShrink -WaitAtLowPriority in a separate background job
  3. After a brief delay, queries sys.dm_exec_requests for wait_type = 'LCK_M_SCH_M_LOW_PRIORITY'
  4. Asserts at least one session is waiting with that wait type
  5. Waits for both jobs to finish and cleans up

This verifies that the WITH WAIT_AT_LOW_PRIORITY clause is genuinely exercised rather than just syntactically present.
|

…ock verification

- Remove redundant ALTER DATABASE MODIFY FILE calls in WaitAtLowPriority test
  (BeforeEach already grows files to 16384KB; re-growing to same size caused failure)
- Add new test that verifies WAIT_AT_LOW_PRIORITY actually shows
  LCK_M_SCH_M_LOW_PRIORITY in sys.dm_exec_requests by holding a DDL lock
  with sp_addextendedproperty in a transaction while the shrink runs

(do Invoke-DbaDbShrink)

Co-authored-by: Andreas Jordan <andreasjordan@users.noreply.github.com>
@ReeceGoding
Copy link
Copy Markdown
Contributor

Maybe my idea was bad? The locks from adding an extended property are

<Database name="model">
  <Locks>
	<Lock request_mode="S" request_status="GRANT" request_count="1" />
  </Locks>
  <Objects>
	<Object name="sysxprops" schema_name="sys">
	  <Locks>
		<Lock resource_type="KEY" index_name="clust" request_mode="X" request_status="GRANT" request_count="1" />
		<Lock resource_type="OBJECT" request_mode="IX" request_status="GRANT" request_count="1" />
	  </Locks>
	</Object>
  </Objects>
</Database>

@ReeceGoding
Copy link
Copy Markdown
Contributor

ReeceGoding commented Apr 4, 2026

Wow. All of my ideas for testing this were bad. Look I what I just got from sp_WhoIsActive!

dd hh:mm:ss.mss : 00 00:00:03.924
session_id : 105
sql_text : <?query -- DBCC SHRINKFILE (N'modeldev' , 51) WITH WAIT_AT_LOW_PRIORITY (ABORT_AFTER_WAIT = SELF); --?>
login_name : sa
wait_info : (3915ms)LCK_M_X
CPU : 4
tempdb_allocations : 0
tempdb_current : 0
blocking_session_id : 104
reads : 960
writes : 0
physical_reads : 0
locks : <Database name="model"><Locks><Lock request_mode="S" request_status="GRANT" request_count="1" /></Locks><Objects><Object name="t" schema_name="dbo"><Locks><Lock resource_type="HOBT.BULK_OPERATION" request_mode="IX" request_status="GRANT" request_count="1" /><Lock resource_type="OBJECT" request_mode="Sch-S" request_status="GRANT" request_count="2" /><Lock resource_type="OBJECT" request_mode="X" request_status="CONVERT" request_count="1" /></Locks></Object></Objects></Database>

Perhaps a better test is to either:

  • Just grab the SQL text for whatever is running and see if WAIT_AT_LOW_PRIORITY is in those stings
  • Tell it to abort itself, wait a minute while you have a really nasty lock, and then see if the query has errored.

Getting a lock nasty enough for my second idea has proven difficult. I suggest the first idea instead.

At this point, I can't even prove to myself that Microsoft's implementation of WAIT_AT_LOW_PRIORITY works.

@andreasjordan
Copy link
Copy Markdown
Collaborator Author

@claude can you help?

@claude
Copy link
Copy Markdown

claude bot commented Apr 4, 2026

Claude finished @andreasjordan's task in 4m 39s —— View job


Fixing the WAIT_AT_LOW_PRIORITY test

  • Analyze request and gather context
  • Read current test file and implementation
  • Implement simpler SQL text verification approach (per ReeceGoding's suggestion)
  • Commit and push changes

What was changed

Replaced the "Verifies WAIT_AT_LOW_PRIORITY shows LCK_M_SCH_M_LOW_PRIORITY in DMV when blocked" test with "Verifies WAIT_AT_LOW_PRIORITY SQL syntax appears in running requests when blocked".

The problem with the old test: It checked for wait_type = 'LCK_M_SCH_M_LOW_PRIORITY' in sys.dm_exec_requests. As ReeceGoding discovered, the actual wait type when DBCC SHRINKFILE is blocked is LCK_M_X, not LCK_M_SCH_M_LOW_PRIORITY.

The fix (per ReeceGoding's suggestion to "grab the SQL text and see if WAIT_AT_LOW_PRIORITY is in those strings"):

  1. Creates a table with data so DBCC SHRINKFILE actually has pages to move (and thus needs locks)
  2. Starts a background job holding TABLOCKX, HOLDLOCK on that table — this blocks the shrink
  3. Starts the shrink with -WaitAtLowPriority in another background job
  4. Queries sys.dm_exec_requests joined with sys.dm_exec_sql_text to check if any running query's SQL text contains WAIT_AT_LOW_PRIORITY
  5. Asserts that count is > 0

This directly verifies the generated T-SQL contains the WITH WAIT_AT_LOW_PRIORITY clause, regardless of what wait type SQL Server assigns to the blocked session.
|

…tead of lock wait type

Replace the DMV lock wait type check (LCK_M_SCH_M_LOW_PRIORITY) with a SQL text
check via sys.dm_exec_requests + sys.dm_exec_sql_text. This is per ReeceGoding's
suggestion after investigating that the actual wait type during a blocked
DBCC SHRINKFILE WITH WAIT_AT_LOW_PRIORITY is LCK_M_X, not LCK_M_SCH_M_LOW_PRIORITY.

The blocker now uses TABLOCKX+HOLDLOCK on a table with data to reliably hold
a lock that forces the shrink to wait. The test verifies the generated DBCC
SHRINKFILE T-SQL contains WAIT_AT_LOW_PRIORITY by querying the DMV SQL text.

(do Invoke-DbaDbShrink)

Co-authored-by: Andreas Jordan <andreasjordan@users.noreply.github.com>
@ReeceGoding
Copy link
Copy Markdown
Contributor

I'm very surprised. I've thrown an unplanned few hours at it and I have not been able to produce a demo showing DBCC SHRINKFILE [...] WITH WAIT_AT_LOW_PRIORITY actually waiting at low priority. The documentation makes it sound like you need to get it to move an IAM page. I'm confident that I could make it happen by throwing the command at a real multi-terabyte database and waiting, but I would be very impressed if anyone can show a reproducible demo.

Regardless, I agree with the approach Claude has now taken. Assuming that the test passes, then I'm happy.

@potatoqualitee
Copy link
Copy Markdown
Member

👌🏼 thank you both!

@potatoqualitee potatoqualitee merged commit 0d4acaa into development Apr 4, 2026
14 checks passed
@potatoqualitee potatoqualitee deleted the claude/issue-10193-20260403-1505 branch April 4, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add WAIT_AT_LOW_PRIORITY support to Invoke-DbaDbShrink

3 participants