Process slideshow requests in batches, add more memory
This commit is contained in:
+5
-1
@@ -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"]
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)]] = {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user