[PYTHON] So schreiben Sie eine Datei, bei der Sie in allen Sprachen vorsichtig sein sollten

Was ich sagen will

Beim Schreiben wichtiger Dateien muss ein unerwartetes Herunterfahren des Betriebssystems usw. berücksichtigt werden. Wenn Sie nicht wissen, wie es geht, werden halbfertige und leere Dateien generiert, die beim Systemstart und im verknüpften System schwerwiegend sind.

Als Beispiel wird die Sprache C / Java / Python / JavaScript (node.js) angegeben, es ist jedoch erforderlich, Maßnahmen in fast allen Sprachen zu ergreifen.

Hintergrund

Es ist ein schwerwiegendes Problem aufgetreten, bei dem die Software in der Produktion nicht gestartet wurde.

Als ich Protokolle und Konfigurationsdateien sammelte und analysierte, waren die Konfigurationsdateien vollständig beschädigt.

Die Konfigurationsdatei wird beim Start gelesen, kann jedoch nach Bedarf geschrieben werden. Wenn ich dem Code folge, stelle ich fest, dass er möglicherweise zur Hälfte geschrieben wird, wenn er während des Schreibvorgangs zwangsweise beendet wird.

Die Stromversorgung wird unterbrochen, wenn sie aufgebraucht ist, und das Timing kann sich auf wundersame Weise überlappt haben.

Als erstes habe ich es versucht

Wenn Sie es direkt in die Konfigurationsdatei schreiben, können Sie einen halbfertigen Zustand erhalten (wenn Sie beispielsweise 10 Zeichen schreiben möchten, haben Sie immer noch nur 1 Zeichen), auch wenn dies nur kurze Zeit dauert. Sobald Sie mit dem Schreiben in config.yml.tmp fertig sind und es dann in config.yml umbenennen, kann alles aktualisiert werden oder nicht oder atomar!

Ergebnis 1

Es wird mit voller Zufriedenheit veröffentlicht, aber es gibt ein Problem, dass es nicht erneut gestartet wird. Diesmal ist die Konfigurationsdatei 0 KB groß. Wenn das Schreiben der tmp-Datei fehlschlägt, wird sie nicht umbenannt, daher sollte sie im Flow niemals 0 KB groß sein. Das Flush ist explizit und das Schließen ist ordentlich.

Ich fand einen bestimmten Gegenstand, als ich die Linux-Systemprogrammierung von O'Reilly durchblätterte, die ich zur Hand hatte.

Was was··? Schreiben Sie den schmutzigen Puffer auf die Festplatte ...?

An diesem Punkt bemerke ich endlich den Fehler und die Lösung. Nein, ich habe es als Wissen gelernt, aber es kam nicht heraus.

Schmutziger Puffer

Selbst wenn der Schreibvorgang programmiert ist, wird die Datei nicht sofort geschrieben und vorübergehend im Format eines verschmutzten Puffers gespeichert.

Der Exportvorgang der Festplatte ist extrem langsam. Wenn Sie also jedes Mal exportieren, wird das Programm durcheinander gebracht (100 Mal oder mehr oder auf dieser Ebene). Um dies zu vermeiden, handelt es sich um eine Technik, die in neueren Dateisystemen immer angewendet wurde. Bei diesem Ansatz lagert der Prozess die langsame Schreibverarbeitung an das Betriebssystem aus und ermöglicht es ihm, fortzufahren. Wann das Betriebssystem auf die Festplatte schreibt, hängt vom Dateisystem ab, mit dem das Betriebssystem interagiert. In einigen Fällen kann es 10 Sekunden oder länger dauern, und die Vorlaufzeit ist mehr als ausreichend, um die Datei zu beschädigen.

Wenn das Betriebssystem aufgrund eines unerwarteten Stromausfalls in diesem Zustand ausfällt, wird eine fehlerhafte oder leere Datei erstellt, selbst wenn sie im Programmablauf geleert oder geschlossen wird. Mit anderen Worten, config.yml.tmp selbst war auf halbem Weg, und selbst wenn es in config.yml umbenannt wurde, war es auf halbem Weg.

Durch Aufrufen von fsync () wird der fehlerhafte Puffer sofort auf die Festplatte geschrieben. Dies ist abgeschlossen, bevor mit dem nächsten Schritt fortgefahren wird.

fsync () und fdatasync ()

Unter Linux besteht eine Datei aus den folgenden zwei Datentypen.

Der Inode ist das Datum und die Uhrzeit, zu der die Datei geändert wurde, oder die Daten, die bei der Verwaltung in einem Verzeichnis angezeigt werden.

fsync () schreibt beide und fdatasync () schreibt nur die Daten der Datei selbst.

Wenn die Datei nicht sehr oft aktualisiert wird oder Sie sich keine Gedanken über die Leistung machen müssen, können Sie fsync () verwenden.

Verwenden Sie fdatasync (), wenn der Inode (dh Metadaten wie die letzte Aktualisierungszeit) im schlimmsten Fall nicht aktualisiert werden muss oder wenn er häufig aktualisiert wird und die Leistung ein Problem darstellt.

Gegenmaßnahmen

Wenn Sie wichtige Dateien generieren, können Sie zwangsweise auf die Festplatte schreiben, damit Sie sicher sein können, dass es zu einem unerwarteten Herunterfahren kommt. Unter Linux ist das Herunterfahren nach dem regulären Verfahren kein Problem.

Hier sind einige spezifische Beispiele für Code. Einige Fehlerbehandlungen entfallen.

sample.c


int main() {
    const char* tmp_file_path = "./fsync_test.txt";
    FILE* fp= fopen(tmp_file_path , "w");
    int fd = fileno(fp);
    fputs("fsync() test\n", fp);
    fflush(fp);

    //Das ist der Punkt!!
    int fsync_ret = fsync(fd);

    fclose(fp);
    return fsync_ret;
}

FsyncTest.java


import java.io.File;
import java.io.FileOutputStream;

public class FsyncTest {
    public static void main(String[] args) throws Exception {
        File file = new File("./fsync.txt");
        try (FileOutputStream output = new FileOutputStream(file);) {
            output.write("Fsync() test\n".getBytes("UTF-8"));

            //Das ist der Punkt!!
            output.getFD().sync();
        }
    }
}

sample.py


import os

with open('./fsync_test.txt', 'w') as f:
    f.write('fsync() test')
    f.flush() #Schmutziger Puffer mit genau dem

    #Das ist der Punkt!!
    os.fsync(f.fileno())

fsync.js


const http = require('http');

const server = http.createServer((request, response) => {
    const fs = require('fs');
    fs.open('./fsync_test.txt', 'w', (err, fd) => {
        fs.write(fd, 'fsync() test\n', () => {
            
            //Das ist der Punkt!!
            fs.fsyncSync(fd);

            response.writeHead(200, {'Content-Type': 'text/plain'})
            response.end('Write success\n');
            fs.close(fd, ()=>{});
        })
    })
})

server.listen(9000);

Ergebnis 2

Die Datei ist nicht mehr beschädigt.

Übrigens ist die Zeit als schmutziger Puffer ziemlich lang. Je nach Dateisystem kann dies etwa 30 Sekunden dauern. Vor der Gegenmaßnahme habe ich in Windows 7 eine Datei geschrieben, sie mit einem Texteditor geöffnet, bestätigt, dass der Inhalt geschrieben wurde, nach 15 Sekunden den Netzstecker gezogen und die Datei nach dem Start beschädigt War dort. Als ich es in der CentOS 6-Umgebung ausprobierte, war das Ergebnis fast das gleiche.

Nach den Maßnahmen wurde die Gewinnschwelle nicht unmittelbar nach dem Schreiben erreicht.

Fazit

Unabhängig von der Sprache ist fsync () oder eine gleichwertige Verarbeitung beim Generieren wichtiger Dateien unerlässlich. Dies erzwingt jedoch extrem langsame Exporte synchroner Festplatten, was unter bestimmten Bedingungen schwerwiegende Auswirkungen auf die Leistung haben kann. Es ist NG, es in den dunklen Wolken zu tun, weil es sicher ist.

Ergänzung

Verwandte Themen

Sie haben in den Kommentaren einen Link angegeben, der für diesen Artikel von hoher Relevanz ist.

Firefox-Herausforderungen

https://www.atmarkit.co.jp/flinux/rensai/watch2009/watch05a.html

Dies ist ein Problem, das durch langsames fsync () verursacht wird. Es ist ein Artikel, der die Verwendung von fdatasync () um den Betrag beschleunigt, um den der Inode nicht aktualisiert wird.

PostgreSQL-Herausforderungen

https://masahikosawada.github.io/2019/02/17/PostgreSQL-fsync-issue/ Das Problem ist, dass Sie fsync () nicht erneut aufrufen können, wenn fsync () fehlschlägt. Wenn in PostgreSQL fsync () fehlschlägt, stürzen Sie die Datenbank und das Transaktionsprotokoll (WAL) ab. Es scheint, dass er eine Korrektur vorgenommen hat, um es wiederherzustellen.

Ich habe mich gefragt, ob es überhaupt fehlschlagen würde, aber es scheint, dass es in SAN und NFS leicht auftritt. Wenn Sie in einem allgemeinen System schreiben, sollten Sie die zu schreibenden Daten () behalten und von write () neu beginnen.

Unterstützung unter Windows

fsync () ist eine Linux-Bibliotheksfunktion und kann unter Windows nicht verwendet werden. In Windows kann dies mithilfe der folgenden API realisiert werden.

BOOL FlushFileBuffers(HANDLE hFile);

In Windows 7 kann dies auch durch Festlegen des gesamten Betriebssystems realisiert werden. [1] Öffnen Sie das Bedienfeld [2] Öffnen Sie den Geräte-Manager [3] Wählen Sie eine Festplatte aus dem Festplattenlaufwerk aus und öffnen Sie die Eigenschaften [4] Deaktivieren Sie auf der Registerkarte Richtlinie das Kontrollkästchen "Geräteschreibcache aktivieren".

Beachten Sie, dass diese Methode alle Vorgänge verlangsamt, nicht nur die App.

Recommended Posts

So schreiben Sie eine Datei, bei der Sie in allen Sprachen vorsichtig sein sollten
Sie müssen vorsichtig mit den Befehlen sein, die Sie jeden Tag in der Produktionsumgebung verwenden.
Zusammenfassung zum Schreiben von in gRPC verwendeten .proto-Dateien
Wie man nüchtern mit Pandas schreibt
Lesen von CSV-Dateien mit Pandas
Wie schreibe ich diesen Prozess in Perl?
Wie schreibe ich Ruby to_s in Python
So überprüfen / extrahieren Sie Dateien im RPM-Paket
So erhalten Sie die Dateien im Ordner [Python]
Wie schreibe ich ein benanntes Tupeldokument im Jahr 2020?
So schreiben Sie in Python die Verkettung von Zeichenfolgen in mehrere Zeilen
So laden Sie Dateien in Google Drive mit Google Colaboratory
[Python] So schreiben Sie eine Dokumentzeichenfolge, die PEP8 entspricht
Verwendung von Variablen in systemd Unit-Definitionsdateien
So laden Sie Dateien von Selenium of Python in Chrome herunter
Hochladen von Dateien in der generischen Klassenansicht von Django
So verweisen Sie auf statische Dateien in einem Django-Projekt
2 Möglichkeiten, alle CSV-Dateien in einem Ordner zu lesen
So richten Sie einen einfachen SMTP-Server ein, der lokal in Python getestet werden kann
[Python3] Code, der verwendet werden kann, wenn Sie die Größe von Bildern Ordner für Ordner ändern möchten
So schreiben Sie Typhinweise für Variablen, die mehrfach in einer Zeile zugewiesen werden
Zusammenfassung des Python-Implementierungs-Know-hows und Tipps, mit denen KI-Ingenieure vorsichtig sein möchten
Über __all__ in Python
So testen Sie, ob die Ausnahme in Python unittest ausgelöst wird
So schreiben Sie eine benutzerdefinierte Validierung in Django REST Framework
Eine Geschichte darüber, wie man einen relativen Pfad in Python angibt.
Python3-Verarbeitung, die in Paiza verwendbar zu sein scheint
Wie viel sollte ich schließlich einen Qiita-Artikel schreiben?
So importieren Sie Dateien in Python an eine beliebige Stelle
So schreiben Sie einen Test für die Verarbeitung mit BigQuery
Batch-Konvertierung aller XLSX-Dateien im Ordner in CSV-Dateien
So erhalten Sie alle Schlüssel und Werte im Wörterbuch
So schreiben Sie eine Meta-Klasse, die sowohl Python2 als auch Python3 unterstützt
Wie kann doi nützlich sein, wenn man fragt, wie man Code schreibt?
Ein Befehl zum Auflisten aller Dateien in der Reihenfolge des Dateinamens
Wie man eine schreckliche Bibliothek austrickst und benutzt, die global in einer Flasche aufbewahrt werden soll
Wenn Sie einen go table-gesteuerten Test in Python schreiben, ist es möglicherweise besser, subTest zu verwenden
So lösen Sie das Problem, dass die Zeit jedes Mal schief geht, wenn Sie die Stromversorgung unter Linux einschalten