Skip to content

Commit 44037b9

Browse files
authored
fix: lazily open random access files (#782)
1 parent 8c57873 commit 44037b9

File tree

3 files changed

+32
-16
lines changed

3 files changed

+32
-16
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "0cc34a91-f764-49d8-b24e-b170973e1f9d",
3+
"type": "bugfix",
4+
"description": "Lazily open random access files to prevent exhausting file handles in highly concurrent scenarios",
5+
"issues": [
6+
"awslabs/smithy-kotlin#781"
7+
]
8+
}

runtime/io/jvm/src/aws/smithy/kotlin/runtime/io/RandomAccessFileSource.kt

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,35 @@ import java.io.RandomAccessFile
1313
import kotlin.jvm.Throws
1414

1515
internal class RandomAccessFileSource(
16-
file: File,
16+
private val fileObject: File,
1717
start: Long,
1818
private val endInclusive: Long,
1919
private val timeout: Timeout = Timeout.NONE,
2020
) : okio.Source {
21-
private val file = RandomAccessFile(file, "r")
22-
23-
init {
24-
require(file.exists()) { "cannot create SdkSource, file does not exist: $this" }
25-
require(file.isFile) { "cannot create a SdkSource from a directory: $this" }
21+
private val file by lazy {
22+
require(fileObject.exists()) { "cannot create SdkSource, file does not exist: $this" }
23+
require(fileObject.isFile) { "cannot create a SdkSource from a directory: $this" }
2624
require(start >= 0L) { "start position should be >= 0, found $start" }
27-
require(endInclusive >= 0 && endInclusive <= file.length() - 1) {
25+
require(endInclusive >= 0 && endInclusive <= fileObject.length() - 1) {
2826
"endInclusive should be less than or equal to the length of the file, was $endInclusive"
2927
}
30-
if (start > 0) {
31-
this.file.seek(start)
28+
29+
RandomAccessFile(fileObject, "r").also {
30+
if (start > 0) {
31+
it.seek(start)
32+
}
3233
}
3334
}
3435

3536
private var position = start
36-
private val channel = this.file.channel
3737

38-
override fun toString(): String = "RandomAccessFileSource($file)"
38+
override fun toString(): String = "RandomAccessFileSource($fileObject)"
3939
override fun timeout(): Timeout = timeout
4040

4141
@Throws(IOException::class)
4242
override fun read(sink: Buffer, byteCount: Long): Long {
43+
val channel = file.channel
44+
4345
check(channel.isOpen) { "channel is closed" }
4446
if (position > endInclusive) return -1
4547

@@ -50,7 +52,6 @@ internal class RandomAccessFileSource(
5052
}
5153

5254
override fun close() {
53-
channel.close()
5455
file.close()
5556
}
5657
}

runtime/io/jvm/test/aws/smithy/kotlin/runtime/io/FileSourceTest.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,31 @@ package aws.smithy.kotlin.runtime.io
77

88
import aws.smithy.kotlin.runtime.testing.RandomTempFile
99
import io.kotest.matchers.string.shouldContain
10+
import kotlinx.coroutines.ExperimentalCoroutinesApi
11+
import kotlinx.coroutines.test.runTest
1012
import kotlin.test.Test
1113
import kotlin.test.assertEquals
1214
import kotlin.test.assertFailsWith
1315

16+
@OptIn(ExperimentalCoroutinesApi::class)
1417
class FileSourceTest {
1518
@Test
16-
fun testStartPositionValidation() {
19+
fun testStartPositionValidation() = runTest {
1720
val file = RandomTempFile(1024)
21+
val source = file.source(-1)
1822
assertFailsWith<IllegalArgumentException> {
19-
file.source(-1)
23+
// Files are opened lazily...IAE won't be thrown until a read is attempted
24+
source.readToByteArray()
2025
}.message.shouldContain("start position should be >= 0, found -1")
2126
}
2227

2328
@Test
24-
fun testEndPositionValidation() {
29+
fun testEndPositionValidation() = runTest {
2530
val file = RandomTempFile(1024)
31+
val source = file.source(endInclusive = 1024)
2632
assertFailsWith<IllegalArgumentException> {
27-
file.source(endInclusive = 1024)
33+
// Files are opened lazily...IAE won't be thrown until a read is attempted
34+
source.readToByteArray()
2835
}.message.shouldContain("endInclusive should be less than or equal to the length of the file, was 1024")
2936

3037
file.source(1023).close()

0 commit comments

Comments
 (0)