このエントリは以下の一連のエントリをベースにしたものです。
This entry is based on the following ones written by Mario Wolczko (Architect, Oracle Labs) and Bill Bridge (Software Architect, Oracle).
- Part 1: Introducing NVM
https://medium.com/@mwolczko/non-volatile-memory-and-java-7ba80f1e730c - Part 2: The view from software
https://medium.com/@mwolczko/non-volatile-memory-and-java-part-2-c15954c04e11 - Part 3: Benefits and challenges of Non-Volatile RAM (このエントリ)
https://medium.com/@mwolczko/non-volatile-memory-and-java-part-3-ebe305ef4bc4 - Part 4: Java and non-volatility
https://medium.com/@mwolczko/non-volatile-memory-and-java-part-4-17f7a7f78f1e
また、筆者がこのテーマで語った際のスライド、動画は以下からご覧頂けます。
PROGRAMMING LANGUAGE IMPLEMENTATION SUMMER SCHOOL 2019
The Coming Persistence Apocalypse
https://pliss2019.github.io/mario_wolczko_slides.pdf
Part 3: Benefits and challenges of Non-Volatile RAM
NVRAMには以下のような魅力があります。
- 起動時間の削減
システムやアプリケーションが再起動した後、SSDやHDのようなセカンダリストレージデータからのブロック読み込みの遅延や、インメモリデータ構造の構成を必要とせず、メモリのデータにすぐアクセスできます。ここでの課題は(障害が原因で生じた可能性があるため)再起動前のデータをそのまんま利用することではなく、最新の一貫した状態に回復することです。これについては以下をご覧ください。 - 永続性のある更新の時間短縮
現在、更新を永続性あるものにするためには、ブロックストレージに書き出す必要があります。これには少なくともシステムコールを走査してから比較的遅いメディアを待つ必要があります。 - DRAMよりも高いメモリ密度とビットあたりでのコストがより低いと、より大きなインメモリデータセットが可能になり、それに伴ってより高速かつ効率的なアプリケーションが可能になります。これはムーアの法則により、今まで予想されていた進歩に対する1回だけの効率向上と見なすことができるため、これ自身にはソフトウェアの劇的な変更は必要ありません。JEP 316では、不揮発性を利用せずにJavaヒープをNVRAMに割り当て、こうしたエコノミーを利用できるようにすることを提案しています。
JEP 316: Heap Allocation on Alternative Memory Devices
http://openjdk.java.net/jeps/316
永続化からのメリットを享受するために、以下の通りアプリケーションアーキテクチャの変更が必要です。
Volatility as both curse and cure
再起動後のデータ再読み込みで発生する遅延は望ましくないため、この遅延を除去すると可用性がすぐに向上することは明らかですが、データの再読み込みが重要なメリットをもらたすのはあまり明確ではありません。このメリットの損失は容認できないため、他の方法で実現する必要があります。
Restarts are used to work around memory corruption bugs
メモリ上の情報を完全に消去し、セカンダリストレージから再起動するというのは、大規模なバグへの万能薬です。半世紀以上のシステム設計と実装により、このアプローチが定着しました。プログラマーは、セカンダリストレージへの更新が問題ないことの確認に細心の注意を払っていますが、RAMの問題の回避する場合に、継続的な不断の運用に必要な正しさのレベルに比べて(プロセスやアプリケーション、ノード、システム全体の場合もあるかもしれませんが)再起動を選択することに対してそれほど慎重ではありません。これは急場しのぎと言えるでしょう。というのも、システムは予定外の再起動(電力損失、ハードウェアエラー、オペレータエラーなど)に対処する必要があるためです。では、破損したデータのクリアにあたり同じ仕組みを使ってもよいのではないでしょうか。
(特に「クラウド」における)スケールアウトアーキテクチャの現在の人気は、このアプローチを強化するものとして見ることができます。ノードレベルで冗長性を構築し、必要に応じて異なるハードウェアでノードを再起動することでノード障害に対処します。たとえば数年に1回だけダウンタイムが発生するノードのエンジニアリングにかかる莫大なコストは、ダウンタイムがさらに高価なシナリオ(産業プロセス制御など)でのみ負担されます。また、設計されているシステムに固有の複雑さは、現代の汎用ソフトウェアスタックに比べてはるかに小さくなっています。
Coping with hardware faults
ハードウェアは完全に信頼できるわけではなく、一定レベルの信頼性を超えてハードウェアを構築しようとするのはコストが見合いません(例えば、宇宙線によるメモリ破損から保護するには、強力なシールドが必要です)。
通常、一時的なメモリ障害により、おそらく1ビット程度の少量のデータが破損します。DRAMでは、それらは漂遊放射線が原因で引き起こされる可能性があるため、慎重に特徴付けられています。3D XPointメモリの障害特性はまだわかりません(ただし、関連技術の放射線効果を特徴付ける結果があり、高度なイミュニティ (immunity) を示唆しています。詳細は以下の論文をご覧ください)。
Emerging Memory Technologies: Recent Trends and Prospects
Shimeng Yu ; Pai-Yu Chen, 2016
https://ieeexplore.ieee.org/document/7495087
修正可能な一時的なメモリ障害は、メモリインターフェイスのエラー修正コード(ECC)ロジックによってキャッチされ、アプリケーションが問題を認識することなく、ECCビットの冗長情報を使用して修復されますが、修正不能な障害が検出された場合、障害のあるデータまたはデータが伝播する前に、ただちに再起動するのが唯一の妥当な対応です。従来のシステムでは、これはメモリ(またはメモリがOSメモリの場合はOS)を使用するプロセスを強制終了、再起動して、外部ストレージに保持されたデータを戻し、アプリケーションのデータをDRAMにリロードすることを意味します。
永続メモリの障害はつまりメモリの一部の場所が利用できなくなった、ということです。今日のエンタープライズシステムはchipkillのようなテクニックを使って冗長ハードウェアを提供、活用し、障害の起きたチップからスペアへ実行中に切り替えることをやってきました。
Chipkill
https://en.wikipedia.org/wiki/Chipkill
NVRAMを搭載したシステムでこれらの概念はどのように扱われるのでしょうか。UCSDの論文には、Optane PMMには、ウェアレベリングと不良ブロック管理のためにブロックを再マッピングするために使われるマッピングテーブルが含まれていると記載されていますし、Optaneの書き込みレイテンシはDRAMに非常に類似していると報告しているため、初期書き込みはバッファへの書き込みであり、3D-XPointメモリへの書き込みは後で行われると推測できます。
Basic Performance Measurements of the Intel Optane DC Persistent Memory Module
https://arxiv.org/abs/1903.05714
これにより、摩耗と不良ブロックのデータを調べ、最終的な配置を決定し、マッピングを更新する時間ができます。修正可能なエラーが発生したブロックはおそらく回避されます。 各DIMMには、いくつかの不良ブロックに対応するための予備容量がある可能性があります。ただし、Cascade Lakeの詳細なドキュメントを確認するまで、修正不可能なエラーがどのように報告されるか、どのような緩和策が提供されるかはわかりません。効果的な回復システムを設計するには、詳細が必要です。
いずれにせよ、エンタープライズシステムには、修正不可能な一時的な障害と永続的な障害の両方に対処する戦略が必要です。 1つの戦略は、重要なデータのコピーをセカンダリストレージに保持することです。これは現在のDRAMベースのシステムで行われる必要があるものと同様です。NVRAMのデータが失われた場合、状態はコピーから再構築されます。これには、ブロックストレージからのデータの読み取り、またはログの再生が含まれます。このアプローチでは、アプリケーション開発者は、外部データを保持するすべてのコードを削除することはできず、インメモリ構造だけに依存します。このアプローチではセカンダリコピーを管理するコードがすでに存在しているため、NVRAMの既存のアプリケーションを強化するときに魅力的です。 ただし、セカンダリストレージのコピーへの永続的な更新の待ち時間が長くなるという欠点があります。
別の方法としては、NV-DIMMに対し、RAIDのようなアプローチ(ミラーリングなど)を使用する方法があります。ブロックストレージにバックアップするよりも設計は簡単ですが、ハードウェア面で経済的ではありません。この機能はマネージド言語ランタイム(たとえば、各書き込みを異なるNV-DIMMに複製する)でサポートされ、追加のハードウェアサポートやアプリケーション開発者への負担はありませんが、パフォーマンスは多少低下します。これは、セカンダリストレージでコピーを管理するすべてのコードを記述する必要がなくなるため、新しいアプリケーションにとっては魅力的ですが、ノード障害に対処しません。 2つのコピーはまだ同じ障害ドメインにあるためです。
さらに別のアプローチは、ホットスペアとして使用される別のノードでデータの並列コピーを維持することです。この場合、システムアーキテクチャは、アップデートをスペアに伝達する必要があります。現在、このアプローチはハードウェア面でさらに高価であり、複雑なソフトウェアとネットワークを横断するパフォーマンスコストが必要ですが、障害に対する追加の保護を提供し、高可用性を実現します。Gen-Z Consortiumがリモートデータおよびデバイスへのメモリセマンティックアクセスを提供する相互接続を開発しています。
Gen-Z Consortium
https://genzconsortium.org/
中間的なソリューションは、NV-DIMMを外部ストレージに定期的にバックアップする、というものです。バックアップの一貫性を確保するために、休止期間をスケジュールする必要がある場合があります。 アプリケーションと連携して設計されたバックアッププロセスで、共有メモリの同時実行技術を使用してアップデートを調整すると、この休止期間が非常に短くなる可能性があります(コンカレント・ガベージコレクションと並行します)。バックアップ間で行われた重要な変更を記録するには、更新ログが必要になる場合があります。ミラーへの切り替えよりも復旧に時間がかかるでしょう。
特定のシステムでは、ハードウェアコスト、追加ソフトウェアの複雑さ、信頼性、システムの可用性の間でトレードオフが必要になります。上記のアプローチには、それぞれ長所と短所があります。
Coping with software faults
永続メモリが存在する場合のソフトウェア障害の軽減は、非常に厄介な問題です。(その用語の最も広い意味での)メモリ破損を引き起こすソフトウェア障害は、ストレージの破損を引き起こす障害よりも非常によく知られています。この障害をどのように断言できますか? シンプルに言って、一時的な望ましくない動作を修正するために、一定の規則でアプリケーションまたはマシンを再起動することを誰もが期待しています。 前者(DRAM)は後者(セカンダリストレージ)の状態で再初期化されるため、単純な再起動によって問題が修正されると、ソースがセカンダリストレージではなくDRAMにあることが強く証明されます。
NVRAMの導入でバグ率の大幅な削減につながるとは考えていませんが、業界では、不正なメモリ状態をもたらすバグの防止、検出、修正に役立つツールと技術にさらに投資することが賢明と思われます。体系的なアプローチには、より大きな見返りが期待されます。たとえば、アプリケーションプログラマが特定の種類のメモリ破損を表現することさえできないメモリセーフな言語の使用は、即座に訴えかけるものがありますし、Javaはまさにこの陣営に入ります。特に有害な問題の原因は、バグを悪用してメモリを破損するマルウェアです。永続メモリの導入により、マルウェアが到達しやすい攻撃対象領域が増加し、長期データの破損が容易になるため、到達範囲が劇的に広がります。
Data structure evolution currently requires restarts
再起動する別の理由として、ソフトウェアの進化を処理することです。新しいバージョンでは、ブロックストレージからデータをロードするときに、データのメモリ内表現を変更できます。これらのデータが永続的であり、メモリ不足の正規コピーから構築されていない場合、ソフトウェアの変更に応じて表現を進化させる方法が必要です。この問題は、特定のコンテキスト(Javaの動的コード進化、WürthingerなどによるJavaの動的コード進化またはBiermanなどによるUpgradeJ:クラスアップグレードのためのインクリメンタルタイプチェックを参照)で注目されていますが、一般的な場合は難しく、未解決の問題であり、限定された場合でもそれほど使用されていません。業界全体で協調し、基礎となる技術を開発し、それらをソフトウェア開発およびデプロイのプラクティスに統合し、スタッフを再教育する必要があります。
Dynamic code evolution for Java
Thomas Würthinger, Christian Wimmer, Lukas Stadler
https://doi.org/10.1145/1852761.1852764
UpgradeJ: Incremental Typechecking for Class Upgrades
Gavin BiermanMatthew ParkinsonJames Noble
https://doi.org/10.1007/978-3-540-70592-5_11
Restarts from secondary state will still be required
これらの問題が解決されるまで(最後のは長時間かかることでしょう)外部の標準状態から再起動したり、バックアップのリストアが必要になる可能性があります。
再起動後、いつNVMのデータを再利用できるのでしょうか?
- 再起動が改訂されたデータ構造の展開である場合、古い構造が適切に再構築された後にのみ再利用できます。それ以外の場合は、新しい構造をセカンダリストレージから読み込む必要があります。
- 再起動が障害によるものではなく、スケジュールされ、システムが正常にシャットダウンされた場合、データは再利用できます。
- 再起動が修正不可能なエラーによって引き起こされた場合、影響を受けたデータは(たとえば、バックアップを復元したりログを再生したりして)既知の正常な状態に戻す必要があります。
- 他のすべての障害については、セカンダリストレージからデータをリロードするか、NVMのデータを既知の一貫性のある状態に戻すしくみを提供します(これがオプションの場合、リロードに必要な時間よりも短い時間で可能です)。これには以下が必要です。つまるところ、コミット、ロギング、リカバリを備えたトランザクションシステムである必要があります。
- ソフトウェアはいつNVMが一貫性のある状態なのかを明示的に示すこと
- アップデートが記録されていること
- 障害後の再起動中に、ランタイムが最後の一貫性のある状態の後に行われたアップデートを取り消すこと