org.apache.hadoop.fs.FileSystem
抽象クラスFileSystem
は、Hadoopファイルシステムにアクセスするための元のクラスです。抽象でないサブクラスは、Hadoopがサポートするすべてのファイルシステムに存在します。
このインターフェースにPathを受け取るすべての操作は、相対パスをサポートする必要があります。その場合、setWorkingDirectory()
で定義されたワーキングディレクトリを基準にして解決する必要があります。
したがって、すべてのクライアントに対して、状態コンポーネントPWDの概念も追加します。これは、クライアントの現在のワーキングディレクトリを表します。この状態への変更は、ファイルシステム自体には反映されません。クライアントのインスタンスに固有のものです。
実装上の注意: staticメソッドFileSystem get(URI uri, Configuration conf)
は、ファイルシステムクライアントクラスの既存のインスタンスを返す場合があります。このクラスは、他のスレッドでも使用されている可能性があります。Apache Hadoopに同梱されているFileSystem
の実装は、ワーキングディレクトリフィールドへのアクセスを同期しようとはしていません。
有効なFileSystemのすべての要件は、暗黙的な事前条件および事後条件と見なされます。有効なFileSystemに対するすべての操作は、同じく有効な新しいFileSystemをもたらす必要があります。
HDFSには、オプションfs.protected.directories
で宣言された保護されたディレクトリの概念があります。そのようなディレクトリまたはその親を削除または名前変更しようとすると、AccessControlException
が発生します。したがって、ルートディレクトリを削除しようとすると、保護されたディレクトリがある場合は、そのような例外が発生します。
boolean exists(Path p)
def exists(FS, p) = p in paths(FS)
boolean isDirectory(Path p)
def isDirectory(FS, p)= p in directories(FS)
boolean isFile(Path p)
def isFile(FS, p) = p in files(FS)
FileStatus getFileStatus(Path p)
パスの状態を取得します
if not exists(FS, p) : raise FileNotFoundException
result = stat: FileStatus where: if isFile(FS, p) : stat.length = len(FS.Files[p]) stat.isdir = False stat.blockSize > 0 elif isDir(FS, p) : stat.length = 0 stat.isdir = True elif isSymlink(FS, p) : stat.length = 0 stat.isdir = False stat.symlink = FS.Symlinks[p] stat.hasAcl = hasACL(FS, p) stat.isEncrypted = inEncryptionZone(FS, p) stat.isErasureCoded = isErasureCoded(FS, p)
返されたパスのFileStatus
の状態には、ACL、暗号化、およびイレイジャーコーディングに関する情報も含まれています。パスにACLがあるかどうかを確認するためにgetFileStatus(Path p).hasAcl()
をクエリできます。パスが暗号化されているかどうかを確認するためにgetFileStatus(Path p).isEncrypted()
をクエリできます。パスがイレイジャーコード化されているかどうかはgetFileStatus(Path p).isErasureCoded()
で確認できます。
YARNの分散キャッシュを使用すると、アプリケーションはJob.addCacheFile()
およびJob.addCacheArchive()
を使用して、コンテナおよびアプリケーション間でキャッシュされるパスを追加できます。キャッシュは、暗号化されていると宣言されていない限り、ワールドリーダブルリソースパスをアプリケーション間で共有可能として扱い、異なる方法でダウンロードします。
特に委任トークンを使用する場合のコンテナ起動中の失敗を回避するために、ファイルとディレクトリの両方に対してPOSIXアクセス許可を実装していないファイルシステムとオブジェクトストアは、isEncrypted()
述語に対して常にtrue
を返す必要があります。これは、FileStatus
インスタンスを作成するときにencrypted
フラグをtrueに設定することで実現できます。
msync()
クライアントのメタデータ状態をFileSystemのメタデータサービスの最新状態と同期します。
可用性の高いFileSystemでは、スタンバイサービスを読み取り専用のメタデータレプリカとして使用できます。この呼び出しは、スタンバイレプリカからの読み取りの一貫性を保証し、古い読み取りを回避するために不可欠です。
現在、HDFSに対してのみ実装されており、その他はUnsupportedOperationException
をスローするだけです。
この呼び出しは、内部的に呼び出し時のメタデータサービスの状態を記録します。これにより、任意のメタデータレプリカからの後続の読み取りの一貫性が保証されます。クライアントが記録された状態より前のメタデータの状態にアクセスすることはないことを保証します。
HDFSは、アクティブNameNodeを呼び出し、その最新のジャーナルトランザクションIDを要求することにより、HAモードでmsync()
をサポートします。詳細については、HDFSドキュメントHDFS Observer NameNodeからの整合性のある読み取りを参照してください。
Path getHomeDirectory()
関数getHomeDirectory
は、FileSystemと現在のユーザーアカウントのホームディレクトリを返します。
一部のFileSystemでは、パスは["/", "users", System.getProperty("user-name")]
です。
ただし、HDFSの場合、ユーザー名は、クライアントをHDFSで認証するために使用される資格情報から派生します。これは、ローカルユーザーアカウント名とは異なる場合があります。
呼び出し元の実際のホームディレクトリを決定するのは、FileSystemの責任です。
result = p where valid-path(FS, p)
メソッドが呼び出されたときにパスが存在する必要はありません。また、存在する場合でも、ディレクトリを指す必要はありません。ただし、コードはnot isFile(FS, getHomeDirectory())
が保持されていると仮定する傾向があるため、後続のコードが失敗する可能性があります。
FTPFileSystem
は、リモートファイルシステムからこの値をクエリし、接続の問題がある場合は、RuntimeException
またはそのサブクラスで失敗する可能性があります。操作の実行時間は制限されていません。FileStatus[] listStatus(Path path, PathFilter filter)
パスpath
の下のエントリを一覧表示します。
path
がファイルを参照し、フィルターがそれを受け入れる場合、そのファイルのFileStatus
エントリが単一要素配列で返されます。
パスがディレクトリを参照する場合、呼び出しは、フィルターによって受け入れられるすべての子パスのリストを返します。これにはディレクトリ自体は含まれません。
PathFilter
filter
は、パスpath
がフィルターの条件を満たす場合にのみaccept(path)
がtrueを返すクラスです。
パスpath
は存在する必要があります
if not exists(FS, path) : raise FileNotFoundException
if isFile(FS, path) and filter.accept(path) : result = [ getFileStatus(path) ] elif isFile(FS, path) and not filter.accept(P) : result = [] elif isDir(FS, path): result = [ getFileStatus(c) for c in children(FS, path) if filter.accepts(c) ]
暗黙的な不変条件: listStatus()
経由で取得した子のFileStatus
の内容は、同じパスへのgetFileStatus()
の呼び出しからの内容と等しくなります
forall fs in listStatus(path) : fs == getFileStatus(fs.path)
結果の順序: リストされたエントリの順序は保証されません。HDFSは現在、アルファベット順にソートされたリストを返しますが、Posixのreaddir()
やJavaのFile.listFiles()
API呼び出しは、返される値の順序を定義していません。結果に一様なソート順を必要とするアプリケーションは、自身でソートを実行する必要があります。
Nullの戻り値: 3.0.0より前のローカルファイルシステムは、アクセスエラー時にnullを返していました。これは誤りであるとみなされます。アクセスエラーが発生した場合はIOExceptionを予期してください。
listStatus()
操作が呼び出し元に戻る時点では、応答に含まれる情報が最新であるという保証はありません。詳細には、任意のディレクトリの内容、任意のファイルの属性、および提供されたパスの存在などが、最新ではない可能性があります。
ディレクトリの状態は、評価プロセス中に変更される可能性があります。
パスP
にあるエントリが作成された後、ファイルシステムに他の変更が行われる前は、listStatus(P)
はファイルを検出してそのステータスを返す必要があります。
パスP
にあるエントリが削除された後、ファイルシステムに他の変更が行われる前は、listStatus(P)
はFileNotFoundException
を発生させる必要があります。
パスP
にあるエントリが作成された後、ファイルシステムに他の変更が行われる前は、listStatus(parent(P))
の結果にgetFileStatus(P)
の値を含める必要があります。
パスP
にあるエントリが作成された後、ファイルシステムに他の変更が行われる前は、listStatus(parent(P))
の結果にgetFileStatus(P)
の値を含めてはなりません。
これは理論上の可能性ではなく、ディレクトリに数千のファイルが含まれている場合にHDFSで観察できます。
内容が次のようになっているディレクトリ"/d"
を考えます。
a part-0000001 part-0000002 ... part-9999999
ファイル数が多く、HDFSが各応答で部分的なリストを返す場合、リストlistStatus("/d")
が操作rename("/d/a","/d/z")
と同時に実行されると、結果は次のいずれかになります。
[a, part-0000001, ... , part-9999999] [part-0000001, ... , part-9999999, z] [a, part-0000001, ... , part-9999999, z] [part-0000001, ... , part-9999999]
この状況はまれに発生する可能性が高いですが、発生する可能性があります。HDFSでは、このような矛盾したビューは、多数の子を持つディレクトリをリストする場合にのみ発生する可能性が高くなります。
他のファイルシステムは、より強力な一貫性保証を持つか、より容易に矛盾したデータを返す可能性があります。
FileStatus[] listStatus(Path path)
これは、DEFAULT_FILTER.accept(path) = True
がすべてのパスに対して成り立つlistStatus(Path, DEFAULT_FILTER)
と完全に同等です。
原子性と一貫性の制約は、listStatus(Path, DEFAULT_FILTER)
の場合と同様です。
FileStatus[] listStatus(Path[] paths, PathFilter filter)
渡されたディレクトリのリストに見つかったすべてのファイルを列挙し、それぞれでlistStatus(path, filter)
を呼び出します。
listStatus(path, filter)
と同様に、結果は矛盾する可能性があります。つまり、操作中にファイルシステムの状態が変更されました。
パスが特定の順序でリストされるかどうかについての保証はありません。すべてがリストされ、リスト時に存在している必要があるだけです。
すべてのパスが存在する必要があります。一意性に関する要件はありません。
forall p in paths : exists(fs, p) else raise FileNotFoundException
結果は、パスリストで見つかったすべてのステータス要素を含み、それ以外のものは含まない配列です。
result = [listStatus(p, filter) for p in paths]
実装では、重複したエントリをマージしたり、重複したパスを認識してエントリを1回だけリストすることで操作を最適化したりすることができます。
デフォルトの実装ではリストを反復処理します。最適化は実行されません。
原子性と一貫性の制約は、listStatus(Path, PathFilter)
の場合と同様です。
RemoteIterator<FileStatus> listStatusIterator(Path p)
パスの下にあるFileStatus
エントリを列挙するイテレータを返します。これは、リスト全体を返すのではなくイテレータが返される点を除いて、listStatus(Path)
に似ています。リスト中に他の呼び出し元がディレクトリを更新しない限り、結果はlistStatus(Path)
とまったく同じです。ただし、リストの実行中に他の呼び出し元がディレクトリ内でファイルを追加/削除している場合は、原子性は保証されません。異なるファイルシステムでは、より効率的な実装を提供できる場合があります。たとえば、S3Aはページ単位でリストを作成し、ページが処理されている間に次のページを非同期でフェッチします。
初期リストが非同期になったため、バケット/パスが存在しない例外がnext()
呼び出し中に後で表示される可能性があることに注意してください。
呼び出し元は、性質上インクリメンタルであるため、listStatus
よりもlistStatusIterator
を使用することを推奨します。
FileStatus[] listStatus(Path[] paths)
渡されたディレクトリのリストに見つかったすべてのファイルを列挙し、それぞれでlistStatus(path, DEFAULT_FILTER)
を呼び出します。ここで、DEFAULT_FILTER
はすべてのパス名を受け入れます。
RemoteIterator[LocatedFileStatus] listLocatedStatus(Path path, PathFilter filter)
パスの下にあるLocatedFileStatus
エントリを列挙するイテレータを返します。これは、戻り値がFileStatus
のLocatedFileStatus
サブクラスのインスタンスであることと、リスト全体を返すのではなくイテレータが返される点を除いて、listStatus(Path)
に似ています。
これは実際にはprotected
メソッドであり、listLocatedStatus(Path path)
によって直接呼び出されます。これへの呼び出しは、FilterFileSystem
などの階層化されたファイルシステムを通じて委任される可能性があるため、listLocatedStatus(Path path)
が別の方法で実装されている場合でも、その実装は必須と見なす必要があります。このメソッドをパブリックにすることを提案するJIRAが開かれており、将来的には実現する可能性があります。
イテレータがパスの子エントリの一貫したビューを提供する必要はありません。デフォルトの実装では、listStatus(Path)
を使用してその子をリストし、その一貫性の制約はすでに文書化されています。他の実装では、列挙をさらに動的に実行できる場合があります。たとえば、子エントリのウィンドウ化されたサブセットをフェッチすることで、大きなデータ構造の構築や大きなメッセージの送信を回避します。このような状況では、ファイルシステムへの変更がより可視化される可能性が高くなります。
呼び出し元は、この呼び出しが戻ってから反復処理が完全に実行されるまでの間にファイルシステムが変更された場合、反復処理が失敗する可能性があると想定する必要があります。
パスpath
は存在する必要があります
exists(FS, path) : raise FileNotFoundException
この操作は、listStatus(path, filter)
の結果と等しい結果セットresultset
を生成します。
if isFile(FS, path) and filter.accept(path) : resultset = [ getLocatedFileStatus(FS, path) ] elif isFile(FS, path) and not filter.accept(path) : resultset = [] elif isDir(FS, path) : resultset = [ getLocatedFileStatus(FS, c) for c in children(FS, path) where filter.accept(c) ]
操作getLocatedFileStatus(FS, path: Path): LocatedFileStatus
は、LocatedFileStatus
インスタンスls
のジェネレーターとして定義されます。
fileStatus = getFileStatus(FS, path) bl = getFileBlockLocations(FS, path, 0, fileStatus.len) locatedFileStatus = new LocatedFileStatus(fileStatus, bl)
イテレータでresultset
の要素が返される順序は未定義です。
原子性と一貫性の制約は、listStatus(Path, PathFilter)
の場合と同様です。
RemoteIterator[LocatedFileStatus] listLocatedStatus(Path path)
DEFAULT_FILTER
がすべてのパス名を受け入れる場合のlistLocatedStatus(path, DEFAULT_FILTER)
と同等です。
RemoteIterator[LocatedFileStatus] listFiles(Path path, boolean recursive)
ディレクトリ内/下のすべてのファイルに対して、子ディレクトリを再帰的に走査する可能性のあるイテレータを作成します。
この操作の目的は、単一のRPC呼び出しで収集する必要があるデータ量を削減することにより、ファイルシステムが大規模な再帰的なディレクトリ スキャンをより効率的に処理できるようにすることです。
exists(FS, path) else raise FileNotFoundException
結果はイテレータであり、iterator.next()
呼び出しのシーケンスからの出力は、セットiteratorset
として定義できます。
if not recursive: iteratorset == listStatus(path) else: iteratorset = [ getLocatedFileStatus(FS, d) for d in descendants(FS, path) ]
関数getLocatedFileStatus(FS, d)
は、listLocatedStatus(Path, PathFilter)
で定義されているとおりです。
原子性と一貫性の制約は、listStatus(Path, PathFilter)
の場合と同様です。
ContentSummary getContentSummary(Path path)
パスが与えられた場合、そのコンテンツの概要を返します。
getContentSummary()
は最初に、指定されたパスがファイルであるかどうかを確認し、ファイルである場合は、ディレクトリのカウントに0を、ファイルのカウントに1を返します。
exists(FS, path) else raise FileNotFoundException
指定されたパスのディレクトリのカウントやファイルのカウントなどの情報を含むContentSummary
オブジェクトを返します。
原子性と一貫性の制約は、listStatus(Path, PathFilter)
の場合と同様です。
BlockLocation[] getFileBlockLocations(FileStatus f, int s, int l)
if s < 0 or l < 0 : raise {HadoopIllegalArgumentException, InvalidArgumentException}
HadoopIllegalArgumentException
をスローします。これはIllegalArgumentException
を拡張したものです。ファイルシステムがロケーションを認識している場合、範囲[s:s+l]
のデータが見つかる可能性のあるブロックロケーションのリストを返す必要があります。
if f == null : result = null elif f.getLen() <= s: result = [] else result = [ locations(FS, b) for b in blocks(FS, p, s, s+l)]
ここで
def locations(FS, b) = a list of all locations of a block in the filesystem def blocks(FS, p, s, s + l) = a list of the blocks containing data(FS, path)[s:s+l]
length(FS, f)
は、isDir(FS, f)
の場合には0
として定義されているため、ディレクトリに対するgetFileBlockLocations()
の結果は[]
になることに注意してください。
ファイルシステムがロケーションを認識していない場合は、以下を返す必要があります。
[ BlockLocation(["localhost:9866"] , ["localhost"], ["/default/localhost"] 0, f.getLen()) ] ;
*Hadoop 1.0.3のバグにより、クラスター トポロジと同じ数の要素を持つトポロジ パスを提供する必要があるため、ファイルシステムは"/default/localhost"
パスを返す必要があります。これはもはや問題ではありませんが、慣例は一般的に維持されています。
BlockLocation[] getFileBlockLocations(Path P, int S, int L)
if p == null : raise NullPointerException if not exists(FS, p) : raise FileNotFoundException
result = getFileBlockLocations(getFileStatus(FS, P), S, L)
long getDefaultBlockSize()
ファイルシステムの「デフォルト」のブロック サイズを取得します。これは、ジョブ ワーカー プロセス間で作業を最適に分割するための分割計算中によく使用されます。
result = integer > 0
この結果には定義された最小値はありませんが、ジョブの送信中に作業を分割するために使用されるため、ブロック サイズが小さすぎると、ワークロードの分割が不十分になったり、パーティションを計算する際にJobSubmissionClient
とその同等のものがメモリ不足になることさえあります。
実際にはファイルをブロックに分割しないファイル システムは、効率的な処理につながる数値を返す必要があります。ファイル システムは、これをユーザーが構成できるようにすることができます (オブジェクト ストア コネクタは通常これを行います)。
long getDefaultBlockSize(Path p)
パスの「デフォルト」のブロック サイズ、つまり、ファイル システム内のパスにオブジェクトを書き込むときに使用されるブロック サイズを取得します。
result = integer >= 0
通常、この操作の結果はgetDefaultBlockSize()
と同じであり、指定されたパスの存在に関するチェックは行われません。
マウント ポイントをサポートするファイル システムでは、パスによって異なるデフォルト値を持つことができるため、宛先パスの特定のデフォルト値を返す必要があります。
パスが存在しない場合はエラーではありません。ファイルシステムのその部分のデフォルト/推奨値を返す必要があります。
long getBlockSize(Path p)
このメソッドは、getFileStatus(p)
で返されるFileStatus
構造体のブロックサイズを問い合わせるのと全く同じです。これは、ユーザーがgetFileStatus(p)
を1回呼び出し、その結果を使用してファイルの複数の属性(長さ、タイプ、ブロックサイズなど)を調べることを推奨するために非推奨になりました。複数の属性が照会される場合、これはパフォーマンスの大幅な最適化になり、ファイルシステムへの負荷を軽減できます。
if not exists(FS, p) : raise FileNotFoundException
if len(FS, P) > 0: getFileStatus(P).getBlockSize() > 0 result == getFileStatus(P).getBlockSize()
getFileStatus(P).getBlockSize()
の値と同一でなければなりません。boolean mkdirs(Path p, FsPermission permission)
ディレクトリとそのすべての親を作成します。
パスはディレクトリであるか、存在しない必要があります。
if exists(FS, p) and not isDir(FS, p) : raise [ParentNotDirectoryException, FileAlreadyExistsException, IOException]
祖先がファイルであってはなりません。
forall d = ancestors(FS, p) : if exists(FS, d) and not isDir(FS, d) : raise [ParentNotDirectoryException, FileAlreadyExistsException, IOException]
FS' where FS'.Directories' = FS.Directories + [p] + ancestors(FS, p) result = True
FileSystemのディレクトリ、ファイル、シンボリックリンクの排他条件の要件を満たす必要があります。
パスの存在とタイプ、およびディレクトリ作成のプローブはアトミックでなければなりません。mkdirs(parent(F))
を含む結合された操作は、アトミックである可能性があります。
戻り値は、新しいディレクトリが作成されなくても常にtrueです(これはHDFSで定義されています)。
FSDataOutputStream create(Path, ...)
FSDataOutputStream create(Path p, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress) throws IOException;
上書きなしで作成する場合、ファイルは存在してはなりません。
if not overwrite and isFile(FS, p) : raise FileAlreadyExistsException
ディレクトリへの書き込みまたは上書きは失敗する必要があります。
if isDir(FS, p) : raise {FileAlreadyExistsException, FileNotFoundException, IOException}
祖先がファイルであってはなりません。
forall d = ancestors(FS, p) : if exists(FS, d) and not isDir(FS, d) : raise [ParentNotDirectoryException, FileAlreadyExistsException, IOException]
ファイルシステムは、FSが読み取り専用(HDFS)、ブロックサイズが許可される最小値を下回っている(HDFS)、レプリケーション数が範囲外(HDFS)、名前空間またはファイルシステムのクォータを超えている、予約された名前など、他の理由でリクエストを拒否する場合があります。すべての拒否は、IOException
またはそのサブクラスである必要があり、RuntimeException
またはサブクラスである場合があります。たとえば、HDFSはInvalidPathException
を発生させる可能性があります。
FS' where : FS'.Files'[p] == [] ancestors(p) is-subset-of FS'.Directories' result = FSDataOutputStream
すべてのユーザーから見える、指定されたパスの最後に0バイトのファイルが存在する必要があります。
更新された(有効な)FileSystemには、mkdirs(parent(p))
によって作成されたパスのすべての親ディレクトリが含まれている必要があります。
結果はFSDataOutputStream
であり、その操作を通じて、FS.Files[p]
の更新された値を持つ新しいファイルシステム状態を生成できます。
返されたストリームの動作については、出力で説明します。
一部の実装では、作成をファイルが存在するかどうかのチェックと実際の作成に分割します。これは、操作がアトミックではないことを意味します。overwrite==true
でファイルを作成するクライアントは、2つのテストの間に別のクライアントによってファイルが作成された場合、失敗する可能性があります。
S3Aおよび潜在的に他のオブジェクトストアコネクタは、出力ストリームのclose()
操作が完了するまで、現在FS
の状態を変更しません。これは、オブジェクトストアの動作とファイルシステムの動作との間の大きな違いです。これにより、複数のクライアントがoverwrite=false
でファイルを作成し、ファイル/ディレクトリのロジックを混乱させる可能性があります。特に、create()
を使用してファイルへの排他ロックを取得する(エラーなしでファイルを作成した人がロックの保持者と見なされる)ことは、オブジェクトストアを操作する場合は安全なアルゴリズムではない可能性があります。
オブジェクトストアは、ファイルが作成されたときにマーカーとして空のファイルを作成する場合があります。ただし、overwrite=true
セマンティクスを持つオブジェクトストアは、これをアトミックに実装しない可能性があるため、overwrite=false
でファイルを作成することは、プロセス間の暗黙的な除外メカニズムとして使用できません。
ローカルファイルシステムは、ディレクトリ上にファイルを作成しようとするとFileNotFoundException
を発生させるため、この前提条件が失敗した場合に発生する可能性のある例外としてリストされています。
対象外:シンボリックリンク。シンボリックリンクの解決済みパスは、create()
操作の最終パス引数として使用されます。
FSDataOutputStreamBuilder createFile(Path p)
ファイルを作成するためのパラメータを指定するためのFSDataOutputStreamBuilder
を作成します。
返されたストリームの動作については、出力で説明します。
createFile(p)
はFSDataOutputStreamBuilder
のみを返し、ファイルシステムをすぐに変更しません。FSDataOutputStreamBuilder
でbuild()
が呼び出されると、ビルダーパラメータが検証され、基になるファイルシステムでcreate(Path p)
が呼び出されます。build()
には、create(Path p)
と同じ前提条件と事後条件があります。
create(Path p)
と同様に、builder.overwrite(false)
を指定しない限り、ファイルはデフォルトで上書きされます。create(Path p)
とは異なり、builder.recursive()
を指定しない限り、親ディレクトリはデフォルトで作成されません。FSDataOutputStream append(Path p, int bufferSize, Progressable progress)
準拠した呼び出しを持たない実装は、UnsupportedOperationException
をスローする必要があります。
if not exists(FS, p) : raise FileNotFoundException if not isFile(FS, p) : raise [FileAlreadyExistsException, FileNotFoundException, IOException]
FS' = FS result = FSDataOutputStream
戻り値:既存のリストにデータを追加することにより、エントリFS.Files[p]
を更新できるFSDataOutputStream
。
返されたストリームの動作については、出力で説明します。
FSDataOutputStreamBuilder appendFile(Path p)
既存のファイルに追加するためのパラメータを指定するためのFSDataOutputStreamBuilder
を作成します。
返されたストリームの動作については、出力で説明します。
appendFile(p)
はFSDataOutputStreamBuilder
のみを返し、ファイルシステムをすぐに変更しません。FSDataOutputStreamBuilder
でbuild()
が呼び出されると、ビルダーパラメータが検証され、基になるファイルシステムでappend()
が呼び出されます。build()
には、append()
と同じ前提条件と事後条件があります。
FSDataInputStream open(Path f, int bufferSize)
準拠した呼び出しを持たない実装は、UnsupportedOperationException
をスローする必要があります。
if not isFile(FS, p)) : raise [FileNotFoundException, IOException]
これは重要な前提条件です。一部のファイルシステム(例:オブジェクトストア)の実装では、返されたFSDataInputStream
での最初のread()
までHTTP GET操作を延期することにより、1回のラウンドトリップを短縮できます。ただし、多くのクライアントコードは、open()
操作時に実行される存在チェックに依存しています。実装は、作成時にファイルの存在をチェックする必要があります。これは、ファイルとそのデータが、次のread()
または後続の任意の時点でもまだ存在することを意味するものではありません。
result = FSDataInputStream(0, FS.Files[p])
結果は、FS.Files[p]
で定義されたバイト配列へのアクセスを提供します。そのアクセスがopen()
操作が呼び出された時点でのコンテンツへのアクセスであるか、FSの後続の状態でのデータへの変更をどのように取得するかは、実装の詳細です。
結果は、操作のローカルおよびリモートの呼び出し元に対して同じでなければなりません。
HDFSは、シンボリックリンクをトラバースしようとすると、UnresolvedPathException
をスローする可能性があります。
HDFSは、パスがメタデータに存在しても、ブロックのコピーが見つからない場合、IOException("Cannot open filename " + src)
をスローします。FileNotFoundException
の方がより正確で役立つと思われます。
FSDataInputStreamBuilder openFile(Path path)
openFile()を参照してください。
FSDataInputStreamBuilder openFile(PathHandle)
openFile()を参照してください。
PathHandle getPathHandle(FileStatus stat, HandleOpt... options)
準拠した呼び出しを持たない実装は、UnsupportedOperationException
をスローする必要があります。
let stat = getFileStatus(Path p) let FS' where: (FS.Directories', FS.Files', FS.Symlinks') p' in paths(FS') where: exists(FS, stat.path) implies exists(FS', p')
解決された時点でのFileStatus
インスタンスの参照は、getPathHandle(FileStatus)
の結果と同じ参照です。PathHandle
は、後続の操作で、呼び出し間で不変条件が保持されるようにするために使用できます。
options
パラメータは、たとえば、参照データまたは場所が変更された場合に、後続の呼び出し(例:open(PathHandle)
)が成功するかどうかを指定します。デフォルトでは、変更が発生するとエラーになります。呼び出し元は、参照が別のパスに存在する場合やデータが変更された場合でも、操作が成功するように許可する緩和を指定できます。
実装は、呼び出し元によって指定されたセマンティクスをサポートできない場合、UnsupportedOperationException
をスローする必要があります。オプションのデフォルトセットは次のとおりです。
Unmoved | Moved | |
---|---|---|
Unchanged | EXACT | CONTENT |
Changed | PATH | REFERENCE |
所有権、拡張属性、およびその他のメタデータへの変更は、PathHandle
と一致する必要はありません。実装では、カスタム制約を使用してHandleOpt
パラメータのセットを拡張できます。
クライアントは、PathHandle
がREFERENCE
を使用して名前変更を追跡する必要があることを指定します。参照の解決に失敗することがエンティティがもはや存在しないことを意味しない限り、実装はPathHandle
を作成するときにUnsupportedOperationException
をスローする必要があります。
クライアントは、PathHandle
がPATH
を使用してエンティティが変更されていない場合にのみ解決する必要があることを指定します。実装は、同じパスに後で配置された同一のエンティティを区別できない限り、PathHandle
を作成するときにUnsupportedOperationException
をスローする必要があります。
result = PathHandle(p')
PathHandle
の参照は、PathHandle
が作成されたときではなく、FileStatus
インスタンスが作成されたときの名前空間です。実装は、有効であっても、サービス提供にコストがかかるPathHandle
インスタンスの作成または解決の試行を拒否する場合があります。
オブジェクトの系統が解決されない限り、オブジェクトをコピーすることで名前を変更するオブジェクトストアは、CONTENT
とREFERENCE
をサポートすると主張してはなりません。
PathHandle
インスタンスをシリアル化し、そのセマンティクスを変更せずに、1つ以上のプロセス、別のマシン、および将来の任意の位置でインスタンス化できる必要があります。実装は、その不変条件を保証できなくなった場合は、インスタンスの解決を拒否する必要があります。
HDFSは、ディレクトリまたはシンボリックリンクへのPathHandle
参照をサポートしていません。CONTENT
とREFERENCE
のサポートは、INodeでファイルを検索します。INodeはNameNode間で一意ではないため、フェデレーションクラスタは、他の名前空間からの参照を検出するために、PathHandle
に十分なメタデータを含める必要があります。
FSDataInputStream open(PathHandle handle, int bufferSize)
準拠した呼び出しを持たない実装は、UnsupportedOperationException
をスローする必要があります。
let fd = getPathHandle(FileStatus stat) if stat.isdir : raise IOException let FS' where: (FS.Directories', FS.Files', FS.Symlinks') p' in FS.Files' where: FS.Files'[p'] = fd if not exists(FS', p') : raise InvalidPathHandleException
実装は、getPathHandle(FileStatus)
で作成時に指定された制約に従って、PathHandle
の参照を解決する必要があります。
この契約を履行するためにFileSystem
に必要なメタデータは、PathHandle
でエンコードされる場合があります。
result = FSDataInputStream(0, FS.Files'[p'])
返されるストリームは、open(Path)
によって返されるストリームの制約を受けます。オープン時にチェックされた制約は、ストリームで保持される可能性がありますが、これは保証されていません。
たとえば、CONTENT
制約で作成されたPathHandle
は、open(PathHandle)
が解決されたときに変更されていなかった場合、オープン後にファイルへの更新を無視するストリームを返す場合があります。
実装は、サーバー側またはクライアントにストリームを返す前に、不変条件をチェックする場合があります。たとえば、実装では、ファイルを開き、getFileStatus(Path)
を使用してPathHandle
の不変条件を検証して、CONTENT
を実装する場合があります。これにより、偽陽性が生成され、追加のRPCトラフィックが必要になります。
boolean delete(Path p, boolean recursive)
ファイル、シンボリックリンク、ディレクトリのいずれであっても、パスを削除します。recursive
フラグは、再帰的な削除を行うかどうかを示します。設定されていない場合、空でないディレクトリを削除することはできません。
ルートディレクトリの特別な場合を除き、このAPI呼び出しが正常に完了した場合、パスの末尾には何も存在しません。つまり、結果は意図したとおりです。戻り値のフラグは、ファイルシステムの状態に変更があったかどうかを呼び出し元に伝えるだけです。
注:このメソッドの多くの使用例では、戻り値がfalseであるかどうかのチェックが行われ、falseの場合は例外を発生させています。たとえば、
if (!fs.delete(path, true)) throw new IOException("Could not delete " + path);
このパターンは必要ありません。コードは、delete(path, recursive)
を呼び出し、ルートディレクトリの特別な場合を除いて、宛先が存在しなくなったと仮定すべきです(ルートディレクトリの特別な扱いについては後述)。
子を持つディレクトリは、recursive == False
の場合、削除できません。
if isDir(FS, p) and not recursive and (children(FS, p) != {}) : raise IOException
(HDFSはここで PathIsNotEmptyDirectoryException
を発生させます。)
ファイルが存在しない場合、ファイルシステムの状態は変更されません。
if not exists(FS, p): FS' = FS result = False
結果は False
にすべきです。これは、ファイルが削除されなかったことを示します。
ファイルを指すパスは削除され、戻り値は True
です。
if isFile(FS, p) : FS' = (FS.Directories, FS.Files - [p], FS.Symlinks) result = True
recursive == False
空のルートを削除してもファイルシステムの状態は変化せず、trueまたはfalseを返す可能性があります。
if isRoot(p) and children(FS, p) == {} : FS ' = FS result = (undetermined)
ルートディレクトリを削除しようとしても、一貫した戻りコードはありません。
実装はtrueを返すようにすべきです。これにより、falseの戻り値をチェックするコードが過剰に反応するのを防ぎます。
オブジェクトストア:オブジェクトストア:ルートディレクトリの削除を参照してください。
recursive == False
ルートではない空のディレクトリを削除すると、FSからパスが削除され、trueが返されます。
if isDir(FS, p) and not isRoot(p) and children(FS, p) == {} : FS' = (FS.Directories - [p], FS.Files, FS.Symlinks) result = True
子を持つルートパスと recursive==True
を削除すると、一般的に3つの結果が生じる可能性があります。
POSIXモデルでは、ユーザーがすべてを削除する適切な権限を持っている場合、それらを自由に削除できると仮定しています(その結果、ファイルシステムは空になります)。
if isDir(FS, p) and isRoot(p) and recursive : FS' = ({["/"]}, {}, {}, {}) result = True
HDFSは、ファイルシステムのルートの削除を許可しません。空のファイルシステムが必要な場合は、ファイルシステムをオフラインにして再フォーマットする必要があります。
if isDir(FS, p) and isRoot(p) and recursive : FS' = FS result = False
オブジェクトストア:オブジェクトストア:ルートディレクトリの削除を参照してください。
この仕様は特定のアクションを推奨するものではありません。ただし、POSIXモデルでは、通常のユーザーがそのルートディレクトリを削除する権限を持たないような権限モデルがあると想定されていることに注意してください。これは、システム管理者のみが実行できるアクションです。
このようなセキュリティモデルを持たないリモートファイルシステムとやり取りするファイルシステムクライアントは、データが失われる可能性が高すぎるという理由で、delete("/", true)
の呼び出しを拒否する可能性があります。
オブジェクトストアに基づくファイルシステムの実装の一部では、ルートを削除すると常にfalseが返され、ストアの状態は変更されません。
if isRoot(p) : FS ' = FS result = False
これは、recursiveフラグの状態やディレクトリの状態に関係なく行われます。
これは、ストアの内容の必然的に非アトミックなスキャンと削除を回避する簡略化です。また、操作が実際に特定のストア/コンテナ自体を削除するかどうか、およびストアのよりシンプルなアクセス許可モデルの悪影響についての混乱も回避されます。
子を持つルートでないパスと recursive==true
を削除すると、パスとそのすべての子孫が削除されます。
if isDir(FS, p) and not isRoot(p) and recursive : FS' where: not isDir(FS', p) and forall d in descendants(FS, p): not isDir(FS', d) not isFile(FS', d) not isSymlink(FS', d) result = True
ファイルの削除はアトミックなアクションでなければなりません。
空のディレクトリの削除はアトミックなアクションでなければなりません。
ディレクトリツリーの再帰的な削除はアトミックでなければなりません。
delete()
を再帰的なリストとエントリごとの削除操作として実装する傾向があります。これにより、O(1)のアトミックなディレクトリ削除に対するクライアントアプリケーションの期待が損なわれ、ストアをHDFSの代替としてドロップインで使用することができなくなる可能性があります。boolean rename(Path src, Path d)
仕様の観点から、rename()
はファイルシステム内で最も複雑な操作の1つです。
実装の観点から、falseを返すか、例外を発生させるかについての曖昧さが最も多い操作の1つです。
リネームには、宛先パスの計算が含まれます。宛先が存在し、それがディレクトリの場合、リネームの最終的な宛先は、宛先+ソースパスのファイル名になります。
let dest = if (isDir(FS, d) and d != src) : d + [filename(src)] else : d
宛先パスに対するすべてのチェックは、最終的な dest
パスが計算された後に行う必要があります。
ソース src
が存在する必要があります。
exists(FS, src) else raise FileNotFoundException
dest
は src
の子孫であってはなりません。
if isDescendant(FS, src, dest) : raise IOException
これにより、isRoot(FS, src)
の特殊なケースが暗黙的にカバーされます。
dest
はルートであるか、存在する親を持っている必要があります。
isRoot(FS, dest) or exists(FS, parent(dest)) else raise IOException
宛先の親パスはファイルであってはなりません。
if isFile(FS, parent(dest)) : raise IOException
これにより、親のすべての上位要素が暗黙的にカバーされます。
宛先パスの末尾に既存のファイルがあってはなりません。
if isFile(FS, dest) : raise FileAlreadyExistsException, IOException
ディレクトリを自分自身にリネームすることは何もしません。戻り値は指定されていません。
POSIXでは結果は False
です。HDFSでは結果は True
です。
if isDir(FS, src) and src == dest : FS' = FS result = (undefined)
ファイルを自分自身にリネームすることは何もしません。結果は True
です。
if isFile(FS, src) and src == dest : FS' = FS result = True
宛先がディレクトリである場合にファイルをリネームすると、ファイルは宛先ディレクトリの子として移動し、ソースパスのファイル名要素が保持されます。
if isFile(FS, src) and src != dest: FS' where: not exists(FS', src) and exists(FS', dest) and data(FS', dest) == data (FS, source) result = True
src
がディレクトリの場合、そのすべての子は dest
の下に存在し、パス src
とその子孫は存在しなくなります。dest
の下のパスの名前は、src
の下のパスの名前と一致し、内容も一致します。
if isDir(FS, src) and isDir(FS, dest) and src != dest : FS' where: not exists(FS', src) and dest in FS'.Directories and forall c in descendants(FS, src) : not exists(FS', c)) and forall c in descendants(FS, src) where isDir(FS, c): isDir(FS', dest + childElements(src, c) and forall c in descendants(FS, src) where not isDir(FS, c): data(FS', dest + childElements(s, c)) == data(FS, c) result = True
not exists(FS, parent(dest))
ここには一貫した動作はありません。
HDFS
結果はファイルシステムの状態への変更はなく、戻り値はfalseです。
FS' = FS; result = False
ローカルファイルシステム
結果は通常のリネームと同じですが、宛先の親ディレクトリも存在するという追加の(暗黙的な)機能があります。
exists(FS', parent(dest))
S3Aファイルシステム
結果は通常のリネームと同じですが、宛先の親ディレクトリが存在するという追加の(暗黙的な)機能があります。exists(FS', parent(dest))
parent(dest)
がファイルである場合はチェックして拒否しますが、その他の上位要素のチェックはありません。
その他のファイルシステム
その他のファイルシステムは、操作を厳密に拒否し、FileNotFoundException
を発生させます。
rename()
のコア操作(ファイルシステム内のあるエントリを別のエントリに移動する)は、アトミックでなければなりません。一部のアプリケーションは、これをデータへのアクセスを調整する方法として利用しています。
一部のファイルシステムの実装では、リネームの前後に宛先ファイルシステムに対するチェックを実行します。この例の1つは、ローカルデータへのチェックサム付きアクセスを提供する ChecksumFileSystem
です。シーケンス全体がアトミックではない場合があります。
読み取り、書き込み、または追記のために開いているファイル
開いているファイルに対する rename()
の動作は指定されていません。許可されるかどうか、開いているストリームからの読み取りまたは書き込みの後の試行がどうなるか
ディレクトリを自分自身にリネームする
ディレクトリを自分自身にリネームしたときの戻りコードは指定されていません。
宛先が存在し、ファイルである
既存のファイルの上にファイルをリネームすることは、失敗として指定され、例外が発生します。
ローカルファイルシステム:リネームが成功します。宛先ファイルはソースファイルに置き換えられます。
HDFS:リネームが失敗し、例外は発生しません。代わりに、メソッド呼び出しは単にfalseを返します。
ソースファイルが見つからない
ソースファイル src
が存在しない場合は、FileNotFoundException
を発生させる必要があります。
HDFSは例外を発生させずに失敗します。rename()
は単にfalseを返します。
FS' = FS result = false
ここでのHDFSの動作は、複製する機能と見なすべきではありません。FileContext
は、例外を発生させるように動作を明示的に変更しました。また、そのアクションを DFSFileSystem
実装に遡って適用することは、継続的な議論のテーマとなっています。
void concat(Path p, Path sources[])
複数のブロックを結合して1つのファイルを作成します。これは、現在HDFSのみが実装している、ほとんど使用されない操作です。
準拠した呼び出しを持たない実装は、UnsupportedOperationException
をスローする必要があります。
if not exists(FS, p) : raise FileNotFoundException if sources==[] : raise IllegalArgumentException
すべてのソースは同じディレクトリにある必要があります。
for s in sources: if parent(S) != parent(p) raise IllegalArgumentException
すべてのブロックサイズはターゲットのサイズと一致する必要があります。
for s in sources: getBlockSize(FS, S) == getBlockSize(FS, p)
重複するパスはありません。
not (exists p1, p2 in (sources + [p]) where p1 == p2)
HDFS:最後のファイルを除くすべてのソースファイルは、完全なブロックである必要があります。
for s in (sources[0:length(sources)-1] + [p]): (length(FS, s) mod getBlockSize(FS, p)) == 0
FS' where: (data(FS', T) = data(FS, T) + data(FS, sources[0]) + ... + data(FS, srcs[length(srcs)-1])) and for s in srcs: not exists(FS', S)
HDFSの制限は、シーケンスでそれらを結合するためにinode参照を変更することによって concat
を実装する方法の実装の詳細である可能性があります。Hadoopコアコードベースの他のファイルシステムはこのメソッドを実装していないため、実装の詳細を仕様から区別する方法はありません。
boolean truncate(Path p, long newLength)
ファイル p
を指定された newLength
に切り詰めます。
準拠した呼び出しを持たない実装は、UnsupportedOperationException
をスローする必要があります。
if not exists(FS, p) : raise FileNotFoundException if isDir(FS, p) : raise [FileNotFoundException, IOException] if newLength < 0 || newLength > len(FS.Files[p]) : raise HadoopIllegalArgumentException
HDFS:ソースファイルは閉じられている必要があります。書き込みまたは追記のために開いているファイルに対しては切り捨てを実行できません。
FS' where: len(FS.Files[p]) = newLength
戻り値:切り詰めが完了し、ファイルがすぐに追加のために開ける場合は true
、それ以外の場合は false
です。
HDFS:HDFSは、最後のブロックの長さを調整するバックグラウンドプロセスが開始されたことを示すために false
を返し、クライアントはファイル更新を続行する前に、その完了を待つ必要があります。
truncate() が発生したときに入力ストリームが開いている場合、切り詰められるファイルの部分に関連する読み取り操作の結果は未定義です。
boolean copyFromLocalFile(boolean delSrc, boolean overwrite, Path src, Path dst)
src
のローカルディスク上のソースファイルまたはディレクトリは、宛先 dst
のファイルシステムにコピーされます。移動後にソースを削除する必要がある場合は、delSrc
フラグをTRUEに設定する必要があります。宛先がすでに存在し、宛先の内容を上書きする必要がある場合は、overwrite
フラグをTRUEに設定する必要があります。
ソースと宛先は異なる必要があります。
if src = dest : raise FileExistsException
宛先とソースは、互いに子孫であってはなりません。
if isDescendant(src, dest) or isDescendant(dest, src) : raise IOException
ソースファイルまたはディレクトリがローカルに存在する必要があります。
if not exists(LocalFS, src) : raise FileNotFoundException
ディレクトリは、上書きフラグの設定に関わらず、ファイルにコピーすることはできません。
if isDir(LocalFS, src) and isFile(FS, dst) : raise PathExistsException
上記の前提条件が例外をスローする場合を除き、宛先が存在する場合は、操作を成功させるために上書きフラグをTRUEに設定する必要があります。これにより、宛先のファイル/ディレクトリも上書きされます。
if exists(FS, dst) and not overwrite : raise PathExistsException
ソースのベースパスbase
と、base
がancestors(child) + child
に含まれる子パスchild
が与えられた場合
def final_name(base, child, dest): is base = child: return dest else: return dest + childElements(base, child)
isFile(LocalFS, src)
ファイルの場合、宛先のデータはソースのデータになります。すべての上位ディレクトリはディレクトリです。
if isFile(LocalFS, src) and (not exists(FS, dest) or (exists(FS, dest) and overwrite)): FS' = FS where: FS'.Files[dest] = LocalFS.Files[src] FS'.Directories = FS.Directories + ancestors(FS, dest) LocalFS' = LocalFS where not delSrc or (delSrc = true and delete(LocalFS, src, false)) else if isFile(LocalFS, src) and isDir(FS, dest): FS' = FS where: let d = final_name(src, dest) FS'.Files[d] = LocalFS.Files[src] LocalFS' = LocalFS where: not delSrc or (delSrc = true and delete(LocalFS, src, false))
ローカルのLocalFS
とリモートのFS
の両方で、ファイルの変更がアトミックであるという期待はありません。
isDir(LocalFS, src)
if isDir(LocalFS, src) and (isFile(FS, dest) or isFile(FS, dest + childElements(src))): raise FileAlreadyExistsException else if isDir(LocalFS, src): if exists(FS, dest): dest' = dest + childElements(src) if exists(FS, dest') and not overwrite: raise PathExistsException else: dest' = dest FS' = FS where: forall c in descendants(LocalFS, src): not exists(FS', final_name(c)) or overwrite and forall c in descendants(LocalFS, src) where isDir(LocalFS, c): FS'.Directories = FS'.Directories + (dest' + childElements(src, c)) and forall c in descendants(LocalFS, src) where isFile(LocalFS, c): FS'.Files[final_name(c, dest')] = LocalFS.Files[c] LocalFS' = LocalFS where not delSrc or (delSrc = true and delete(LocalFS, src, true))
操作の分離/原子性に関する期待はありません。つまり、操作の実行中にソースまたは宛先のファイルが変更される可能性があります。コピー後のファイルまたはディレクトリの最終状態については、最善を尽くす以上の保証は行いません。例:ディレクトリをコピーするとき、1つのファイルがソースから宛先に移動されることがありますが、コピー操作中に宛先の新しいファイルが更新されるのを防ぐものはありません。
デフォルトのHDFS実装では、src
にある各ファイルとフォルダーを再帰的に調べて、それらを最終的な宛先(dst
に対する相対位置)に順番にコピーします。
オブジェクトストアベースのファイルシステムは、上記の実装から生じる制限事項を認識し、スループットを最大化するために、並列アップロードとストアにコピーされるファイルの順序変更の可能性を利用できます。
RemoteIterator
RemoteIterator
インターフェースは、java.util.Iterator
のリモートアクセス相当として使用され、呼び出し元がリモートデータ要素の有限シーケンスを反復処理できるようにします。
主な違いは次のとおりです
Iterator
のオプションのvoid remove()
メソッドはサポートされていません。IOException
例外が発生する可能性があります。public interface RemoteIterator<E> { boolean hasNext() throws IOException; E next() throws IOException; }
インターフェースの基本的な見方は、hasNext()
がtrueの場合、next()
がリスト内の次のエントリを正常に返すことを意味します
while hasNext(): next()
同様に、next()
の呼び出しが成功した場合、next()
の呼び出し前にhasNext()
が呼び出されていれば、trueであったはずであることを意味します。
boolean elementAvailable = hasNext(); try { next(); assert elementAvailable; } catch (NoSuchElementException e) { assert !elementAvailable }
next()
演算子は、hasNext()
が呼び出されなかった場合でも、利用可能な結果のリストを反復処理する必要があります。
つまり、NoSuchElementException
例外が発生した場合にのみ終了するループを通じて、結果を列挙することができます。
try { while (true) { process(iterator.next()); } } catch (NoSuchElementException ignored) { // the end of the list has been reached }
反復の出力は、ループと同等です
while (iterator.hasNext()) { process(iterator.next()); }
JVMで例外を発生させるのはコストのかかる操作であるため、while(hasNext())
ループオプションの方が効率的です。(このトピックに関する議論については、並行性とリモートイテレータも参照してください)。
インターフェースの実装者は、両方の形式の反復をサポートする必要があります。テストの作成者は、両方の反復メカニズムが機能することを確認する必要があります。
反復は有限のシーケンスを返す必要があります。両方の形式のループは最終的に終了する必要があります。Hadoopコードベースのインターフェースのすべての実装は、この要件を満たしています。すべてのコンシューマーは、それが保持されると想定しています。
boolean hasNext()
後続のnext()
の1回の呼び出しが例外を発生させるのではなく要素を返す場合にのみtrueを返します。
result = True ==> next() will succeed. result = False ==> next() will raise an exception
next()
呼び出しを介在させずにhasNext()
を複数回呼び出すと、同じ値が返される必要があります。
boolean has1 = iterator.hasNext(); boolean has2 = iterator.hasNext(); assert has1 == has2;
E next()
反復の次の要素を返します。
hasNext() else raise java.util.NoSuchElementException
result = the next element in the iteration
next()
を繰り返し呼び出すと、シーケンス全体が返されるまで、シーケンス内の後続の要素が返されます。
ファイルシステムAPIでのRemoteIterator
の主な用途は、(場合によってはリモートの)ファイルシステム上のファイルをリストすることです。これらのファイルシステムは常に同時にアクセスされます。ファイルシステムの状態は、hasNext()
プローブとnext()
呼び出しの呼び出しの間で変化する可能性があります。
RemoteIterator
を反復処理中に、リモートファイルシステムでディレクトリが削除された場合、hasNext()
またはnext()
の呼び出しでFileNotFoundException
がスローされる可能性があります。
したがって、RemoteIterator
を介した堅牢な反復では、プロセス中に発生したNoSuchElementException
例外をキャッチして破棄します。これは、上記のwhile(true)
反復の例、またはhasNext()/next()
シーケンスを介して、障害中に発生する可能性のある他の例外(たとえば、FileNotFoundException
)と並行してNoSuchElementException
をキャッチする外側のtry/catch
句を使用して行うことができます。
try { while (iterator.hasNext()) { process(iterator.next()); } } catch (NoSuchElementException ignored) { // the end of the list has been reached }
これはHadoopコードベースでは行われていないことに注意してください。これは、堅牢なループが推奨されないことを意味するのではなく、これらのループの実装中に並行性の問題が考慮されなかったことを意味します。
StreamCapabilities
StreamCapabilities
は、OutputStream
、InputStream
、またはその他のFileSystemクラスがサポートする機能をプログラムでクエリする方法を提供します。
public interface StreamCapabilities { boolean hasCapability(String capability); }
boolean hasCapability(capability)
OutputStream
、InputStream
、またはその他のFileSystemクラスに目的の機能がある場合にのみtrueを返します。
呼び出し元は、文字列値を使用してストリームの機能をクエリできます。可能な文字列値の表を次に示します
文字列 | 定数 | 実装 | 説明 |
---|---|---|---|
hflush | HFLUSH | Syncable | クライアントのユーザーバッファ内のデータをフラッシュします。この呼び出しが返された後、新しいリーダーはデータを表示します。 |
hsync | HSYNC | Syncable | クライアントのユーザーバッファ内のデータをディスクデバイスまでフラッシュします(ただし、ディスクにキャッシュされている可能性があります)。POSIX fsyncと同様。 |
in:readahead | READAHEAD | CanSetReadahead | 入力ストリームにリードアヘッドを設定します。 |
dropbehind | DROPBEHIND | CanSetDropBehind | キャッシュを破棄します。 |
in:unbuffer | UNBUFFER | CanUnbuffer | 入力ストリームのバッファリングを減らします。 |
EtagSource
を介したEtagプローブFileSystemの実装では、FileStatus
エントリからHTTP etagのクエリをサポートできます。その場合、要件は次のとおりです。
getFileStatus()
呼び出し全体で適用される必要があります。つまり、etagサポートを追加する場合、FileStatus
またはListLocatedStatus
エントリを返すすべての操作は、EtagSource
のインスタンスであるサブクラスを返す必要があります。
etagをサポートするには、getFileStatus()
とリスト呼び出しの両方で提供される必要があります。
実装者への注意:これを実現するためにオーバーライドする必要があるコアAPIは次のとおりです
FileStatus getFileStatus(Path) FileStatus[] listStatus(Path) RemoteIterator<FileStatus> listStatusIterator(Path) RemoteIterator<LocatedFileStatus> listFiles([Path, boolean)
EtagSource.getEtag()
の値は、特定のオブジェクトのgetFileStatus()
の呼び出しに対してetagを返すlist*クエリで同じである必要があります。
((EtagSource)getFileStatus(path)).getEtag() == ((EtagSource)listStatus(path)[0]).getEtag()
同様に、パスのlistFiles()
、listStatusIncremental()
、およびリスト内のすべてのファイルの親パスをリストするときに、同じ値が返される必要があります。
同じパスに書き込まれた2つの異なるデータの配列は、プローブ時に異なるetag値を持つ必要があります。これは、HTTP仕様の要件です。
ファイルの名前が変更された後、((EtagSource)getFileStatus(dest)).getEtag()
の値は、名前変更が行われる前の((EtagSource)getFileStatus(source)).getEtag()
の値と同じである必要があります。
これはストアの実装の詳細です。AWS S3には当てはまりません。
ストアがこの要件を一貫して満たす場合にのみ、ファイルシステムはhasPathCapability()
でfs.capability.etags.preserved.in.rename
をサポートしていることを宣言する必要があります。
ディレクトリのエントリは、リスト/プローブ操作でetagを返す場合があります。これらのエントリは、名前変更時に保持される場合があります。
同様に、ディレクトリのエントリは、このようなエントリを提供しない場合や、名前変更時に保持しない場合、時間の経過に伴う一貫性を保証しない場合があります。
注:ルートパス「/」について特筆します。これは実際の「ディレクトリ」ではないため、誰もetagがあることを期待すべきではありません。
FileStatus
サブクラスはSerializable
である必要があります。Writable
の場合もあります基本のFileStatus
クラスは、Serializable
とWritable
を実装し、そのフィールドを適切にマーシャリングします。
サブクラスは、etagを保持し、Javaシリアル化(一部のApache Sparkアプリケーションで使用)をサポートする必要があります。これは、etagフィールドを非静的にしてserialVersionUID
を追加することの問題です。
Writable
サポートは、Hadoop IPC呼び出しを介したステータスデータのマーシャリングに使用されました。Hadoop 3では、org/apache/hadoop/fs/protocolPB/PBHelper.java
と非推奨のメソッドを通じて実装されています。サブクラスは、非推奨のメソッドをオーバーライドしてetagマーシャリングを追加できます。ただし、これに対する期待はなく、そのようなマーシャリングが発生する可能性は低いでしょう。
hasPathCapability(path, "fs.capability.etags.available")
は、ファイルシステムがファイルステータス/リスト操作で有効な(空でないetag)を返す場合にのみtrueを返す必要があります。hasPathCapability(path, "fs.capability.etags.consistent.across.rename")
は、etagが名前変更時に保持される場合にのみtrueを返す必要があります。FileSystem.getFileChecksum(Path)
が、オブジェクトのetagに関連するチェックサム値を返すという要件/期待はありません(値が返される場合)。