Added support for also changing the aeron driver idle strategies

This commit is contained in:
Robinson 2023-07-24 01:42:27 +02:00
parent 57480735c3
commit 3016618b1c
No known key found for this signature in database
GPG Key ID: 8E7DB78588BD6F5C
5 changed files with 99 additions and 35 deletions

View File

@ -36,6 +36,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import mu.KLogger
import mu.KotlinLogging
import org.agrona.concurrent.AgentTerminationException
import org.agrona.concurrent.IdleStrategy
import org.slf4j.helpers.NOPLogger
import java.io.File
import java.net.BindException
@ -325,6 +326,7 @@ abstract class Configuration protected constructor() {
}
}
/**
* Specify the application ID. This is necessary, as it prevents multiple instances of aeron from responding to applications that
* is not theirs. Because of the shared nature of aeron drivers, this is necessary.
@ -542,6 +544,43 @@ abstract class Configuration protected constructor() {
field = value
}
/**
* The idle strategy used by the Aeron Media Driver to write to the network when in DEDICATED mode. Null will use the aeron defaults
*/
var senderIdleStrategy: IdleStrategy? = null
set(value) {
require(!contextDefined) { errorMessage }
field = value
}
/**
* The idle strategy used by the Aeron Media Driver read from the network when in DEDICATED mode. Null will use the aeron defaults
*/
var receiverIdleStrategy: IdleStrategy? = null
set(value) {
require(!contextDefined) { errorMessage }
field = value
}
/**
* The idle strategy used by the Aeron Media Driver to read/write to the network when in NETWORK_SHARED mode. Null will use the aeron defaults
*/
var sharedIdleStrategy: IdleStrategy? = null
set(value) {
require(!contextDefined) { errorMessage }
field = value
}
/**
* The idle strategy used by the Aeron Media Driver conductor when in DEDICATED mode. Null will use the aeron defaults
*/
var conductorIdleStrategy: IdleStrategy? = null
set(value) {
require(!contextDefined) { errorMessage }
field = value
}
/**
* ## A Media Driver, whether being run embedded or not, needs 1-3 threads to perform its operation.
*
@ -601,10 +640,8 @@ abstract class Configuration protected constructor() {
* (> 4KB) messages and for maximizing throughput above everything else. Various checks during publication and subscription/connection
* setup are done to verify a decent relationship with MTU.
*
*
* However, it is good to understand these relationships.
*
*
* The MTU on the Media Driver controls the length of the MTU of data frames. This value is communicated to the Aeron clients during
* registration. So, applications do not have to concern themselves with the MTU value used by the Media Driver and use the same value.
*
@ -612,13 +649,9 @@ abstract class Configuration protected constructor() {
* An MTU value over the interface MTU will cause IP to fragment the datagram. This may increase the likelihood of loss under several
* circumstances. If increasing the MTU over the interface MTU, consider various ways to increase the interface MTU first in preparation.
*
*
* The MTU value indicates the largest message that Aeron will send as a single data frame.
*
*
* MTU length also has implications for socket buffer sizing.
*
*
* Default value is 1408 for internet; for a LAN, 9k is possible with jumbo frames (if the routers/interfaces support it)
*/
var networkMtuSize = Configuration.MTU_LENGTH_DEFAULT
@ -627,6 +660,12 @@ abstract class Configuration protected constructor() {
field = value
}
var ipcMtuSize = Configuration.MAX_UDP_PAYLOAD_LENGTH
set(value) {
require(!contextDefined) { errorMessage }
field = value
}
/**
* Default initial window length for flow control sender to receiver purposes. This assumes a system free of pauses.
*
@ -781,6 +820,8 @@ abstract class Configuration protected constructor() {
require(networkMtuSize > 0) { "configuration networkMtuSize must be > 0" }
require(networkMtuSize < Configuration.MAX_UDP_PAYLOAD_LENGTH) { "configuration networkMtuSize must be < ${Configuration.MAX_UDP_PAYLOAD_LENGTH}" }
require(ipcMtuSize > 0) { "configuration ipcMtuSize must be > 0" }
require(ipcMtuSize <= Configuration.MAX_UDP_PAYLOAD_LENGTH) { "configuration ipcMtuSize must be <= ${Configuration.MAX_UDP_PAYLOAD_LENGTH}" }
require(sendBufferSize >= 0) { "configuration socket send buffer must be >= 0"}
require(receiveBufferSize >= 0) { "configuration socket receive buffer must be >= 0"}
@ -892,6 +933,7 @@ abstract class Configuration protected constructor() {
val threadingMode get() = config.threadingMode
val networkMtuSize get() = config.networkMtuSize
val ipcMtuSize get() = config.ipcMtuSize
val initialWindowLength get() = config.initialWindowLength
val sendBufferSize get() = config.sendBufferSize
val receiveBufferSize get() = config.receiveBufferSize
@ -905,6 +947,10 @@ abstract class Configuration protected constructor() {
val ipcTermBufferLength get() = config.ipcTermBufferLength
val publicationTermBufferLength get() = config.publicationTermBufferLength
val conductorIdleStrategy get() = config.conductorIdleStrategy
val sharedIdleStrategy get() = config.sharedIdleStrategy
val receiverIdleStrategy get() = config.receiverIdleStrategy
val senderIdleStrategy get() = config.senderIdleStrategy
val aeronErrorFilter get() = config.aeronErrorFilter
var contextDefined
@ -918,15 +964,7 @@ abstract class Configuration protected constructor() {
*/
@Suppress("DuplicatedCode")
fun validate() {
require(networkMtuSize > 0) { "configuration networkMtuSize must be > 0" }
require(networkMtuSize < Configuration.MAX_UDP_PAYLOAD_LENGTH) { "configuration networkMtuSize must be < ${Configuration.MAX_UDP_PAYLOAD_LENGTH}" }
require(sendBufferSize >= 0) { "configuration socket send buffer must be a >= 0"}
require(receiveBufferSize >= 0) { "configuration socket receive buffer must be >= 0"}
require(ipcTermBufferLength > 65535) { "configuration IPC term buffer must be > 65535"}
require(ipcTermBufferLength < 1_073_741_824) { "configuration IPC term buffer must be < 1,073,741,824"}
require(publicationTermBufferLength > 65535) { "configuration publication term buffer must be > 65535"}
require(publicationTermBufferLength < 1_073_741_824) { "configuration publication term buffer must be < 1,073,741,824"}
// already validated! do nothing.
}
/**
@ -956,6 +994,7 @@ abstract class Configuration protected constructor() {
if (connectionCloseTimeoutInSeconds != other.connectionCloseTimeoutInSeconds) return false
if (threadingMode != other.threadingMode) return false
if (networkMtuSize != other.networkMtuSize) return false
if (ipcMtuSize != other.ipcMtuSize) return false
if (initialWindowLength != other.initialWindowLength) return false
if (sendBufferSize != other.sendBufferSize) return false
if (receiveBufferSize != other.receiveBufferSize) return false
@ -968,6 +1007,11 @@ abstract class Configuration protected constructor() {
if (publicationTermBufferLength != other.publicationTermBufferLength) return false
if (aeronErrorFilter != other.aeronErrorFilter) return false
if (conductorIdleStrategy != other.conductorIdleStrategy) return false
if (sharedIdleStrategy != other.sharedIdleStrategy) return false
if (receiverIdleStrategy != other.receiverIdleStrategy) return false
if (senderIdleStrategy != other.senderIdleStrategy) return false
return true
}
}
@ -977,10 +1021,16 @@ abstract class Configuration protected constructor() {
if (forceAllowSharedAeronDriver != other.forceAllowSharedAeronDriver) return false
if (threadingMode != other.threadingMode) return false
if (networkMtuSize != other.networkMtuSize) return false
if (ipcMtuSize != other.ipcMtuSize) return false
if (initialWindowLength != other.initialWindowLength) return false
if (sendBufferSize != other.sendBufferSize) return false
if (receiveBufferSize != other.receiveBufferSize) return false
if (conductorIdleStrategy != other.conductorIdleStrategy) return false
if (sharedIdleStrategy != other.sharedIdleStrategy) return false
if (receiverIdleStrategy != other.receiverIdleStrategy) return false
if (senderIdleStrategy != other.senderIdleStrategy) return false
if (aeronDirectory != other.aeronDirectory) return false
if (uniqueAeronDirectory != other.uniqueAeronDirectory) return false
if (uniqueAeronDirectoryID != other.uniqueAeronDirectoryID) return false
@ -1037,6 +1087,7 @@ abstract class Configuration protected constructor() {
private fun mediaDriverIdNoDir(): Int {
var result = threadingMode.hashCode()
result = 31 * result + networkMtuSize
result = 31 * result + ipcMtuSize
result = 31 * result + initialWindowLength
result = 31 * result + sendBufferSize
result = 31 * result + receiveBufferSize
@ -1063,15 +1114,18 @@ abstract class Configuration protected constructor() {
if (enableIpc != other.enableIpc) return false
if (ipcId != other.ipcId) return false
if (udpId != other.udpId) return false
if (enableRemoteSignatureValidation != other.enableRemoteSignatureValidation) return false
if (connectionCloseTimeoutInSeconds != other.connectionCloseTimeoutInSeconds) return false
if (connectionCheckIntervalNanos != other.connectionCheckIntervalNanos) return false
if (connectionExpirationTimoutNanos != other.connectionExpirationTimoutNanos) return false
if (isReliable != other.isReliable) return false
if (pingTimeoutSeconds != other.pingTimeoutSeconds) return false
if (settingsStore != other.settingsStore) return false
if (serialization != other.serialization) return false
if (maxStreamSizeInMemoryMB != other.maxStreamSizeInMemoryMB) return false
if (pollIdleStrategy != other.pollIdleStrategy) return false
if (sendIdleStrategy != other.sendIdleStrategy) return false

View File

@ -62,7 +62,7 @@ internal class AeronContext(config: Configuration.MediaDriverConfig, logger: KLo
.threadingMode(config.threadingMode)
.mtuLength(config.networkMtuSize)
.ipcMtuLength(config.networkMtuSize)
.ipcMtuLength(config.ipcMtuSize)
.initialWindowLength(config.initialWindowLength)
.socketSndbufLength(config.sendBufferSize)
@ -74,11 +74,18 @@ internal class AeronContext(config: Configuration.MediaDriverConfig, logger: KLo
.sharedNetworkThreadFactory(threadFactory)
.sharedThreadFactory(threadFactory)
// default backoff idle strategy
// .conductorIdleStrategy(BusySpinIdleStrategy.INSTANCE)
// .sharedIdleStrategy(NoOpIdleStrategy.INSTANCE)
// .receiverIdleStrategy(BusySpinIdleStrategy.INSTANCE)
// .senderIdleStrategy(BusySpinIdleStrategy.INSTANCE)
if (config.conductorIdleStrategy != null) {
mediaDriverContext.conductorIdleStrategy(config.conductorIdleStrategy)
}
if (config.sharedIdleStrategy != null) {
mediaDriverContext.sharedIdleStrategy(config.sharedIdleStrategy)
}
if (config.receiverIdleStrategy != null) {
mediaDriverContext.receiverIdleStrategy(config.receiverIdleStrategy)
}
if (config.senderIdleStrategy != null) {
mediaDriverContext.senderIdleStrategy(config.senderIdleStrategy)
}
mediaDriverContext.aeronDirectoryName(config.aeronDirectory!!.path)

View File

@ -23,6 +23,7 @@ import dorkbox.network.rmi.RmiSupportConnection
import io.aeron.Image
import io.aeron.logbuffer.FragmentHandler
import io.aeron.logbuffer.Header
import io.aeron.protocol.DataHeaderFlyweight
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.getAndUpdate
import kotlinx.coroutines.delay
@ -99,7 +100,17 @@ open class Connection(connectionParameters: ConnectionParams<*>) {
*/
val isNetwork = !isIpc
/**
* The largest size a SINGLE message via AERON can be. Because the maximum size we can send in a "single fragment" is the
* publication.maxPayloadLength() function (which is the MTU length less header). We could depend on Aeron for fragment reassembly,
* but that has a (very low) maximum reassembly size -- so we have our own mechanism for object fragmentation/assembly, which
* is (in reality) only limited by available ram.
*/
internal val maxMessageSize = if (isNetwork) {
endPoint.config.networkMtuSize - DataHeaderFlyweight.HEADER_LENGTH
} else {
endPoint.config.ipcMtuSize - DataHeaderFlyweight.HEADER_LENGTH
}
private val listenerManager = atomic<ListenerManager<Connection>?>(null)
@ -184,7 +195,7 @@ open class Connection(connectionParameters: ConnectionParams<*>) {
internal suspend fun send(message: Any, abortEarly: Boolean): Boolean {
// The handshake sessionId IS NOT globally unique
logger.trace { "[$toString0] send: ${message.javaClass.simpleName} : $message" }
return endPoint.write(message, publication, sendIdleStrategy, this@Connection, abortEarly)
return endPoint.write(message, publication, sendIdleStrategy, this@Connection, maxMessageSize, abortEarly)
}
/**

View File

@ -119,14 +119,6 @@ abstract class EndPoint<CONNECTION : Connection> private constructor(val type: C
*/
val serialization: Serialization<CONNECTION>
/**
* The largest size a SINGLE message via AERON can be. Because the maximum size we can send in a "single fragment" is the
* publication.maxPayloadLength() function (which is the MTU length less header). We could depend on Aeron for fragment reassembly,
* but that has a (very low) maximum reassembly size -- so we have our own mechanism for object fragmentation/assembly, which
* is (in reality) only limited by available ram.
*/
private val maxMessageSize = config.networkMtuSize - DataHeaderFlyweight.HEADER_LENGTH
/**
* Read and Write can be concurrent (different buffers are used)
* GLOBAL, single threaded only kryo instances.
@ -475,6 +467,7 @@ abstract class EndPoint<CONNECTION : Connection> private constructor(val type: C
publication: Publication,
sendIdleStrategy: CoroutineIdleStrategy,
connection: Connection,
maxMessageSize: Int,
abortEarly: Boolean
): Boolean {
@Suppress("UNCHECKED_CAST")

View File

@ -36,7 +36,6 @@ import dorkbox.network.serialization.KryoWriter
import dorkbox.os.OS
import dorkbox.util.Sys
import io.aeron.Publication
import io.aeron.protocol.DataHeaderFlyweight
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import mu.KLogger
@ -53,7 +52,7 @@ internal class StreamingManager<CONNECTION : Connection>(private val logger: KLo
private const val GIGABYTE = 1024 * MEGABYTE
private const val TERABYTE = 1024L * GIGABYTE
@Suppress("UNUSED_CHANGED_VALUE")
@Suppress("UNUSED_CHANGED_VALUE", "SameParameterValue")
private fun writeVarInt(internalBuffer: MutableDirectBuffer, position: Int, value: Int, optimizePositive: Boolean): Int {
var p = position
var newValue = value
@ -571,6 +570,7 @@ internal class StreamingManager<CONNECTION : Connection>(private val logger: KLo
*
* @return true if ALL the message blocks were successfully sent by aeron, false otherwise. Exceptions are caught and rethrown!
*/
@Suppress("SameParameterValue")
suspend fun sendFile(
file: File,
publication: Publication,
@ -580,8 +580,7 @@ internal class StreamingManager<CONNECTION : Connection>(private val logger: KLo
connection: CONNECTION,
streamSessionId: Int
): Boolean {
val maxMessageSize = (config.networkMtuSize - DataHeaderFlyweight.HEADER_LENGTH).toLong()
val maxMessageSize = connection.maxMessageSize.toLong()
val fileInputStream = file.inputStream()
// if the message is a file, we xfer the file AS a file, and leave it as a temp file (with a file reference to it) on the remote endpoint