Windows Storage Offloaded Data Transfer (ODX) und ein Prefetch-Rätsel Wenn SSD-Daten zu langsam gelesen werden

Ein Gastbeitrag von Thomas Rothschilds*

Im folgenden Beitrag beschreiben wir, wie ein Kunde beim Vergleich von Standard-READ/WRITE mit ODX eine Leistung feststellte, die langsamer als erwartet war – und wie unser Entwicklungsteam die Metriken, den schnellen Release-Zyklus sowie die nahtlose Upgrade-Erfahrung von Qumulo Core nutzte, um das Problem zu lösen.

Als bei einem Kunden die ODX-Leistung des Speichersystems langsamer als erwartet war, ging man bei Qumulo auf Fehlersuche.
Als bei einem Kunden die ODX-Leistung des Speichersystems langsamer als erwartet war, ging man bei Qumulo auf Fehlersuche.
(Bild: gemeinfrei / Pixabay )

Was ist ODX (Windows Storage Offloaded Data Transfer)?

Windows Storage Offloaded Data Transfer (ODX) ist ein Teil des SMB-Protokolls (Server Message Block), mit dem das Qumulo-Dateisystem angewiesen werden kann, einen Bestandteil einer Datei in eine andere zu kopieren, ohne die Daten dabei über den Client-Rechner senden zu müssen. ODX verbessert die Kopierleistung ähnlich wie die serverseitige Kopie (SSC). Es eliminiert die Netzwerklatenz zwischen Client und Server und ermöglicht es auf diese Weise Qumulo Core, sowohl IO-Größen als auch die Pufferung zu optimieren. Beim Vergleich von ODX und SSC zeigen unsere internen Testergebnisse, dass ODX siebenmal und SSC viermal schneller ist als die typische READ/WRITE-Sequenz, die beim Kopieren einer Datei an einen anderen Ort auf demselben Server auftritt.

Bildergalerie
Bildergalerie mit 10 Bildern

Mysterium der Lese-Latenzzeit

Im Jahr 2021 hat Qumulo ODX-Unterstützung für Qumulo Core Version 3.0.0 (und höher) ausgeliefert. Kurz nach der Auslieferung meldete ein Kunde, dass die ODX-Leistung langsamer als erwartet war.

Da wir wissen, dass ODX aus einer Datei liest und in eine andere schreibt, vermuteten wir zunächst, dass das Problem in der Schreibleistung liegt. Normalerweise dauern Lesevorgänge maximal ein paar Millisekunden (dank SSD-Caching und Prefetching). Als wir uns jedoch die Leselatenz ansahen, stellten wir fest, dass sie einen größeren Anteil an der ODX-Latenz einnimmt, als wir erwarten würden – in der Größenordnung von circa 25 Millisekunden, mit höheren Spitzen von 40 oder 50 Millisekunden. Was hat es damit auf sich? Grafik 1 zeigt eine Visualisierung der Lebensdauer der Lesephase einer ODX-Operation.

In der Folge haben wir uns unsere Zeitreihendaten mit geringer Granularität angesehen, die wir über unsere Cloud-Überwachungspipeline sammeln. Im Durchschnitt sahen unsere RAM-Cache-Trefferrate sowie die Latenzmetriken gut aus. Aber warum waren dann die ODX-spezifischen Lesevorgänge in der Regel so langsam?

Der erste Anhaltspunkt

Um der Ursache auf die Spur zu kommen, brauchten wir Metriken mit höherer Granularität, die spezifisch für eine einzelne ODX-Workload auf diesem Cluster waren – ohne das Rauschen anderer Operationen. Glücklicherweise verfügen wir über ein Tool namens „Trigger“, das wir intern entwickelt haben und das bei Bedarf diese Art von verteilten Leistungsdaten sammelt. Zusammen mit unserem Kunden haben wir ein Profil einiger spezifischer ODX-Sequenzen erstellt – und dabei Trigger verwendet, um Daten während dieser Sequenzen zu sammeln. Wir haben dabei Zusammenhänge erkannt und verstanden.

Bei grundsätzlich jedem Lesevorgang befanden sich die meisten Blöcke im RAM-Cache, aber es fehlten ein paar Blöcke. In dieser Situation muss in der Folge die Dateisystemtransaktion die Daten direkt von den jeweiligen Speichergeräten lesen. Im hier beschriebenen Fall zeigten uns die Metriken, dass sich die Daten auf rotierenden Festplatten befanden.

Anmerkung: Insgeheim vermutete ich, dass es die Spinning-Disks sein müssen. Es sind immer die Spinning-Disks! Ich hätte allerdings wissen müssen, dass nur die Suchvorgänge die Latenzzeit auf 50 Millisekunden ansteigen lassen können. Drehende Festplatten und ich haben nicht das allerbeste Verhältnis. Sie haben die ärgerliche, dabei zugegebenermaßen durchaus nützliche Angewohnheit, unbeabsichtigtes Verhalten in leistungssensiblen Codepfaden deutlich offenzulegen.

Spinning-Disks können Daten effizient streamen, wenn die Anfragen in einer zusammenhängenden Reihenfolge in die Warteschlange gestellt werden; sie beginnen am Anfang einer Datei und lesen diese bis zum jeweiligen Ende. Im vorliegenden Fall fehlten jedoch nur wenige Blöcke aus dem Cache pro ODX-Lesevorgang, was sich auf der Spinning-Disk als eine Menge unzusammenhängender Lesevorgänge bemerkbar machte: schlechte Nachrichten für die Latenzzeit der Spinning-Disk!

Normalerweise kommt es bei Lesevorgängen im Dateisystem nicht zu dieser Art von Latenz auf der rotierenden Festplatte, weil wir einen effektiven Prefetcher haben. Der Prefetcher errät, was als nächstes gelesen werden soll, und lädt es in weiser Voraussicht vorzeitig in den Cache. Der Prefetcher hat ein IO-Muster, das für das Lesen von rotierenden Festplatten optimiert ist.

Jetzt Newsletter abonnieren

Täglich die wichtigsten Infos zu Data-Storage und -Management

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

Warum hat der Prefetcher versagt?

Unser Prefetcher (Grafiken 2 bis 5) verfügt über Metriken, die zeigen, wie erfolgreich er im Durchschnitt ist. Diese Metriken sagten uns, dass alles, was im Prefetch war, auch gelesen wurde – aber einige Daten, die das Dateisystem als gelesen anzeigte, wurden dennoch nicht miteinbezogen. Das war eigenartig. Unsere Vermutung war: Vielleicht lesen die ODX-Operationen nicht in der korrekten Reihenfolge, sondern beliebig? Der Prefetcher kommt mit zufälligem IO nicht gut zurecht.

Bildergalerie
Bildergalerie mit 10 Bildern

Wir haben in der Folge die Operationsprotokolle überprüft, um das IO-Muster zu sehen. Operationsprotokolle sind Textprotokolle, die uns Informationen über jede Operation liefern, wie etwa Offset, Größe, Cache-Treffer (oder entsprechend auch Cache-Fehlversuch). Womit wir allerdings nicht gerechnet hatten: dass wir die ODX-Lesegrößen und -Offsets in unseren Operationsprotokollen benötigen würden. Deshalb also war dieser Code nicht eingebunden worden! Folglich mussten wir noch etwas mehr Code schreiben und parallel dazu auf ein Upgrade warten.

Finde den Fehler!

Innerhalb weniger Wochen hatten wir neuen Code ausgeliefert, und mit Hilfe unserer großartigen Upgrades war der Kunde in der Lage, sein Qumulo-Speichersystem kurz darauf auf die neueste Version zu aktualisieren. Die Test-Workloads wurden dann entsprechend erneut ausgeführt. Mit der verbesserten Betriebsprotokollierung kamen wir einem weiteren Rätsel auf die Schliche: Beim Lesen der Operationsprotokolle (ich fühlte mich wie in einer Matrix, als ich die grünen Zahlen vorbeiziehen sah) fanden wir heraus, dass unser ODX-Protokollcode dem Dateisystem zusammenhängende Leseanfragen für jeweils 8.650.752 Bytes schickte. Nachrechnen ergab: 8 Megabyte + 256 Kilobyte. Aber das Betriebsprotokoll zeigte, dass jede ODX-Anforderung tatsächlich nur 8.388.608 Bytes aus dem Cache holte (exakt 8 Mebibytes).

Ich traute meinen Augen nicht – hatte ich die Zahlen falsch gelesen? Wie konnte es sein, dass bei jeder Anfrage nur 256 KiB der 8 MiB, die angefordert wurden, im Cache fehlen? Wie konnte es sein, dass der Prefetcher nur eine Teilmenge jeder Anfrage abrief? Das schien unerklärlich.

Anmerkung: Jeder Fehler beginnt mit einer Verneinung – mit dem Glauben, dass die Daten falsch seien. Glücklicherweise war dies nicht mein erstes Problemszenario. Ich ging nicht den Weg der Verneinung, sondern fragte mich stattdessen: „Wie kann das möglich sein?“

Ich begann meinem Kollegen, der mit mir an dieser Untersuchung arbeitete, zu erklären, wie der Prefetcher funktioniert. Der Prefetcher soll dem Lese-Workload zuvorkommen, so dass die IO-Größe des Lese-Workloads in der Folge keine Rolle spielt. Der Prefetcher holt die Daten immer in großen Blöcken und in einer Art und Weise, dass die Warteschlangen auf den rotierenden Festplatten gut geordnet bleiben, um Suchvorgänge zu vermeiden. Und dann fiel es mir wie Schuppen von den Augen! Große Blöcke? Wie groß sind diese Prefetch-Blöcke wirklich? Aha! Sie ahnen es? Genau. 8 MiB. Und wie kommt es dann zu einem Vorsprung vor dem Lese-Workload? Wir starten einen Prefetch pro Leseanforderung.

Betrachten wir diese beiden Fakten einmal zusammen: Wenn wir nur einen einzigen Prefetch pro Leseanforderung starten und die Prefetch-Chunk-Größe maximal 8 MiB beträgt, und darüber hinaus der Leser Chunks von 8 MiB + 256 KiB liest – dann, ja dann wird der Prefetcher offensichtlich unter keinen Umständen einen Vorsprung haben. Er wird immer die jeweils nächsten 8 MiB im Prefetch halten und einen Rattenschwanz von 256 KiB ungehostet lassen. Wow! Wir hatten die Erklärung gefunden (Grafiken 6 bis 10).

Bildergalerie
Bildergalerie mit 10 Bildern

Das Mysterium im Detail erklärt

Die Entdeckung des Fehlers stellte aber nur den Anfang dar. Es standen sofort drei Anschlussfragen im Raum:

1. Weshalb tritt das Problem nicht bei Standard-Leseleistungstests des Dateisystems auf?

Unser Dateisystemprotokoll-Scheduler verarbeitet alle Protokollanfragen. Die typische Größe einer Protokollanforderung stellt ein MiB dar. Wenn wir Protokoll-Leseanfragen erhalten, verarbeiten wir sie und kombinieren dabei unter Umständen Anfragen, um die Leseleistung zu optimieren. Natürlich verfügen wir über eine maximale, kombinierte IO-Größe mit Blick auf diese Dateisystemanforderungen. Wenn man den IO-Wert zu groß werden lässt, kann die Anforderung durch eine große Single-Thread-Aktivität (beispielsweise CPU, Speicherzuweisung) in einen Engpass geraten. Unser Dateisystem-Scheduler legt daher die maximale kombinierte Leseanforderung auf exakt 8 MiB fest, was der maximalen IO-Größe unseres Prefetcher entspricht. Das war so geplant. In diesen Workloads holt der Prefetcher also alles vor, obwohl er nie mehr als 8 MiB vorwegnimmt, wenn die kombinierte IO-Größe ebenfalls 8 MiB beträgt.

2. Weshalb verwendet ODX eine IO-Größe von 8 MiB + 256 KiB?

Die ODX-Operation selbst durchläuft zwar den Scheduler, stellt aber ihre interne Leseanforderung nicht durch den Scheduler. ODX legte dann die maximale IO-Lesegröße fest, die es unabhängig vom Scheduler anfordern würde.

Wir haben festgestellt, dass die maximale IO-Größe des Schedulers und die maximale IO-Größe von ODX unterschiedliche Funktionen verwenden:

fs_target_transaction_size()fs_max_transaction_size()

Der Scheduler verwendete „fs_target_transaction_size“, was ein Minimum von „fs_max_transaction_size“ und 8 MiB ausmachte! Der ODX-Code hingegen verwendete intuitiv „fs_max_transaction_size“, das berechnet wurde und sich als 8 MiB + 256KiB offenbarte.

Hier also lag die Ursache. Wir änderten den ODX-Protokollcode dahingehend, dass er „fs_target_transaction_size“ verwendet, und voilà! Die nicht zwischengespeicherten Enden bei den Leseanforderungen traten nicht mehr auf.

3. Weshalb haben unsere ODX-Leistungstests dies nicht erkannt?

Unsere ODX-Leistungstests zeigten in der Tat das Verhalten nicht zwischengespeicherter Tails. Aber insgesamt war die Leistung passabel, weil in diesem Leistungstest die Daten klein genug waren, um in den SSD-Cache zu passen.

Die Latenzzeit bei Lesevorgängen von SSDs ist viel geringer als bei Festplatten. Es war also nicht so offensichtlich, dass der Prefetcher etwas falsch gemacht hat. Der Test überprüfte nicht explizit die Metriken des Prefetchers.

Als wir den Leistungstest so anpassten, dass in einem zweiten Schritt die Daten von der SSD ausgelagert und dann von der Festplatte zurückgelesen wurden, war die Topline-Leistung deutlich schlechter, als sie hätte sein sollen – der Fehler sprang entsprechend ins Auge. Wir haben den Fehler behoben, die Optimierung dieses automatisierten Leistungstests beobachtet und dann innerhalb von 14 Tagen ausgeliefert. Dank Qumulos grundsätzlich sehr einfachen Upgrades konnte der Kunde sehr rasch die verbesserte ODX-Leistung nutzen.

*Der Autor: Thomas Rothschilds, Software-Entwickler, Qumulo.

Aktuelles eBook

Speichertechnologien im Vergleich

HDD, Flash & Hybrid: Der passende Speicher für jeden Einsatz

eBook HDD, Flash und Hybrid
eBook „HDD, Flash & Hybrid“
(Bild: Storage-Insider)

In diesem eBook lesen Sie, welche verschiedenen Speicher es gibt und welche Vor- und Nachteile diese haben.

Die Themen im Überblick:

  • Der passende Speicher für jeden Einsatz – HDD, SSD und Tape
  • HDDs: Heimvorteil und neue Technologien
  • Flash-SSDs & Co.: wie ein geölter Blitz
  • Storage-Arrays unter die Haube geschaut – SSD, HDD und Hybrid
  • Tape: die Kunst der Langzeitarchivierung

(ID:48206124)