Skip to content

Commit f07708f

Browse files
committed
Don't create prewarm container when used memory reaches the limit
1 parent a6ad9e4 commit f07708f

File tree

2 files changed

+44
-40
lines changed

2 files changed

+44
-40
lines changed

core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/ContainerPool.scala

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -129,44 +129,40 @@ class ContainerPool(childFactory: ActorRefFactory => ActorRef,
129129
val memory = r.action.limits.memory.megabytes.MB
130130

131131
val createdContainer =
132-
// Is there enough space on the invoker for this action to be executed.
133-
if (hasPoolSpaceFor(busyPool ++ prewarmedPool, memory)) {
134-
// Schedule a job to a warm container
135-
ContainerPool
136-
.schedule(r.action, r.msg.user.namespace.name, freePool)
137-
.map(container => (container, container._2.initingState)) //warmed, warming, and warmingCold always know their state
138-
.orElse(
139-
// There was no warm/warming/warmingCold container. Try to take a prewarm container or a cold container.
140-
141-
// Is there enough space to create a new container or do other containers have to be removed?
142-
if (hasPoolSpaceFor(busyPool ++ freePool ++ prewarmedPool, memory)) {
132+
// Schedule a job to a warm container
133+
ContainerPool
134+
.schedule(r.action, r.msg.user.namespace.name, freePool)
135+
.map(container => (container, container._2.initingState)) //warmed, warming, and warmingCold always know their state
136+
.orElse(
137+
// There was no warm/warming/warmingCold container. Try to take a prewarm container or a cold container.
138+
139+
// Is there enough space to create a new container or do other containers have to be removed?
140+
if (hasPoolSpaceFor(busyPool ++ freePool ++ prewarmedPool, memory)) {
141+
takePrewarmContainer(r.action)
142+
.map(container => (container, "prewarmed"))
143+
.orElse {
144+
val container = Some(createContainer(memory), "cold")
145+
incrementColdStartCount(kind, memory)
146+
container
147+
}
148+
} else None)
149+
.orElse(
150+
// Remove a container and create a new one for the given job
151+
ContainerPool
152+
// Only free up the amount, that is really needed to free up
153+
.remove(freePool, Math.min(r.action.limits.memory.megabytes, memoryConsumptionOf(freePool)).MB)
154+
.map(removeContainer)
155+
// If the list had at least one entry, enough containers were removed to start the new container. After
156+
// removing the containers, we are not interested anymore in the containers that have been removed.
157+
.headOption
158+
.map(_ =>
143159
takePrewarmContainer(r.action)
144-
.map(container => (container, "prewarmed"))
145-
.orElse {
146-
val container = Some(createContainer(memory), "cold")
160+
.map(container => (container, "recreatedPrewarm"))
161+
.getOrElse {
162+
val container = (createContainer(memory), "recreated")
147163
incrementColdStartCount(kind, memory)
148164
container
149-
}
150-
} else None)
151-
.orElse(
152-
// Remove a container and create a new one for the given job
153-
ContainerPool
154-
// Only free up the amount, that is really needed to free up
155-
.remove(freePool, Math.min(r.action.limits.memory.megabytes, memoryConsumptionOf(freePool)).MB)
156-
.map(removeContainer)
157-
// If the list had at least one entry, enough containers were removed to start the new container. After
158-
// removing the containers, we are not interested anymore in the containers that have been removed.
159-
.headOption
160-
.map(_ =>
161-
takePrewarmContainer(r.action)
162-
.map(container => (container, "recreatedPrewarm"))
163-
.getOrElse {
164-
val container = (createContainer(memory), "recreated")
165-
incrementColdStartCount(kind, memory)
166-
container
167-
}))
168-
169-
} else None
165+
}))
170166

171167
createdContainer match {
172168
case Some(((actor, data), containerState)) =>
@@ -369,9 +365,15 @@ class ContainerPool(childFactory: ActorRefFactory => ActorRef,
369365

370366
/** Creates a new prewarmed container */
371367
def prewarmContainer(exec: CodeExec[_], memoryLimit: ByteSize, ttl: Option[FiniteDuration]): Unit = {
372-
val newContainer = childFactory(context)
373-
prewarmStartingPool = prewarmStartingPool + (newContainer -> (exec.kind, memoryLimit))
374-
newContainer ! Start(exec, memoryLimit, ttl)
368+
if (hasPoolSpaceFor(busyPool ++ freePool ++ prewarmedPool, memoryLimit)) {
369+
val newContainer = childFactory(context)
370+
prewarmStartingPool = prewarmStartingPool + (newContainer -> (exec.kind, memoryLimit))
371+
newContainer ! Start(exec, memoryLimit, ttl)
372+
} else {
373+
logging.warn(
374+
this,
375+
s"Cannot create prewarm container due to reach the invoker memory limit: ${poolConfig.userMemory.toMB}")
376+
}
375377
}
376378

377379
/** this is only for cold start statistics of prewarm configs, e.g. not blackbox or other configs. */

tests/src/test/scala/org/apache/openwhisk/core/containerpool/test/ContainerPoolTests.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ class ContainerPoolTests
129129
def testContainers(n: Int) = {
130130
val containers = (0 to n).map(_ => TestProbe())
131131
val queue = mutable.Queue(containers: _*)
132-
val factory = (fac: ActorRefFactory) => queue.dequeue().ref
132+
val factory = (fac: ActorRefFactory) => {
133+
queue.dequeue().ref
134+
}
133135
(containers, factory)
134136
}
135137

@@ -320,7 +322,7 @@ class ContainerPoolTests
320322
val pool =
321323
system.actorOf(
322324
ContainerPool
323-
.props(factory, poolConfig(0.MB), feed.ref, List(PrewarmingConfig(1, exec, memoryLimit))))
325+
.props(factory, poolConfig(MemoryLimit.STD_MEMORY), feed.ref, List(PrewarmingConfig(1, exec, memoryLimit))))
324326
containers(0).expectMsg(Start(exec, memoryLimit))
325327
}
326328

0 commit comments

Comments
 (0)