Skip to content

Commit

Permalink
Hold onto randomly allocated ports to avoid port conflicts in concurr…
Browse files Browse the repository at this point in the history
…ent tests (#182)

* Hold onto randomly allocated ports to avoid port conflicts in concurrent tests

* Rename releasePort to onBeforeStartup as per PR feedback
  • Loading branch information
mmollaverdi authored May 30, 2024
1 parent 0c53a3f commit 38c0913
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ import com.github.dockerjava.api.model.Ports
import com.google.common.util.concurrent.AbstractIdleService

class DockerDynamoDbServer private constructor(
override val port: Int
override val port: Int,
private val onBeforeStartup: () -> Unit
) : AbstractIdleService(), TestDynamoDbServer {

override val id = "tempest-docker-dynamodb-local-$port"

override fun startUp() {
onBeforeStartup()
composer.start()

// Temporary client to block until the container is running
Expand Down Expand Up @@ -68,6 +70,6 @@ class DockerDynamoDbServer private constructor(
)

object Factory : TestDynamoDbServer.Factory<DockerDynamoDbServer> {
override fun create(port: Int) = DockerDynamoDbServer(port)
override fun create(port: Int, onBeforeStartup: () -> Unit) = DockerDynamoDbServer(port, onBeforeStartup)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,29 @@ class TestDynamoDbService private constructor(

companion object {
private val defaultPorts = ConcurrentHashMap<String, Int>()
private fun defaultPort(key: String): Int {
private fun defaultPort(key: String): PortHolder {
var releasePort: () -> Unit = {}
// Only pick random port once to share one test server with multiple tests.
return defaultPorts.getOrPut(key, ::pickRandomPort)
val port = defaultPorts.getOrPut(key) {
val socket = allocateRandomPort()
releasePort = { socket.close() }
socket.localPort
}
return PortHolder(port, releasePort)
}

private val runningServers = ConcurrentHashMap.newKeySet<String>()
private val log = getLogger<TestDynamoDbService>()

@JvmStatic
fun create(serverFactory: TestDynamoDbServer.Factory<*>, tables: List<TestTable>, port: Int? = null): TestDynamoDbService {
val port = port ?: defaultPort(serverFactory.toString())
val portHolder = port?.let { PortHolder(it) } ?: defaultPort(serverFactory.toString())
return TestDynamoDbService(
DefaultTestDynamoDbClient(tables, port),
serverFactory.create(port)
DefaultTestDynamoDbClient(tables, portHolder.value),
serverFactory.create(portHolder.value, portHolder.releasePort)
)
}
}

private data class PortHolder(val value: Int, val releasePort: () -> Unit = {})
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ import java.net.InetSocketAddress
import java.net.ServerSocket
import java.net.Socket

fun pickRandomPort(): Int {
ServerSocket(0).use { socket -> return socket.localPort }
fun allocateRandomPort(): ServerSocket {
val socket = ServerSocket(0) //use { socket -> return socket.localPort }
Runtime.getRuntime().addShutdownHook(
Thread { socket.close() }
)
return socket
}

private const val CONNECT_TIMEOUT_MILLIS = 1_000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import com.google.common.util.concurrent.AbstractIdleService
import java.io.File

class JvmDynamoDbServer private constructor(
override val port: Int
override val port: Int,
private val onBeforeStartup: () -> Unit
) : AbstractIdleService(), TestDynamoDbServer {

override val id = "tempest-jvm-dynamodb-local-$port"
Expand All @@ -33,6 +34,7 @@ class JvmDynamoDbServer private constructor(
val libraryFile = libsqlite4javaNativeLibrary()
System.setProperty("sqlite4java.library.path", libraryFile.parent)

onBeforeStartup()
server = ServerRunner.createServerFromCommandLineArgs(
arrayOf("-inMemory", "-port", port.toString())
)
Expand Down Expand Up @@ -87,6 +89,6 @@ class JvmDynamoDbServer private constructor(
}

object Factory : TestDynamoDbServer.Factory<JvmDynamoDbServer> {
override fun create(port: Int) = JvmDynamoDbServer(port)
override fun create(port: Int, onBeforeStartup: () -> Unit) = JvmDynamoDbServer(port, onBeforeStartup)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface TestDynamoDbServer : Service {
val port: Int

interface Factory<T : TestDynamoDbServer> {
fun create(port: Int): T
fun create(port: Int): T = create(port) {}
fun create(port: Int, onBeforeStartup: () -> Unit): T
}
}

0 comments on commit 38c0913

Please sign in to comment.