raf を開き、ファイル チャネルを取得し、バッファにデータを蓄積します。
channel.position(raf.length)
channel.write(buffers)
channel.close
raf.close
これらのオフセットでバイトが raf に書き込まれることを期待しています。これは、raf.length + 配列内の累積バッファー サイズです。
0,62,132,195,259,322,392,455,519,589,652,716,779,842,905,968,1031,1093,1155,1225,1287,1350,1414,1477,1541,1611,1674,1737,1801,1863,1927,1989,2059,2123,2193,2256,2319,2382,2452,2516,2586,2648,2711,2774,2837,2900,2962,3025,3089,3152,3216,3286,3348,3412,3482,3544,3614,3684,3754,3818,3882,3952,4022,4092
ProcMon は、これが OS レベルで発生し始めていることを表示します
"CreateFile","objects.bin","SUCCESS","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened"
"QueryNetworkOpenInformationFile","objects.bin","SUCCESS","CreationTime: 6.12.2015 20:11:21, LastAccessTime: 8.12.2015 11:03:49, LastWriteTime: 8.12.2015 11:03:49, ChangeTime: 8.12.2015 11:03:49, AllocationSize: 1.01.1601 2:00:00, EndOfFile: 1.01.1601 2:00:00, FileAttributes: ANCI"
"CloseFile","objects.bin","SUCCESS",""
"CreateFile","objects.bin","SUCCESS","Desired Access: Read Attributes, Delete, Disposition: Open, Options: Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened"
"QueryAttributeTagFile","objects.bin","SUCCESS","Attributes: ANCI, ReparseTag: 0x0"
"SetDispositionInformationFile","objects.bin","SUCCESS","Delete: True"
"CloseFile","objects.bin","SUCCESS",""
"CreateFile","objects.bin","SUCCESS","Desired Access: Generic Read/Write, Disposition: OpenIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0, OpenResult: Created"
"QueryStandardInformationFile","objects.bin","SUCCESS","AllocationSize: 0, EndOfFile: 0, NumberOfLinks: 1, DeletePending: False, Directory: False"
"WriteFile","objects.bin","SUCCESS","Offset: 0, Length: 62, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 62, Length: 70, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 132, Length: 63, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 195, Length: 64, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 259, Length: 63, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 322, Length: 70, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 392, Length: 63, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 455, Length: 64, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 519, Length: 70, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 589, Length: 63, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 652, Length: 64, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 716, Length: 63, Priority: Normal"
"WriteFile","objects.bin","SUCCESS","Offset: 779, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 842, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 905, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 968, Length: 63"
"ReadFile","objects.bin","END OF FILE","Offset: 2 452, Length: 2"
"QueryStandardInformationFile","objects.bin","SUCCESS","AllocationSize: 4 096, EndOfFile: 1 031, NumberOfLinks: 1, DeletePending: False, Directory: False"
"WriteFile","objects.bin","SUCCESS","Offset: 1 031, Length: 64"
"WriteFile","objects.bin","SUCCESS","Offset: 1 095, Length: 70"
"WriteFile","objects.bin","SUCCESS","Offset: 1 165, Length: 70"
"WriteFile","objects.bin","SUCCESS","Offset: 1 235, Length: 64"
"WriteFile","objects.bin","SUCCESS","Offset: 1 299, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 1 362, Length: 64"
"WriteFile","objects.bin","SUCCESS","Offset: 1 426, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 1 489, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 1 552, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 1 615, Length: 64"
"WriteFile","objects.bin","SUCCESS","Offset: 1 679, Length: 70"
"WriteFile","objects.bin","SUCCESS","Offset: 1 749, Length: 64"
"WriteFile","objects.bin","SUCCESS","Offset: 1 813, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 1 876, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 1 939, Length: 63"
"WriteFile","objects.bin","SUCCESS","Offset: 2 002, Length: 63"
"CloseFile","objects.bin","SUCCESS",""
ご覧のとおり、オフセット 0、62、132、... から 968 までの 16 個のバッファを書き込み、ファイルを閉じます! 次に、もう一度それを開き、オフセット 1031、1095、1165 に進みます。これは別のセッションからのものです。ここで同じことを行います。raf と fc を開き、別の一連のバッファーを書き込みます。これは、16 個のバッファーが再度書き込まれた後に中断されます。2 番目のシリーズは、ファイルが閉じられた 1031 から始まり、最初の書き込みから 17 番目のバッファーの場所でした。jdocs の 16 バッファ制限については何も表示されません。
幸いなことに、fileChannel.force
システムがシャットダウンしない限り、ディスク上にデータを物理的に必要としないため、この状況を解決することはできません。fc.write 時または JVM の終了時にデータが OS キャッシュにフラッシュされるので、次回同じシステムでファイルを開くときにデータを利用できることに満足しています。
バグを再現するプログラムはこちら
object FileChannelFailureDemo extends App {
import java.nio._, java.io._, java.nio.channels._
val raf = new RandomAccessFile("""objects2""", "rw") ; val fc = raf.getChannel
val range = (1 to 20)
val ba = range.map (i => ByteBuffer.wrap(Array.ofDim[Byte](i)))
fc.write(ba.toArray) //> res0: Long = 136
val expectedLength = range.foldLeft(0){case (acc, i) => acc + i}
//> expectedLength : Int = 210
// 1+2+..+20 = 20*21/2 = 210 // epxected len
// 1+2+..+16 = 16*17/2 = 136 // len of file if only 16 buffers written
// assertion here, file size 136 != 210
assert(raf.length == expectedLength , raf.length + " != " + expectedLength)
//> java.lang.AssertionError: assertion failed: 136 != 210
//| at scala.Predef$.assert(Predef.scala:165)
println("succeeded, file size is " + raf.length)
fc.close; raf.close
}
不思議なことに、そのリモート マシンでは再現されません。この短いプログラムをコンソールから実行しても表示されませんが、このコードはワークシートから実行されたアサーションに失敗します。プログラムの状態に依存するようです。同じ JRE で失敗または成功する可能性があります。