SQL Server 2005 e file «sparse» su NTFS

Di recente mi hanno chiesto di sistemare un vecchio sistema che da qualche giorno non funzionava più: un database di SQL Server era in stato «suspect» e non si riusciva più ad accedervi.

La macchina era piuttosto vecchia: Windows Server 2003 a 64 bit, SQL Server 2005 a 64 bit; il tutto su ESXi. Era un sistema di produzione, con un applicativo — baan IV — in via di migrazione alla nuova versione (dopo oltre 10 anni di onorata carriera su IBM AIX e Informix e gli ultimi anni su questo Windows Server con SQL Server).

Il sistemista locale faceva il backup con Veeam, sia della intera macchina virtuale, sia dei database, ma da qualche mese il backup di SQL Server falliva, sicché quest’ultimo era stato disabilitato. Invece il backup della macchina virtuale completa girava regolarmente. Per non si sa quale motivo, il sistema si è automaticamente riavviato e SQL Server ha smesso di funzionare.

Mi soffermerò qui su alcuni brevi passaggi che hanno portato alla soluzione del problema.

Il database era in stato suspect, vale a dire che SQL Server 2005 non era riuscito a farne il recovery all’accensione dell’istanza. Se si cerca su Internet come risolvere questa cosa, la risposta è abbastanza costante e consiste nel mettere il database in stato EMERGENCY, poi fare un controllo di consistenza dei file e dei dati (con eventuale accettazione di perdita dei dati), infine mettere il database nuovamente in stato online. Ad esempio, si può fare tutto con questi comandi SQL:

EXEC sp_resetstatus 'b4c4db';
ALTER DATABASE b4c4db SET EMERGENCY;
ALTER DATABASE b4c4db SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DBCC CheckDB ('b4c4db', REPAIR_ALLOW_DATA_LOSS)
ALTER DATABASE b4c4db SET MULTI_USER;

Durante queste operazioni si possono leggere i messaggi di stato e quelli d’errore nel normale file di log di SQL Server. Durante il DBCC viene riportato il messaggio:

2021-11-05 11:28:07.81 spid2s SQL Server has encountered 1 occurrence(s) of I/O requests taking longer than 15 seconds to complete on file [G:\MSSQL\B4C4\b4c4db.mdf] in database b4c4db. The OS file handle is 0x00000000000004E8. The offset of the latest long I/O is: 0x00002076d00000

È l’ultimo messaggio che si legge, mentre normalmente ci sarebbero messaggi sulla percentuale di avanzamento del recovery.

Dal controllo dell’istanza è venuto fuori che il database era di una versione senza quasi nessuna patch, cioè la 9.0.1406. Ho quindi deciso di installare il service pack 4 di SQL Server 2005 (kb2463332-x64-enu) per vedere se questo risolvesse il problema. L’installazione non è andata a buon fine perché, dopo aver copiato sul file system il programma aggiornato, veniva avviato il nuovo SQL Server per eseguire alcuni script SQL che avrebbero dovuto aggiornare i database di sistema, ma all’avvio partiva anche il recovery, che trovava il database utente che non va e si bloccava. Fortunatamente questa nuova versione (SP4) inserisce una informazione in più nel log, che viene ripetuta ogni pochi secondi:

2021-11-05 11:28:07.81 spid2s SQL Server has encountered 1 occurrence(s) of I/O requests taking longer than 15 seconds to complete on file [G:\MSSQL\B4C4\b4c4db.mdf] in database b4c4db. The OS file handle is 0x00000000000004E8. The offset of the latest long I/O is: 0x00002076d00000
2021-11-05 11:29:28.42 spid51 The operating system returned error 1450(error not found) to SQL Server during a write at offset 0x00002076d00000 in file with handle 0x00000000000004E8. This is usually a temporary condition and the SQL Server will keep retrying the operation. If the condition persists then immediate action must be taken to correct it.

L’errore 1450 di Windows Server 2003 è «Insufficient system resources exist to complete the requested service» e, nel nostro caso, sta a indicare che SQL Server ha chiesto qualcosa al file system, ma questo non riesce a farlo. Scavando nella documentazione si scopre che durante le operazioni di backup (il famoso backup di Veeam che falliva da mesi) e di recovery (quello fatto all’avvio) SQL Server sfrutta gli snapshot per completare l’operazione. Lo snapshot va a sua volta a usare una caratteristica dei file sparse, che non esistono solo su Unix e Linux ma anche su NTFS, solo che qui viene usata una tabellina a dimensione fissa per segnare l’elenco delle zone di blocchi contigui del disco usate per lo snapshot. Se queste zone di blocchi contigui sono piccole allora, a parità di dati, il loro numero aumenta, e ouò finire per riempire la tabellina. Al che il file system genera l’errore 1450, SQL Server non riesce a terminare il backup o il recovery e tutto si blocca. La soluzione indicata da Microsoft è di usare ReFS (disponibile da Windows Server 2012) al posto di NTFS, il quale non ha questa limitazione sulla gestione dei file sparse, cioè permette di avere snapshot con un numero qualsiasi di zone di blocchi contigui.

Non avendo ReFS su Windows Server 2003, ho aggiunto un ulteriore disco alla VM, l’ho formattato con file system NTFS e cluster da 64kb, vi ho copiato i file del database di SQL Server (questa operazione ha prodotto una copia deframmentata dei file, riducendo al minimo i blocchi di settori contigui che formano i file). Ho poi cambiato le lettere del nome dei dischi in modo che il nuovo prendesse il posto del vecchio, ho riacceso SQL Server che ha fatto il recovery senza errori.

Voilà.