TL; DR: se il kernel Linux perde una scrittura I / O bufferizzata , c'è modo che l'applicazione lo scopra?
So che devi conservare fsync()
il file (e la sua directory principale) per durare nel tempo . La domanda è se il kernel perde buffer sporchi in attesa di scrittura a causa di un errore I / O, come può l'applicazione rilevarlo e ripristinarlo o interromperlo?
Pensa alle applicazioni di database, ecc., Dove l'ordine di scrittura e la durata della scrittura possono essere cruciali.
Scritte perse? Come?
Il layer di blocchi del kernel Linux in alcune circostanze può perdere richieste di I / O bufferizzate che sono state inviate correttamente da write()
, pwrite()
ecc., Con un errore come:
Buffer I/O error on device dm-0, logical block 12345
lost page write due to I/O error on dm-0
(Vedi end_buffer_write_sync(...)
e end_buffer_async_write(...)
dentrofs/buffer.c
).
Nei kernel più recenti l'errore conterrà invece "scrittura asincrona persa della pagina" , come:
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
Dato che l'applicazione write()
sarà già tornata senza errori, non sembra esserci modo di riportare un errore all'applicazione.
Li stai rilevando?
Non ho molta familiarità con i sorgenti del kernel, ma penso che sia impostato AS_EIO
sul buffer che non è stato scritto se sta eseguendo una scrittura asincrona:
set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh);
SetPageError(page);
ma non mi è chiaro se o come l'applicazione possa scoprirlo quando in seguito fsync()
è il file per confermare che è sul disco.
Sembra che wait_on_page_writeback_range(...)
inmm/filemap.c
potenza per mezzo do_sync_mapping_range(...)
infs/sync.c
cui è il turno chiamato da sys_sync_file_range(...)
. Restituisce -EIO
se non è possibile scrivere uno o più buffer.
Se, come suppongo, questo si propaga al fsync()
risultato, allora se l'app va in panico e si blocca se riceve un errore I / O fsync()
e sa come fare di nuovo il suo lavoro al riavvio, dovrebbe essere una protezione sufficiente?
Presumibilmente non c'è modo per l'app di sapere quale offset di byte in un file corrisponde alle pagine perse, quindi può riscriverle se sa come, ma se l'app ripete tutto il suo lavoro in sospeso dall'ultimo successo fsync()
del file e che riscrive eventuali buffer del kernel sporchi corrispondenti a scritture perse sul file, che dovrebbero cancellare eventuali flag di errore I / O sulle pagine perse e consentire il fsync()
completamento del successivo - giusto?
Vi sono quindi altre circostanze, innocue, in cui fsync()
potrebbe tornare -EIO
dove salvare e rifare il lavoro sarebbe troppo drastico?
Perché?
Naturalmente tali errori non dovrebbero accadere. In questo caso l'errore è nato da una sfortunata interazione tra ildm-multipath
impostazioni predefinite del driver e il codice di rilevamento utilizzato dalla SAN per segnalare un errore nell'allocazione dell'archiviazione con thin provisioning. Ma questa non è l'unica circostanza in cui possono accadere: ne ho anche visto delle notizie da LVM con thin provisioning, come quelle usate da libvirt, Docker e altro. Un'applicazione critica come un database dovrebbe cercare di far fronte a tali errori, piuttosto che continuare ciecamente come se tutto andasse bene.
Se la kernel pensa che sia OK perdere scritture senza morire con il panico del kernel, le applicazioni devono trovare un modo per far fronte.
L'impatto pratico è che ho trovato un caso in cui un problema multipath con una SAN ha causato scritture perse che si sono verificate causando la corruzione del database perché il DBMS non sapeva che le sue scritture erano fallite. Non è divertente.