Ich wurde heute gefragt wieso es so lange dauert ein großes Verzeichnis mit vielen Dateien zu löschen, welches per NFS gemountet wurde. Angenommen ich habe die folgende Verzeichnisstruktur:

/fileserver/Vorratsdatenspeicherung/
/fileserver/Vorratsdatenspeicherung/2007/Januar/
/fileserver/Vorratsdatenspeicherung/2007/Januar/01/Server1/…
/fileserver/Vorratsdatenspeicherung/2007/Januar/01/Server2/…
[...]
/fileserver/Vorratsdatenspeicherung/2007/Januar/31/Server1/…
/fileserver/Vorratsdatenspeicherung/2007/Januar/31/Server2/…
[...]
/fileserver/Vorratsdatenspeicherung/2010/Januar/31/Server1/…
/fileserver/Vorratsdatenspeicherung/2010/Maerz/02/Server2/…

usw…

und möchte das aus gegebenen Anlass loswerden. Die Vermutung war, dass der Linux Client an den NFS Server nur den Befehl  ‘lösche das Verzeichnis “Vorratsdatenspeicherung”‘  schicken müsste, und den Rest würde der NFS Server dann automagisch tun. Ein Vorgang der innerhalb von 1-2 Sekunden also zumindest aus Client Sicht erledigt wäre. In der Realität sieht das ganze etwas anders aus, weshalb es schonmal eine Weile dauert, bis so ein großes Verzeichnis leer ist.

Warum?

Wenn ich den oben genannten Befehl (rm -rf /fileserver/Vorratsdatenspeicherung) absetze, muss der Linux Client das ersteinmal für das Linux Dateisystem übersetzen, denn der Benutzer spricht nicht direkt mit dem NFS Server, sondern über eine Dateisystem-Zwischenschicht, die im Hintergrund Dateisystem-Anfragen in NFS-Operationen umwandelt.

(Achtung: Es folgt eine abstrakte Übersicht über das, was im Hintergrund dabei passiert. Diese Übersicht ist nicht vollständig und in der Realität noch x-fach komplexer)

Der NFS Server kennt den Pfad /fileserver/Vorratsdatenspeicherung vermutlich gar nicht. Der Fileserver hat die Daten vielleicht unter /vol/datenschutzabteilung/Server/Vorratsdatenspeicherung/ liegen und muss nun mit Löschbefehlen gefüttert werden. Der Linux Client läuft also los und sagt ‘Hey Verzeichnis Vorratsdatenspeicherung/, ich lösch dich jetz!’. Das Linux Filesystem antwortet darauf und meinte “Nee, das ist ein Verzeichnis, das geht so nicht, das muss erstmal leer sein, bevor du das löschen kannst. Und dann geht der Spaß los:

- Das Verzeichnis wird geöffnet, openat(Vorratsdatenspeicherung)
- Die Verzeichnisattribute werden eingelesen, fstat()
- Die Verzeichnisinhalte werden abgefragt, getdents()

Die Ausgabe von getdents wird überprüft und oh schreck. Da sind ja noch mehr Verzeichnisse! Also geht das Spiel wieder los für das Verzeichnis Vorratsdatenspeicherung/2007/, dann für Vorratsdatenspeicherung/2007/Januar/, Vorratsdatenspeicherung/2007/Januar/01/ bis Vorratsdatenspeicherung/2007/Januar/01/Server01/ und dann meint der Server, ja, da sind momentan (z.B.) 8192 Dateien drin. Daraufhin holt sich der Linux Client die Liste der Dateien und fängt dann an nach und nach alle 8192 Dateien in dem Verzeichnis zu löschen:

unlinkat(Datei0)
unlinkat(Datei1)
unlinkat(Datei2)
[...]
unlinkat(Datei8191)

Jetzt ist das erste Unterverzeichnis leer. Dann schaut der Client zur Sicherheit mit getdents() nochmal nach ob alles weg ist und erst dann wird das erste Unterzeichnis Vorratsdatenspeicherung/2007/Januar/01/Server01/ entfernt – unlinkat(Server01, AT_REMOVEDIR).

Der Übersicht halber habe ich hier einige Systembefehle weggelassen. In der Realtität passiert da noch viel viel mehr. Bis das gesamte Verzeichnis Vorratsdatenspeicherung/ tatsächlich weg ist, wird der Linux Client mehrere zehntausende Dateisystem-Anfragen gestellt haben und bis zu dieser Stelle hab ich noch gar nicht über NFS(v3) Anfragen gesprochen.

Im Hintergrund wird jede herkömmliche Linux Filesystem-Anfrage in diesem Fall in NFS RPC Calls übersetzt und an den NFS Server übermittelt. Am Ende dieses Löschbefehls wird der NFS Server also vielleicht einige Millionen Befehle empfangen haben, bis das Verzeichnis tatsächlich auf dem Share gelöscht ist. Und genau deshalb dauert es manchmal ziemlich lang einen kleinen unscheinbaren Befehl auszuführen.

Weiterführende Doku:

Leave a Reply