ファイルシステム契約のテスト

テストの実行

通常のHadoopテスト実行では、ローカルファイルシステムを介してローカルでテストできるファイルシステムがテストされます。これは通常、file://とその基盤となるLocalFileSystem、およびHDFS MiniClusterを介したhdfs://を意味します。

ファイルシステムへの特定の構成がリモートサーバーに提供されていない限り、他のファイルシステムはスキップされます。

これらのファイルシステムバインディングは、通常hadoop-common-project/hadoop-common/src/test/resources/contract-test-options.xmlにあるXML構成ファイルで定義する必要があります。このファイルは除外されており、チェックインしないでください。

ftp://

contract-test-options.xmlでは、ファイルシステム名はプロパティfs.contract.test.fs.ftpで定義する必要があります。次に、FTPサーバーに接続するための具体的なログインオプションを指定する必要があります。

テストディレクトリへのパスも、オプションfs.contract.test.ftp.testdirで指定する必要があります。これは、操作が行われるディレクトリです。

<configuration>
  <property>
    <name>fs.contract.test.fs.ftp</name>
    <value>ftp://server1/</value>
  </property>

  <property>
    <name>fs.ftp.user.server1</name>
    <value>testuser</value>
  </property>

  <property>
    <name>fs.contract.test.ftp.testdir</name>
    <value>/home/testuser/test</value>
  </property>

  <property>
    <name>fs.ftp.password.server1</name>
    <value>secret-login</value>
  </property>
</configuration>

新しいファイルシステムのテスト

契約テストに新しいファイルシステムを追加するコアは、新しい契約クラスを追加し、テストするすべてのテストスイートに対して新しい非抽象テストクラスを作成することです。

  1. これらのテストをHadoop自体に追加しようとしないでください。ソースツリーに追加されません。テストは独自のファイルシステムソースと共に存在する必要があります。
  2. 独自のテストソースツリー(通常)のcontract下に、ファイルとテスト用のパッケージを作成します。
  3. 独自の契約実装のためにAbstractFSContractをサブクラス化します。
  4. サポートするすべてのテストスイートに対して、名前がTestとファイルシステムの名前で始まる非抽象サブクラスを作成します。例: TestHDFSRenameContract
  5. これらの非抽象クラスは、抽象メソッドcreateContract()を実装する必要があります。
  6. 特定のプロジェクトのsrc/test/resources/contract-test-options.xmlファイルで定義する必要があるファイルシステムバインディングを特定し、文書化します。
  7. テストが機能するまで実行します。

例として、ローカルファイルシステムのcreate()テストの実装を次に示します。

package org.apache.hadoop.fs.contract.localfs;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.contract.AbstractCreateContractTest;
import org.apache.hadoop.fs.contract.AbstractFSContract;

public class TestLocalCreateContract extends AbstractCreateContractTest {
  @Override
  protected AbstractFSContract createContract(Configuration conf) {
    return new LocalFSContract(conf);
  }
}

AbstractFSContractのサブクラスの標準的な実装手法は、テストリソースツリーに格納されているHadoop XML構成ファイルによって完全に駆動されることです。最適な方法は、ファイルシステムの名前(例:contract/localfs.xml)を使用して/contract下に格納することです。XMLファイルですべてのファイルシステムオプションを定義することで、ファイルシステムの動作の一覧をすぐに表示できます。

LocalFSContractはこれの特別なケースであり、実行中のOSに基づいて大文字と小文字の区別ポリシーを調整する必要があります。WindowsとOS/Xの両方で、ファイルシステムは大文字と小文字を区別しないため、ContractOptions.IS_CASE_SENSITIVEオプションをfalseに設定する必要があります。さらに、WindowsファイルシステムはUnixファイルおよびディレクトリの権限をサポートしないため、関連するフラグも設定する必要があります。これは、リソースツリーからXML契約ファイルをロードした、ロード済みの構成オプションを更新するだけで行われます。

  getConf().setBoolean(getConfKey(ContractOptions.SUPPORTS_UNIX_PERMISSIONS), false);

テストの失敗の処理

新しいFileSystemのテストケースが契約テストの1つに失敗した場合、どうすればよいでしょうか?

問題は原因によって異なります。

  1. ケース: カスタムFileSystemサブクラスが仕様を正しく実装していません。修正してください。
  2. ケース: 基になるファイルシステムがHadoopの期待に一致する方法で動作しません。理想的には、修正してください。または、例外を変換するなどして、FileSystemサブクラスで違いを隠してみてください。
  3. ケース: ファイルシステムとHadoop間の根本的なアーキテクチャの違い。例: 異なる同時実行性と整合性モデル。推奨事項: ファイルシステムがHDFSと互換性がないことを文書化し、明確にしてください。
  4. ケース: テストが仕様に一致しません。修正: テストにパッチを適用し、Hadoopにパッチを提出します。
  5. ケース: 仕様が正しくありません。基になる仕様は(いくつかの例外を除いて)HDFSです。仕様がHDFSと一致しない場合、HDFSは通常、ファイルシステムが実行する必要があるものの実際の定義であると見なされます。不一致がある場合は、hdfs-devメーリングリストで報告してください。ファイルシステムテストはHadoopのコアコードベースに存在しますが、ファイルシステム仕様とその付属のテストを所有しているのはHDFSチームであることに注意してください。

機能がサポートされていないためにテストをスキップする必要がある場合は、ContractOptionsクラスで既存の構成オプションを探します。メソッドがない場合、短期的な修正はメソッドをオーバーライドし、ContractTestUtils.skip()メッセージを使用してテストがスキップされたことをログに記録することです。このメソッドを使用すると、メッセージがログに出力され、テストランナーにテストがスキップされたことが伝えられます。これにより、問題が強調表示されます。

推奨される戦略は、スーパークラスを呼び出し、例外をキャッチし、例外クラスとエラー文字列の一部が現在の実装によって発生したものと一致することを確認することです。スーパークラスが実際に成功した場合(つまり、実装が現在行っていない方法で失敗した場合)もfail()する必要があります。これにより、テストパスが実行され続け、テストの他の失敗(おそらく回帰)が検出されます。そして、機能が実装された場合、その変更が検出されます。

長期的な解決策は、基本テストを拡張して新しいオプション機能キーを追加することです。これには、hdfs-devメーリングリストの開発者との協力が必要です。

「寛容 vs 厳格」例外

契約テストには、厳格な例外と寛容な例外の概念が含まれています。*厳格な*例外レポートとは、FileNotFoundExceptionEOFExceptionなど、IOExceptionの特定のサブクラスを使用して失敗を報告することを意味します。*寛容な*レポートとは、IOExceptionをスローすることを意味します。

ファイルシステムはより厳格な例外を発生させるべきですが、できない理由がある場合があります。寛容な例外が発生しても許可されますが、ユーザーアプリケーションでの失敗の診断が妨げられるだけです。ファイルシステムがより厳格な例外をサポートしていないことを宣言するには、オプションfs.contract.supports-strict-exceptionsをfalseに設定します。

ログインおよび認証パラメータを伴うファイルシステムのサポート

リモートファイルシステムに対するテストでは、ファイルシステムのURLを指定する必要があります。ログイン情報が必要なリモートファイルシステムに対するテストでは、ユーザー名/IDとパスワードが必要です。

これらの情報はすべて、ファイル`src/test/resources/contract-test-options.xml`に配置する必要があります。また、SCMツールを構成して、このファイルをSubversion、Git、または同等のシステムにコミットしないようにしてください。さらに、ビルドを構成して、生成される`-test`アーティファクトにこのファイルをバンドルしないようにする必要があります。Hadoopビルドでは、JARファイルから`src/test/**/*.xml`を除外することでこれを実現しています。さらに、`src/test/resources/auth-keys.xml`を作成する必要があります。これは`contract-test-options.xml`のコピーでも構いません。`AbstractFSContract`クラスは、存在する場合、このリソースファイルを読み込みます。特定のテストケースに対する特定のキーを追加できます。

例として、S3Aテストキーを以下に示します。

<configuration>
  <property>
    <name>fs.contract.test.fs.s3a</name>
    <value>s3a://tests3contract</value>
  </property>

  <property>
    <name>fs.s3a.access.key</name>
    <value>DONOTPCOMMITTHISKEYTOSCM</value>
  </property>

  <property>
    <name>fs.s3a.secret.key</name>
    <value>DONOTEVERSHARETHISSECRETKEY!</value>
  </property>
</configuration>

`AbstractBondedFSContract`は、プロパティ`fs.contract.test.fs.%s`にファイルシステムのURLが定義されていない場合、テストスイートを自動的にスキップします。ここで`%s`はファイルシステムのスキーマ名に一致します。

テストを実行する際には、デフォルトでこれらのテストではtrueである`maven.test.skip`をオフにする必要があります。これは、`mvn test -Ptests-on`のようなコマンドで実行できます。

重要:テストに合格しても互換性が保証されるわけではありません

すべてのファイルシステムコントラクトテストに合格しても、ファイルシステムを「HDFSと互換性がある」と記述できるわけではありません。テストでは、各操作の独立した機能に注目し、各アクションの前提条件と事後条件に焦点を当てています。カバーされていないコア領域には、分散システム全体での同時実行と障害の側面があります。

  • 一貫性:すべての変更はすぐに表示されますか?
  • 原子性:HDFSが原子性を持つと保証する操作は、新しいファイルシステムでも同様に原子性を持っていますか?
  • べき等性:ファイルシステムが再試行ポリシーを実装している場合、他のクライアントがファイルシステムを操作している間でもべき等性がありますか?
  • スケーラビリティ:HDFSと同じ大きさのファイル、または単一ディレクトリに同じ数のファイルをサポートしますか?
  • 耐久性:ファイルは実際に残り続けますか?そして、どのくらいの期間ですか?

ファイルシステムAPIの使用に関する特定の側面もあります。

  • `hadoop -fs` CLIとの互換性。
  • ブロックサイズポリシーが、分析作業に適したファイル分割を生成するかどうか。(例:ブロックサイズが1は仕様に合致しますが、MapReduceジョブに1バイトずつ処理するように指示するため、使用できません。)

これらの動作を確認するテストは大歓迎です。

新しいテストスイートの追加

  1. 新しいテストは、`seek()`、`rename()`、`create()`などで行われているように、操作ごとにテストクラスを分割する必要があります。これは、ファイルシステムコントラクト仕様が操作ごとに分割されている方法と一致させるためです。また、ファイルシステム実装者が一度に1つのテストスイートに取り組むことを容易にします。
  2. 新しい抽象テストスイートクラスで`AbstractFSContractTestBase`をサブクラス化します。同様に、タイトルに`Abstract`を使用してください。
  3. テストを支援するためのユーティリティクラスを多く備えた、`org.apache.hadoop.fs.contract.ContractTestUtils`を参照してください。これらを使用して、ファイルシステムの状態に関するアサーションを行い、アサーションが実際に失敗した場合に、ディレクトリ一覧や不一致ファイルのダンプなどの診断情報を追加します。
  4. ローカル、ローローカル、HDFSファイルシステムに対してテストを実行します。これらのいずれかが失敗した場合、問題の兆候です。ただし、それらには違いがあることに注意してください。
  5. コアファイルシステムがテストに合格したら、オブジェクトストアでテストを実行します。
  6. 可能な限り詳細に障害をログに記録してください。障害をデバッグする担当者は感謝するでしょう。

ルート操作テスト

一部のテストはルートファイルシステムに対して直接動作し、「/」の名前変更など、同様のアクションを実行しようとします。ルートディレクトリは「特殊」であり、特にオブジェクトストアなどの非POSIXファイルシステムでこれらをテストすることが重要です。これらのテストはネイティブファイルシステムに対して非常に破壊的な可能性があるため、注意が必要です。

  1. テストを`AbstractRootDirectoryContractTest`に追加するか、(a) タイトルに「Root」が含まれ、(b) ルートテストが無効になっている場合にテストをスキップするチェックがセットアップメソッドにある新しいテストを作成します。

      skipIfUnsupported(TEST_ROOT_TESTS_ENABLED);
    
  2. ローカルFSに対して実行するこのテストスイートの実装は提供しないでください。

スケーラビリティテスト

スケーラブルな負荷を生成するように設計されたテスト(多数の小さなファイルと少数の大きなファイルの両方を含む)は、構成可能であるように設計する必要があります。これにより、テストスイートのユーザーはファイルの数とサイズを構成できます。

オブジェクトストアでは、ディレクトリの名前変更操作は通常`O(files)*O(data)`であるのに対し、削除操作は`O(files)`です。後者は、ディレクトリのクリーンアップ操作でも時間がかかり、タイムアウトする可能性があることを意味します。すべての操作で遅延の可能性があるリモートファイルシステムに対して動作するテストを設計することが重要です。

仕様の拡張

仕様は不完全です。ファイルシステムクラスの完全なカバレッジがなく、既存の指定クラスの一部がカバーされていない可能性があります。

  1. クラス/インターフェース/メソッドの実装を見て、特にHDFSとローカルの実装を見て、それらが何をしているかを確認してください。これらは、現在行われていることのドキュメントです。
  2. POSIX API仕様を参照してください。
  3. ファイルシステムに関するトピックについて、HDFS JIRAを検索して、何が起こるはずだったのか、そして何が実際に起こっているのかを理解してください。
  4. IDEを使用して、Hadoop、HBase、スタックの他の部分でメソッドがどのように使用されているかを確認します。これは、これらが代表的なHadoopアプリケーションであると仮定していますが、少なくともアプリケーションがファイルシステムの動作をどのように期待しているかを示します。
  5. バインドされたファイルシステムクラスがどのように動作することが期待されているかを調べるために、`java.io`ソースを参照し、そのJavadocを注意深く読んでください。
  6. 何かが不明な場合は、hdfs-devリストに投稿してください。
  7. 実験としてテストを作成して、実際に何が起こっているかを明確にすることを恐れないでください。HDFSの動作を規範的なガイドとして使用してください。