Process slideshow requests in batches, add more memory

This commit is contained in:
Guntis Smaukstelis
2025-02-25 18:19:42 +02:00
parent 3696172c72
commit 5e47a336e6
5 changed files with 55 additions and 23 deletions
+5 -1
View File
@@ -7,4 +7,8 @@ COPY ./target/scala-2.13/WeatherTool-assembly-0.1.1-SNAPSHOT.jar app.jar
VOLUME /app/data VOLUME /app/data
CMD ["java", "-jar", "app.jar"]
# flags setting heap to 256mb and max 384mb, plus g1 garbage collector
# CMD ["java", "-Xms256m", "-Xmx384m", "-XX:+UseG1GC", "-jar", "app.jar"]
# CMD ["java", "-jar", "app.jar"]
CMD ["java", "-Xms512m", "-Xmx600m", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=200", "-jar", "app.jar"]
+1 -1
View File
@@ -18,7 +18,7 @@ primary_region = "waw"
size_gb = 10 size_gb = 10
[[vm]] [[vm]]
memory = 512 memory = 1024
cpu_kind = 'shared' cpu_kind = 'shared'
cpus = 1 cpus = 1
+24 -13
View File
@@ -64,29 +64,40 @@ class DataService(log: Logger[IO]) {
def getAllFileStructure(): IO[List[Grib]] = { def getAllFileStructure(): IO[List[Grib]] = {
for { for {
fileList <- getFileList() fileList <- getFileList()
allStructure <- fileList.parTraverseN(4)(getGribStucture) // limit concurrency to 4 allStructure <- fileList.parTraverseN(4)(getGribStucture) // limit concurrency
} yield allStructure.flatten } yield allStructure.flatten
} }
// private val blockingEC = ExecutionContext.fromExecutorService( private val semaphore = Semaphore[IO](4).unsafeRunSync()
// Executors.newFixedThreadPool(4) // limit concurrency to 4
// ) private val blockingEC = ExecutionContext.fromExecutorService(
// Executors.newFixedThreadPool(4) // limit concurrency
// private val semaphore = Semaphore[IO](4).unsafeRunSync() )
def getBinaryChunk(offset: Int, length: Int, fileName: String): IO[Array[Byte]] = { def getBinaryChunk(offset: Int, length: Int, fileName: String): IO[Array[Byte]] = {
val fileResource = Resource.make( val fileResource = Resource.make(
IO.blocking(new RandomAccessFile(s"$GRIB_FOLDER/$fileName", "r")) IO.blocking(new RandomAccessFile(s"$GRIB_FOLDER/$fileName", "r"))
)(file => IO.blocking(file.close())) )(file => IO.blocking(file.close()))
fileResource.use { file => val logMemory = IO {
IO.blocking { val runtime = Runtime.getRuntime
val buffer = new Array[Byte](length) val usedMemoryMB = (runtime.totalMemory - runtime.freeMemory) / 1024 / 1024
file.seek(offset) val maxMemoryMB = runtime.maxMemory / 1024 / 1024
file.readFully(buffer) println(s"Memory usage before reading $fileName: $usedMemoryMB MB / $maxMemoryMB MB max")
buffer
}
} }
for {
_ <- logMemory
result <- fileResource.use { file =>
IO.blocking {
val buffer = new Array[Byte](length)
file.seek(offset)
file.readFully(buffer)
buffer
}.evalOn(blockingEC)
}
_ <- IO { System.gc() }
} yield result
} }
def getForecasts(): IO[List[(ZonedDateTime, ZonedDateTime)]] = { def getForecasts(): IO[List[(ZonedDateTime, ZonedDateTime)]] = {
+4 -4
View File
@@ -6,7 +6,7 @@ import { drawGrib } from './draw/drawGrib'
import { CROP_BOUNDS } from './DrawView' import { CROP_BOUNDS } from './DrawView'
import styles from './harmonie.module.css' import styles from './harmonie.module.css'
import { handleProgressivePromises } from '../helpers/progressivePromises' import { processPromisesInBatches } from '../helpers/progressivePromises'
const METEO_PARAMS: [string, MeteoParam][] = [ const METEO_PARAMS: [string, MeteoParam][] = [
['temperature', { discipline: 0, category: 0, product: 0, levelType: -1, levelValue: -1, subType: 'now' }], ['temperature', { discipline: 0, category: 0, product: 0, levelType: -1, levelValue: -1, subType: 'now' }],
@@ -62,7 +62,7 @@ export const ReferenceTimes: Component<{
.map((grib): [string, undefined] => [grib.time.forecastTime, undefined]) .map((grib): [string, undefined] => [grib.time.forecastTime, undefined])
.sort((a, b) => a[0] > b[0] ? 1 : -1) .sort((a, b) => a[0] > b[0] ? 1 : -1)
setImgList(emptyImgList) setImgList(emptyImgList)
const promiseList = forecastList.map(async (grib): Promise<[string, ImageBitmap]> => { const promiseFnsList = forecastList.map((grib): () => Promise<[string, ImageBitmap]> => async () => {
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
const [messages, buffers, bitmasks] = await fetchGribBinaries(grib, getGribList()) const [messages, buffers, bitmasks] = await fetchGribBinaries(grib, getGribList())
drawGrib(canvas, messages, buffers, bitmasks, cropBounds, contour, isInterpolated) drawGrib(canvas, messages, buffers, bitmasks, cropBounds, contour, isInterpolated)
@@ -72,8 +72,8 @@ export const ReferenceTimes: Component<{
return [grib.time.forecastTime, img] return [grib.time.forecastTime, img]
}) })
handleProgressivePromises( processPromisesInBatches(
promiseList, promiseFnsList,
([forecastDate, img]) => { ([forecastDate, img]) => {
const udpdatedImgList = [...getImgList()] const udpdatedImgList = [...getImgList()]
const idx = udpdatedImgList.findIndex(([d]) => forecastDate === d) const idx = udpdatedImgList.findIndex(([d]) => forecastDate === d)
+21 -4
View File
@@ -1,10 +1,10 @@
export async function handleProgressivePromises<T>( async function processPromises<T>(
promises: Promise<T>[], promiseFns: (() => Promise<T>)[],
onProgress: (result: T) => void, onProgress: (result: T) => void,
): Promise<(T | undefined)[]> { ): Promise<(T | undefined)[]> {
const allPromises = promises.map(async promise => { const allPromises = promiseFns.map(async promise => {
try { try {
const result = await promise; const result = await promise();
onProgress(result) onProgress(result)
return result return result
} catch (error) { } catch (error) {
@@ -17,3 +17,20 @@ export async function handleProgressivePromises<T>(
result.status === 'fulfilled' ? result.value : undefined result.status === 'fulfilled' ? result.value : undefined
) )
} }
export async function processPromisesInBatches<T>(
promiseFns: (() => Promise<T>)[],
onProgress: (result: T) => void,
batchSize = 3,
): Promise<(T | undefined)[]> {
const results = []
while(await promiseFns.length > 0) {
const size = Math.min(batchSize, promiseFns.length)
const batchPromises = promiseFns.splice(0, size)
console.log('process:', batchPromises.length)
const batchResults = await processPromises(batchPromises, onProgress)
results.push(...batchResults)
}
return results
}