still a bit janky (the prototype/tiles join ordering isn't right) but it mostly works, and is performant

This commit is contained in:
Adam 2022-03-13 12:37:43 +01:00
parent 8bb73b8291
commit 6bb514afc4

View file

@ -19,19 +19,28 @@ import dev.adamko.kafkatorio.processor.serdes.kxsBinary
import dev.adamko.kotka.extensions.consumedAs import dev.adamko.kotka.extensions.consumedAs
import dev.adamko.kotka.extensions.groupedAs import dev.adamko.kotka.extensions.groupedAs
import dev.adamko.kotka.extensions.materializedAs import dev.adamko.kotka.extensions.materializedAs
import dev.adamko.kotka.extensions.materializedWith
import dev.adamko.kotka.extensions.producedAs import dev.adamko.kotka.extensions.producedAs
import dev.adamko.kotka.extensions.repartitionedAs
import dev.adamko.kotka.extensions.streams.filter import dev.adamko.kotka.extensions.streams.filter
import dev.adamko.kotka.extensions.streams.flatMap import dev.adamko.kotka.extensions.streams.flatMap
import dev.adamko.kotka.extensions.streams.mapValues import dev.adamko.kotka.extensions.streams.mapValues
import dev.adamko.kotka.extensions.streams.peek import dev.adamko.kotka.extensions.streams.peek
import dev.adamko.kotka.extensions.streams.reduce import dev.adamko.kotka.extensions.streams.reduce
import dev.adamko.kotka.extensions.streams.toTable
import dev.adamko.kotka.extensions.tableJoined
import dev.adamko.kotka.extensions.tables.filter
import dev.adamko.kotka.extensions.tables.join import dev.adamko.kotka.extensions.tables.join
import dev.adamko.kotka.extensions.tables.mapValues
import dev.adamko.kotka.extensions.tables.toStream import dev.adamko.kotka.extensions.tables.toStream
import dev.adamko.kotka.kxs.serde import dev.adamko.kotka.kxs.serde
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import org.apache.kafka.streams.StreamsBuilder import org.apache.kafka.streams.StreamsBuilder
import org.apache.kafka.streams.Topology import org.apache.kafka.streams.Topology
@ -71,19 +80,6 @@ fun groupMapChunks(
val tileProtoColourDict: KTable<FactorioServerId, TileColourDict> = val tileProtoColourDict: KTable<FactorioServerId, TileColourDict> =
tileProtoColourDictionary(protosStream) tileProtoColourDictionary(protosStream)
// val mapTilesStream: KStream<FactorioServerId, FactorioEvent> =
// builder.stream(
// "kafkatorio.${KafkatorioPacket.PacketType.EVENT}.${FactorioObjectData.ObjectName.LuaTiles}",
// consumedAs("consume.map-tiles", jsonMapper.serde(), jsonMapper.serde())
// )
//
// val mapChunksStream: KStream<FactorioServerId, FactorioEvent> =
// builder.stream(
// "kafkatorio.${KafkatorioPacket.PacketType.EVENT}.${FactorioObjectData.ObjectName.MapChunk}",
// consumedAs("consume.map-chunks", jsonMapper.serde(), jsonMapper.serde())
// )
val mapChunksUpdatesStream: KStream<FactorioServerId, FactorioEventUpdatePacket> = val mapChunksUpdatesStream: KStream<FactorioServerId, FactorioEventUpdatePacket> =
builder.stream( builder.stream(
mapChunkUpdatesTopicName, mapChunkUpdatesTopicName,
@ -93,19 +89,9 @@ fun groupMapChunks(
jsonMapper.serde<FactorioEventUpdatePacket>(), jsonMapper.serde<FactorioEventUpdatePacket>(),
) )
) )
// .repartition(
// repartitionedAs(
// "map-chunk-updates.repartition.change-serde",
// kxsBinary.serde<FactorioServerId>(),
// kxsBinary.serde<FactorioEventUpdatePacket>(),
// numberOfPartitions = 3,
// )
// )
val groupedMapChunkTiles: KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>> = val groupedMapChunkTiles: KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>> =
groupTilesIntoChunksWithColours( groupTilesIntoChunksWithColours(
// mapTilesStream,
// mapChunksStream,
mapChunksUpdatesStream, mapChunksUpdatesStream,
tileProtoColourDict, tileProtoColourDict,
) )
@ -133,6 +119,11 @@ fun groupMapChunks(
return builder.build() return builder.build()
} }
private val coroutineContext: CoroutineContext =
Dispatchers.Default + SupervisorJob() + CoroutineName("groupTilesIntoChunksWithColours")
private fun groupTilesIntoChunksWithColours( private fun groupTilesIntoChunksWithColours(
mapChunkUpdatePacketsStream: KStream<FactorioServerId, FactorioEventUpdatePacket>, mapChunkUpdatePacketsStream: KStream<FactorioServerId, FactorioEventUpdatePacket>,
tileProtoColourDict: KTable<FactorioServerId, TileColourDict>, tileProtoColourDict: KTable<FactorioServerId, TileColourDict>,
@ -171,29 +162,36 @@ private fun groupTilesIntoChunksWithColours(
val standardChunkSize = ChunkSize.MAX val standardChunkSize = ChunkSize.MAX
val flatMappedChunkTiles: List<Pair<ServerMapChunkId, ServerMapChunkTiles<TileProtoHashCode>>> = val flatMappedChunkTiles: List<Pair<ServerMapChunkId, ServerMapChunkTiles<TileProtoHashCode>>> =
mapTiles // println("[${System.currentTimeMillis()}] Flat-mapped ${flatMappedChunkTiles.size} chunk, with ${flatMappedChunkTiles.joinToString { it.second.map.size.toString() }} tiles")
.tiles runBlocking(coroutineContext) {
.groupBy { tile ->
tile.position.toMapChunkPosition(standardChunkSize.tilesPerChunk)
}
.map { (chunkPos, tiles) ->
val chunkId = ServerMapChunkId( mapTiles
serverId = serverId, .tiles
chunkPosition = chunkPos, .groupBy { tile ->
surfaceIndex = mapTiles.surfaceIndex, tile.position.toMapChunkPosition(standardChunkSize.tilesPerChunk)
chunkSize = standardChunkSize, }
) .map { (chunkPos, tiles) ->
val tilesMap = tiles.associate { it.position to TileProtoHashCode(it) } async {
val chunkTiles = ServerMapChunkTiles(chunkId, tilesMap) val chunkId = ServerMapChunkId(
serverId = serverId,
chunkPosition = chunkPos,
surfaceIndex = mapTiles.surfaceIndex,
chunkSize = standardChunkSize,
)
chunkId to chunkTiles val tilesMap = tiles.associate { it.position to TileProtoHashCode(it) }
}
val chunkTiles = ServerMapChunkTiles(chunkId, tilesMap)
chunkId to chunkTiles
}
}.awaitAll()
// println("[${System.currentTimeMillis()}] Flat-mapped ${flatMappedChunkTiles.size} chunk, with ${flatMappedChunkTiles.joinToString { it.second.map.size.toString() }} tiles") // println("[${System.currentTimeMillis()}] Flat-mapped ${flatMappedChunkTiles.size} chunk, with ${flatMappedChunkTiles.joinToString { it.second.map.size.toString() }} tiles")
}
flatMappedChunkTiles flatMappedChunkTiles
} }
.filter("server-map-data.tiles.filter-not-empty") { _, value -> .filter("server-map-data.tiles.filter-not-empty") { _, value ->
@ -214,13 +212,74 @@ private fun groupTilesIntoChunksWithColours(
kxsBinary.serde<ServerMapChunkTiles<TileProtoHashCode>>(), kxsBinary.serde<ServerMapChunkTiles<TileProtoHashCode>>(),
) )
) { chunkTiles, other -> chunkTiles + other } ) { chunkTiles, other -> chunkTiles + other }
.toStream("server-map-data.tiles.repartition.to-stream")
.repartition(
repartitionedAs(
"server-map-data.tiles.repartition.config",
kxsBinary.serde<ServerMapChunkId>(),
kxsBinary.serde<ServerMapChunkTiles<TileProtoHashCode>?>(),
3,
)
).toTable(
"server-map-data.tiles.repartition.to-table",
materializedAs(
"server-map-data.tiles.repartition.to-table.store",
kxsBinary.serde<ServerMapChunkId>(),
kxsBinary.serde<ServerMapChunkTiles<TileProtoHashCode>?>(),
)
)
.filter("server-map-data.tiles.repartition.filter-not-null") { _: ServerMapChunkId, v: ServerMapChunkTiles<TileProtoHashCode>? ->
v != null
}
.mapValues(
"server-map-data.tiles.repartition.map-not-null",
materializedWith(
kxsBinary.serde<ServerMapChunkId>(),
kxsBinary.serde<ServerMapChunkTiles<TileProtoHashCode>>(),
)
) { _: ServerMapChunkId, v: ServerMapChunkTiles<TileProtoHashCode>? ->
v as ServerMapChunkTiles<TileProtoHashCode>
}
// repartition to try and fix KTable-KTable joining skipping records
val tileProtoColourDictRepartitioned =
tileProtoColourDict
.toStream("tileProtoColourDict.repartition.to-stream")
.repartition(
repartitionedAs(
"tileProtoColourDict.repartition.config",
kxsBinary.serde<FactorioServerId>(),
kxsBinary.serde<TileColourDict?>(),
3,
)
).toTable(
"tileProtoColourDict.repartition.to-table",
materializedAs(
"tileProtoColourDict.repartition.to-table.store",
kxsBinary.serde<FactorioServerId>(),
kxsBinary.serde<TileColourDict?>(),
)
)
.filter("tileProtoColourDict.repartition.filter-not-null") { _: FactorioServerId, v: TileColourDict? ->
v != null
}
.mapValues(
"tileProtoColourDict.repartition.map-not-null",
materializedWith(
kxsBinary.serde<FactorioServerId>(),
kxsBinary.serde<TileColourDict>(),
)
) { _: FactorioServerId, v: TileColourDict? ->
v as TileColourDict
}
val colourisedChunkTable: KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>> = val colourisedChunkTable: KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>> =
chunkedTilesTable chunkedTilesTable
.join( .join(
other = tileProtoColourDict, other = tileProtoColourDictRepartitioned,
name = "server-map-data.join-tiles-with-prototypes", tableJoined = tableJoined("server-map-data.join-tiles-with-prototypes"),
materialized = materializedAs( materialized = materializedAs(
"server-map-data.join-tiles-with-prototypes.store", "server-map-data.join-tiles-with-prototypes.store",
kxsBinary.serde<ServerMapChunkId>(), kxsBinary.serde<ServerMapChunkId>(),