Document native networking limits
This commit is contained in:
parent
717758149b
commit
121d860043
@ -3,6 +3,12 @@
|
||||
This module provides minimal raw transport networking for Lyng scripts. It is implemented in `lyngio` and backed by Ktor sockets on the JVM and Linux Native, and by Node networking APIs on JS/Node runtimes.
|
||||
|
||||
> **Note:** `lyngio` is a separate library module. It must be explicitly added as a dependency to your host application and initialized in your Lyng scopes.
|
||||
>
|
||||
> **Important native platform limit:** current native TCP/UDP support is backed by a selector with a per-process file descriptor ceiling. On Linux/macOS native targets this makes high-connection-count servers and same-process load tests unsuitable once the process approaches that limit.
|
||||
>
|
||||
> **Recommendation:** for serious HTTP/TCP servers, prefer the JVM target today. On native targets, keep concurrency bounded, batch local load tests in waves, and use multiple worker processes behind a reverse proxy if you need more throughput before the backend is reworked.
|
||||
>
|
||||
> **Need this fixed?** Please open or upvote an issue at <https://github.com/sergeych/lyng/issues> so native high-concurrency networking can be prioritized.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
`lyngio` is a separate library that extends the Lyng core (`lynglib`) with powerful, multiplatform, and secure I/O capabilities.
|
||||
|
||||
> **Important native networking limit:** `lyng.io.net` on current native targets is suitable for modest workloads, local tools, and test servers, but not yet for high-connection-count production servers. For serious HTTP/TCP serving, prefer the JVM target for now. If native high-concurrency networking matters for your use case, please open or upvote an issue at <https://github.com/sergeych/lyng/issues>.
|
||||
|
||||
#### Why a separate module?
|
||||
|
||||
1. **Security:** I/O and process execution are sensitive operations. By keeping them in a separate module, we ensure that the Lyng core remains 100% safe by default. You only enable what you explicitly need.
|
||||
|
||||
@ -2,14 +2,15 @@ import lyng.io.net
|
||||
|
||||
val host = "127.0.0.1"
|
||||
val clientCount = 1000
|
||||
val server = Net.tcpListen(0, host, clientCount, true)
|
||||
val clientWindow = 128
|
||||
val server = Net.tcpListen(0, host, clientWindow, true)
|
||||
val port = server.localAddress().port
|
||||
|
||||
fun payloadFor(index: Int) = "$index:${Random.nextInt()}:${Random.nextInt()}"
|
||||
|
||||
launch {
|
||||
val serverJob = launch {
|
||||
try {
|
||||
while(true) {
|
||||
while (true) {
|
||||
val client = server.accept()
|
||||
launch {
|
||||
try {
|
||||
@ -22,12 +23,21 @@ launch {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (server.isOpen()) {
|
||||
throw e
|
||||
}
|
||||
} finally {
|
||||
if (server.isOpen()) {
|
||||
server.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val replies = (0..<clientCount).map { index ->
|
||||
var completed = 0
|
||||
for (batchStart in 0..<clientCount step clientWindow) {
|
||||
val batchEnd = if (batchStart + clientWindow < clientCount) batchStart + clientWindow else clientCount
|
||||
val replies = (batchStart..<batchEnd).map { index ->
|
||||
val payload = payloadFor(index)
|
||||
launch {
|
||||
val socket = Net.tcpConnect(host, port) as TcpSocket
|
||||
@ -40,7 +50,11 @@ val replies = (0..<clientCount).map { index ->
|
||||
socket.close()
|
||||
}
|
||||
}
|
||||
}.joinAll()
|
||||
}.joinAll()
|
||||
completed += replies.size
|
||||
}
|
||||
|
||||
assertEquals(clientCount, replies.size)
|
||||
assertEquals(clientCount, completed)
|
||||
server.close()
|
||||
serverJob.await()
|
||||
println("OK: $clientCount concurrent tcp clients")
|
||||
|
||||
@ -30,12 +30,13 @@ import kotlin.test.assertEquals
|
||||
|
||||
class LyngNetTcpServerExampleTest {
|
||||
|
||||
private fun concurrentTcpScript(clientCount: Int): String = """
|
||||
private fun concurrentTcpScript(clientCount: Int, clientWindow: Int): String = """
|
||||
import lyng.io.net
|
||||
|
||||
val host = "127.0.0.1"
|
||||
val clientCount = $clientCount
|
||||
val server: TcpServer = Net.tcpListen(0, host, clientCount, true) as TcpServer
|
||||
val clientWindow = $clientWindow
|
||||
val server: TcpServer = Net.tcpListen(0, host, clientWindow, true) as TcpServer
|
||||
val port: Int = server.localAddress().port
|
||||
|
||||
fun payloadFor(index: Int): String {
|
||||
@ -72,7 +73,10 @@ class LyngNetTcpServerExampleTest {
|
||||
}
|
||||
}
|
||||
|
||||
val clientJobs: List<Deferred> = (0..<clientCount).map { index ->
|
||||
var replies: List<Object> = List()
|
||||
for (batchStart in 0..<clientCount step clientWindow) {
|
||||
val batchEnd = if (batchStart + clientWindow < clientCount) batchStart + clientWindow else clientCount
|
||||
val clientJobs: List<Deferred> = (batchStart..<batchEnd).map { index ->
|
||||
val payload = payloadFor(index)
|
||||
launch {
|
||||
val socket: TcpSocket = Net.tcpConnect(host, port) as TcpSocket
|
||||
@ -92,8 +96,9 @@ class LyngNetTcpServerExampleTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
replies += clientJobs.joinAll()
|
||||
}
|
||||
|
||||
val replies = clientJobs.joinAll()
|
||||
val serverReplies = serverJob.await() as List<Object>
|
||||
|
||||
assertEquals(clientCount, replies.size)
|
||||
@ -112,10 +117,10 @@ class LyngNetTcpServerExampleTest {
|
||||
|
||||
val result = withContext(Dispatchers.Default) {
|
||||
withTimeout(20_000) {
|
||||
Compiler.compile(concurrentTcpScript(clientCount = 32)).execute(scope).inspect(scope)
|
||||
Compiler.compile(concurrentTcpScript(clientCount = 1_000, clientWindow = 128)).execute(scope).inspect(scope)
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("\"OK:32\"", result)
|
||||
assertEquals("\"OK:1000\"", result)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user