@@ -26,7 +26,10 @@ import spray.json.DefaultJsonProtocol._
2626import org .apache .openwhisk .core .{ConfigKeys , WhiskConfig }
2727import org .apache .openwhisk .core .entity .Attachments ._
2828import org .apache .openwhisk .core .entity .Attachments .Attached ._
29- import fastparse ._ , NoWhitespace ._
29+ import fastparse ._
30+ import NoWhitespace ._
31+
32+ import scala .concurrent .duration .{Duration , FiniteDuration }
3033
3134/**
3235 * Reads manifest of supported runtimes from configuration file and stores
@@ -135,11 +138,37 @@ protected[core] object ExecManifest {
135138 /**
136139 * A stemcell configuration read from the manifest for a container image to be initialized by the container pool.
137140 *
138- * @param count the number of stemcell containers to create
141+ * @param initialCount the initial number of stemcell containers to create
139142 * @param memory the max memory this stemcell will allocate
143+ * @param reactive the reactive prewarming prewarmed config, which is disabled by default
140144 */
141- protected [entity] case class StemCell (count : Int , memory : ByteSize ) {
142- require(count > 0 , " count must be positive" )
145+ protected [entity] case class StemCell (initialCount : Int ,
146+ memory : ByteSize ,
147+ reactive : Option [ReactivePrewarmingConfig ] = None ) {
148+ require(initialCount > 0 , " initialCount must be positive" )
149+ }
150+
151+ /**
152+ * A stemcell's ReactivePrewarmingConfig configuration
153+ *
154+ * @param minCount the max number of stemcell containers to exist
155+ * @param maxCount the max number of stemcell containers to create
156+ * @param ttl time to live of the prewarmed container
157+ * @param threshold the executed activation number of cold start in previous one minute
158+ * @param increment increase per increment prewarmed number under per threshold activations
159+ */
160+ protected [core] case class ReactivePrewarmingConfig (minCount : Int ,
161+ maxCount : Int ,
162+ ttl : FiniteDuration ,
163+ threshold : Int ,
164+ increment : Int ) {
165+ require(
166+ minCount >= 0 && minCount <= maxCount,
167+ " minCount must be be greater than 0 and less than or equal to maxCount" )
168+ require(maxCount > 0 , " maxCount must be positive" )
169+ require(ttl.toMillis > 0 , " ttl must be positive" )
170+ require(threshold > 0 , " threshold must be positive" )
171+ require(increment > 0 && increment <= maxCount, " increment must be positive and less than or equal to maxCount" )
143172 }
144173
145174 /**
@@ -344,9 +373,45 @@ protected[core] object ExecManifest {
344373
345374 protected [entity] implicit val imageNameSerdes : RootJsonFormat [ImageName ] = jsonFormat4(ImageName .apply)
346375
347- protected [entity] implicit val stemCellSerdes : RootJsonFormat [StemCell ] = {
376+ protected [entity] implicit val ttlSerdes : RootJsonFormat [FiniteDuration ] = new RootJsonFormat [FiniteDuration ] {
377+ override def write (finiteDuration : FiniteDuration ): JsValue = JsString (finiteDuration.toString)
378+
379+ override def read (value : JsValue ): FiniteDuration = value match {
380+ case JsString (s) =>
381+ val duration = Duration (s)
382+ FiniteDuration (duration.length, duration.unit)
383+ case _ =>
384+ deserializationError(" time unit not supported. Only milliseconds, seconds, minutes, hours, days are supported" )
385+ }
386+ }
387+
388+ protected [entity] implicit val reactivePrewarmingConfigSerdes : RootJsonFormat [ReactivePrewarmingConfig ] = jsonFormat5(
389+ ReactivePrewarmingConfig .apply)
390+
391+ protected [entity] implicit val stemCellSerdes = new RootJsonFormat [StemCell ] {
348392 import org .apache .openwhisk .core .entity .size .serdes
349- jsonFormat2(StemCell .apply)
393+ val defaultSerdes = jsonFormat3(StemCell .apply)
394+ override def read (value : JsValue ): StemCell = {
395+ val fields = value.asJsObject.fields
396+ val initialCount : Option [Int ] =
397+ fields
398+ .get(" initialCount" )
399+ .orElse(fields.get(" count" ))
400+ .map(_.convertTo[Int ])
401+ val memory : Option [ByteSize ] = fields.get(" memory" ).map(_.convertTo[ByteSize ])
402+ val config = fields.get(" reactive" ).map(_.convertTo[ReactivePrewarmingConfig ])
403+
404+ (initialCount, memory) match {
405+ case (Some (c), Some (m)) => StemCell (c, m, config)
406+ case (Some (c), None ) =>
407+ throw new IllegalArgumentException (s " memory is required, just provide initialCount: ${c}" )
408+ case (None , Some (m)) =>
409+ throw new IllegalArgumentException (s " initialCount is required, just provide memory: ${m.toString}" )
410+ case _ => throw new IllegalArgumentException (" both initialCount and memory are required" )
411+ }
412+ }
413+
414+ override def write (s : StemCell ) = defaultSerdes.write(s)
350415 }
351416
352417 protected [entity] implicit val runtimeManifestSerdes : RootJsonFormat [RuntimeManifest ] = jsonFormat8(RuntimeManifest )
0 commit comments