Forum Discussion
Query Store transaction preventing restoring database
From the SQL Server 2019 Machine. I have restored the database.
5 percent processed.
10 percent processed.
15 percent processed.
20 percent processed.
25 percent processed.
30 percent processed.
35 percent processed.
40 percent processed.
45 percent processed.
50 percent processed.
55 percent processed.
60 percent processed.
65 percent processed.
70 percent processed.
75 percent processed.
80 percent processed.
85 percent processed.
90 percent processed.
95 percent processed.
100 percent processed.
Processed 357688 pages for database 'BrokenDatabase', file 'BrokenDatabase' on file 1.
Processed 7444260 pages for database 'BrokenDatabase', file 'BrokenDatabase_log' on file 1.
Msg 829, Level 16, State 1, Line 2
Database ID 5, Page (1:47416) is marked RestorePending, which may indicate disk corruption. To recover from this state, perform a restore.
Errors occurred during recovery while rolling back a transaction. The transaction was deferred. Restore the bad page or file, and re-run recovery.
Restore was successful but deferred transactions remain. These transactions cannot be resolved because there are data that is unavailable. Either use RESTORE to make that data available or drop the filegroups if you never need this data again. Dropping the filegroup results in a defunct filegroup.
RESTORE DATABASE successfully processed 7801948 pages in 376.268 seconds (161.992 MB/sec).
When I run CheckDB the first time, I get the following error:
Msg 7929, Level 16, State 1, Line 8
Check statement aborted. Database contains deferred transactions.
I am able to get the options:
| actual_state_desc | desired_state_desc | current_storage_size_mb | max_storage_size_mb | readonly_reason | interval_length_minutes | stale_query_threshold_days | size_based_cleanup_mode_desc | query_capture_mode_desc |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| READ_WRITE | READ_WRITE | 60 | 1000 | 0 | 60 | 30 | AUTO | AUTO |
To get Query Store to turn off I have to use the Forced, but I am able to get it to turn off.
Second run of option:
| actual_state_desc | desired_state_desc | current_storage_size_mb | max_storage_size_mb | readonly_reason | interval_length_minutes | stale_query_threshold_days | size_based_cleanup_mode_desc | query_capture_mode_desc |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| OFF | OFF | 60 | 1000 | 0 | 60 | 30 | AUTO | AUTO |
Now I am running the consistency check, but that is almost immediately suspending. I can see this with the dm_exec_requests and dm_os_waiting_tasks
| session_id | status | command | wait_type | wait_time | blocking_session_id | database_id | percent_complete |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| 65 | suspended | EXECUTE | LCK_M_IX | 44539 | -3 | 5 | 6.96263 |
| session_id | wait_type | blocking_session_id | resource_description |
| :--- | :--- | :--- | :--- |
| 65 | LCK_M_IX | -3 | objectlock lockPartition=0 objid=213575799 subresource=FULL dbid=5 id=lock1774ee13380 mode=X associatedObjectId=213575799 |
after I ran the consistency, I also checked the Server Logs and see this:
Internal table access error: failed to access Query Store internal table with HRESULT: 0x80004005
This makes me wonder if I should continue with what you have provided with the checkpoint or not?
Hey, sorry for the late response. I had to think about it. Based on the latest results, I would stop at this point and not continue with CHECKPOINT yet.
The new clue is:
blocking_session_id = -3
wait_type = LCK_M_IX
object_id = 213575799
In SQL Server, blocking_session_id = -3 means that the resource is owned by a deferred recovery transaction. So sp_query_store_consistency_check is not being blocked by a normal user session that can simply be killed.
Also, we know that object_id = 213575799 is the Query Store internal table you identified earlier.
So the likely sequence is:
1. Page 1:47416 cannot be accessed.
2. Recovery cannot finish UNDO for a transaction.
3. The transaction becomes DEFERRED.
4. That recovery transaction keeps locks on Query Store internal data.
5. sp_query_store_consistency_check requests an IX lock.
6. It waits on LCK_M_IX with blocker -3.
So at this point I would treat the Query Store problem as a consequence of the deferred recovery transaction, not necessarily as the original cause.
So here is a plan:
1. First check the affected page
Use this query:
USE master;
GO
SELECT
database_id,
file_id,
page_id,
event_type,
error_count,
last_update_date
FROM msdb.dbo.suspect_pages
WHERE database_id = DB_ID(N'YourDatabaseName');
GOThen try to identify page 1:47416
DBCC TRACEON(3604);
GO
DBCC PAGE (N'YourDatabaseName', 1, 47416, 3);
GODBCC PAGE might fail because the page is already marked RestorePending, but the result is still useful if SQL Server can return any metadata. If you are on a version where sys.dm_db_page_info is available, I would also try:
SELECT *FROM sys.dm_db_page_info
(
DB_ID(N'YourDatabaseName'),
1,
47416,
'DETAILED'
);
2. Check whether this is the only bad page
Also inspect suspect_pages for other entries. If page 1:47416 is only one of several damaged pages, restoring only this page might not be sufficient. It is also worth reviewing the SQL Server error log around the restore/recovery operation for errors such as: 823, 824, 825, 829 or 3414
This is important because the deferred transaction is normally a symptom of SQL Server being unable to access data required during rollback.
3. If you have a known-good backup and transaction log chain
This would be my preferred recovery path. If the database is using FULL or BULK_LOGGED recovery and you have the required backup/log chain, consider a page restore of page 1:47416.
The idea is:
RESTORE DATABASE [YourDatabaseName]
PAGE = '1:47416'
FROM DISK = N'X:\Backups\YourDatabase_full.bak'
WITH NORECOVERY;
GO
Then apply the necessary transaction log backups:
RESTORE LOG [YourDatabaseName]
FROM DISK = N'X:\Backups\YourDatabase_log_1.trn'
WITH NORECOVERY;
GO
RESTORE LOG [YourDatabaseName]
FROM DISK = N'X:\Backups\YourDatabase_log_2.trn'
WITH NORECOVERY;
GOAnd finish the recovery with the last required log:
RESTORE LOG [YourDatabaseName]
FROM DISK = N'X:\Backups\YourDatabase_log_last.trn'
WITH RECOVERY;
GOThe exact restore sequence depends on your backup history, so I would verify the backup chain before executing this. I cannot tell you the exact order because I don't know your backup strategy. After the page is restored and recovery can successfully complete the deferred rollback, check whether the blocker has disappeared- I mean that we have no more blocking_session_id = -3:
SELECT
session_id,
status,
command,
wait_type,
blocking_session_id,
wait_resource
FROM sys.dm_exec_requests
WHERE database_id = DB_ID(N'YourDatabaseName');4. Only after the deferred transaction is gone
Then return to Query Store:
USE [YourDatabaseName];
GO
SELECT
actual_state_desc,
desired_state_desc,
readonly_reason,
current_storage_size_mb,
max_storage_size_mb
FROM sys.database_query_store_options;
GOWith Query Store disabled, run:
EXEC sys.sp_query_store_consistency_check;
GOAt this point it should no longer be waiting on the deferred recovery transaction.
And now you can run:
DBCC CHECKDB (N'YourDatabaseName')
WITH NO_INFOMSGS, ALL_ERRORMSGS;
GOIf CHECKDB completes cleanly, I would then create a new full backup and use that new backup for the SQL Server 2025 restore.
Hopefully you have a known-good backup available and the page restore path from step 4 resolves the deferred transaction. That would be by far the cleanest outcome here
5. If you do NOT have a usable good backup
That is a different situation. Before doing any destructive repair, I would first make a copy of the MDF/LDF files and preserve the current state, and then run:
DBCC CHECKDB (N'YourDatabaseName')
WITH NO_INFOMSGS, ALL_ERRORMSGS;
GOIf it still terminates because of the deferred transaction, the fun begins. The remaining recovery option might eventually involve emergency-mode repair:
ALTER DATABASE [YourDatabaseName] SET EMERGENCY;
GO
ALTER DATABASE [YourDatabaseName]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
DBCC CHECKDB
(
N'YourDatabaseName',
REPAIR_ALLOW_DATA_LOSS
);
GOBut I would consider this a last-resort option only. REPAIR_ALLOW_DATA_LOSS can deallocate damaged pages or rebuild structures in a way that makes the database physically consistent while losing data. It is not equivalent to restoring the original data. So if a clean backup exists, restoring the affected page/database is strongly preferable.
Good luck and fingers crossed this gets you unstuck. Let me know if it resolves the issue, or at least whether we’re making progress and the symptoms change.
- MikeRM2Aug 18, 2026Tin Contributor
So I am able to do some checks:
msdb.dbo.suspect_pages returned nothing on that database.
Here is the top part of the DBCC Page, there is a lot to the memory dump, which I would take as a good thing.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
PAGE: (1:47416)
BUFFER:
BUF @0x000001783BB0F0C0
bpage = 0x000001777A2C0000 bPmmpage = 0x0000000000000000 bsort_r_nextbP = 0x000001783BB0F010
bsort_r_prevbP = 0x000001783BB0F000 bhash = 0x0000000000000000 bpageno = (1:47416)
bpart = 3 ckptGen = 0x0000000000000000 bDirtyRefCount = 0
bstat = 0x809 breferences = 3 berrcode = -6
bUse1 = 55580 bstat2 = 0x0 blog = 0x15ab215a
bsampleCount = 0 bIoCount = 0 resPoolId = 0
bcputicks = 0 bReadMicroSec = 0 bDirtyContext = 0x0000000000000000
bDbPageBroker = 0x0000000000000000 bdbid = 5 bpru = 0x000001773AE78040
PAGE HEADER:
Page @0x000001777A2C0000
m_pageId = (1:47416) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x240
m_objId (AllocUnitId.idObj) = 149 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594047692800
Metadata: PartitionId = 72057594041860096 Metadata: IndexId = 1
Metadata: ObjectId = 213575799 m_prevPage = (1:47327) m_nextPage = (1:47417)
pminlen = 577 m_slotCnt = 11 m_freeCnt = 1606
m_freeData = 6564 m_reservedCnt = 0 m_lsn = (14270:38952:2)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 1597674463 DB Frag ID = 1
Allocation Status
GAM (1:2) = ALLOCATED SGAM (1:3) = NOT ALLOCATED
PFS (1:40440) = 0x40 ALLOCATED 0_PCT_FULL DIFF (1:6) = NOT CHANGED
ML (1:7) = NOT MIN_LOGGED
Slot 0 Offset 0x60 Length 588
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 588
dm_db_page_info:
| database_id | file_id | page_id | page_header_version | page_type | page_type_desc | page_type_flag_bits | page_type_flag_bits_desc | page_flag_bits | page_flag_bits_desc | page_lsn | page_level | object_id | index_id | partition_id | alloc_unit_id | is_encrypted | has_checksum | checksum | is_iam_page | is_mixed_extent | has_ghost_records | has_version_records | pfs_page_id | pfs_is_allocated | pfs_alloc_percent | pfs_status | pfs_status_desc | gam_page_id | gam_status | gam_status_desc | sgam_page_id | sgam_status | sgam_status_desc | diff_map_page_id | diff_status | diff_status_desc | ml_map_page_id | ml_status | ml_status_desc | prev_page_file_id | prev_page_page_id | next_page_file_id | next_page_page_id | fixed_length | slot_count | ghost_rec_count | free_bytes | free_bytes_offset | reserved_bytes | reserved_bytes_by_xdes_id | xdes_id |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| 5 | 1 | 47416 | 1 | 1 | DATA_PAGE | 0x0 | | 0x240 | RestorePending \| HAS_CHECKSUM | 000037be:00009828:0002 | 0 | 213575799 | 1 | 72057594041860096 | 72057594047692800 | 0 | 1 | 1597674463 | 0 | 0 | 0 | 0 | 40440 | 1 | 0 | 0x40 | PFS_IS_ALLOCATED \| 0_PCT_FULL | 2 | 1 | ALLOCATED | 3 | 0 | NOT ALLOCATED | 6 | 0 | NOT CHANGED | 7 | 0 | NOT MIN_LOGGED | 1 | 47327 | 1 | 47417 | 577 | 11 | 0 | 1606 | 6564 | 0 | 0 | 0000:00000000 |I do not have a known-good backup / log chain, as this was the last full backup and was suspected good after a restore over the database.
DBCC CHECKDB failed again. So I put it in Emergency, Added Trace Flag 902, Shut down the instance, moved the log file to another drive, started the instance again, and ran a Rebuild Log. Then did a checkdb which showed 4 errors, ran the repair allow data loss and that actually completed. I was then able to set the database as multi user, took a backup and restored it on the SQL Server 2025 machine. Loaded the connection to the database back up and so far it seems to have all the data. I have not turned query store back on. Something I will look at in the coming days. But I still have the bad database I can play with too to determine if it is too much fun.
- MW_DEVAug 21, 2026Brass Contributor
That is a very good outcome, especially given that you did not have a known-good backup/log chain. At this point I would stop trying to repair anything else and focus entirely on validating the recovered database. The most important thing now is to confirm what REPAIR_ALLOW_DATA_LOSS actually changed. If you still have the output from that run, I would keep it and check whether DBCC repaired/deallocated only Query Store internal structures or anything belonging to application data as well.
Because page 1:47416 belongs to the Query Store internal object, there is a reasonable chance that the damage was limited to Query Store data. But I would not assume that until the repair output confirms it.
On the SQL Server 2025 copy I would run:
DBCC CHECKDB (N'YourDatabaseName') WITH NO_INFOMSGS, ALL_ERRORMSGS; GOand then:
DBCC CHECKCONSTRAINTS WITH ALL_CONSTRAINTS; GOI would also do a few application-level checks on the most important data: row counts, recent records, key totals, timestamps, or whatever makes sense for this database, because after a log rebuild and REPAIR_ALLOW_DATA_LOSS, SQL Server can restore physical consistency, but that does not automatically prove that all logical/business data is exactly as expected.
I agree also with leaving Query Store disabled for now. If the database passes CHECKDB, constraint validation and your application-level checks, then I would revisit Query Store separately. Given that the affected page belonged to Query Store internal storage, I would probably prefer clearing the old Query Store data and starting fresh rather than trying too hard to preserve its previous contents.
One thing I would also verify is trace flag 902. Microsoft documents it for bypassing database upgrade scripts during failed CU/SP upgrades, not as a general database recovery trace flag. If it is still configured as a startup parameter, I would remove it.
Since you still have the original broken database, it could be useful later for investigating exactly what happened, but I would treat that as a separate exercise now - just to satisfy your own curiosity.