High Availability & Scalable ‣ So, you can focus on creating value ‣ Low cost ‣ Free tier ‣ Pay only for compute time ‣ No charge when code is not running ίετɿ ແྉ͋ΓɺίϯϐϡʔςΟϯά࣌ؒʹରͯ͠՝ۚ
context switch cost to my brain ‣ Scala in server app, Python in X, Node.js in Y, Go in Z… ‣ powers of concentration is precious resource ίϯςΩετεΠονͷ੍ͷͨΊ ͜Ε1ZUIPO ͋Ε/PEF ͋ͬͪ(Pͱ͍͏ͷͳΔ͘ආ͚͍ͨ
context switch cost to my brain ‣ Scala in server app, Python in X, Node.js in Y, Go in Z… ‣ powers of concentration is precious resource ‣ Types ‣ Lambda handler is rarely maintained ‣ Information richness comes from types save time to understand code -BNCEBϋϯυϥҰॻ͔ΕΔͱ์ஔ͞Ε͕ͪ ܕͷදݱྗίʔυͷཧղΛॿ͚ͯ͘ΕΔ
context switch cost to my brain ‣ Scala in server app, Python in X, Node.js in Y, Go in Z… ‣ powers of concentration is precious resource ‣ Types ‣ Lambda handler is rarely maintained ‣ Information richness comes from types save time to understand code ‣ Test ‣ Able to use testing library you get used to ׳ΕͨςετϥΠϒϥϦΛ͏͜ͱ͕Ͱ͖Δ
on Lambda is too slow!" ‣ So called "cold-start problem" ‣ There is limitation of function package size ‣ Jar size: up to 50 MB ‣ Need to care about libraries to depend on ؔύοέʔδͷαΠζ.#Ҏͱ͍͏੍ݶଘࡏ͢Δ
is? ‣ How can we make package size small? ‣ How can we run Scala on AWS Lambda in a snappy way? Ͳͷ͘Β͍͍ͷ͔ʁͲ͏ͨ͠ΒύοέʔδαΠζΛখ͘͞Ͱ͖Δ͔ʁ Ͳ͏ͨ͠Β-BNCEBͰ4DBMBΛαΫαΫಈ͔͢͜ͱ͕Ͱ͖Δ͔ʁ
Download code Start new container Bootstrap runtime Run code Freeze the container 5. Freeze the container and wait for a next invocation However there's no guarantee it will run on the same instance. Container may be removed.
Note that downloading jar and spinning-up JVM relatively take time. What is cold-start? We should know Lambda lifecycle! ίʔϧυελʔτίʔυͷμϯϩʔυ͔Β࣮ߦ·ͰΛܦΔ KBSͷμϯϩʔυ+7.ͷىಈൺֱత͕͔͔࣌ؒΔॲཧ Download code Start new container Bootstrap runtime Run code Freeze the container
there's no spin-up time. This is why each invocations after the first is fast. What is cold-start? We should know Lambda lifecycle! ΥʔϜελʔτͱ-BNCBΠϯελϯε͕࠶ར༻͞ΕΔ࣮ߦ ͜Ε͕ճҎ߱ͷݺͼग़͕͔ͬͨ͠ཧ༝ Download code Start new container Bootstrap runtime Run code Freeze the container
there is no available container, cold-start happens ‣ Lambda containers have the limitation that they can handle only single request at a time ‣ If two requests come in at once, two containers are required to handle these requests ‣ If there is no available one, it starts from container creation ෳಉ࣌ϦΫΤετͳͲɺ࣮ߦՄೳͳίϯςφ͕ଘࡏ͠ͳ͍߹ʹى ͖Δ
there is no available container, cold-start happens ‣ Lambda containers have the limitation that they can handle only single request at a time ‣ If two requests come in at once, two containers are required to handle these requests ‣ If there is no available one, it starts from container creation ‣ When a large request spike comes, the throughput problem occurs or it exceed Lambda's concurrency limitation େ͖ͳϦΫΤετͷεύΠΫ͕དྷͨ߹ʹɺεϧʔϓοτ͕ʹͳͬͨΓ -BNCEBͷಉ࣮࣌ߦͷ੍ݶʹͻ͔͔ͬͬͯ͠·͏͜ͱ
footprint of JVM spin-up is relatively large ‣ CPU power is proportional to memory size ‣ Function package size ‣ The larger jar size is, the more download time takes ؔύοέʔδͷαΠζ େ͖͚Εେ͖͍΄Ͳɺμϯϩʔυʹ͕͔͔࣌ؒΔ
footprint of JVM spin-up is relatively large ‣ CPU power is proportional to memory size ‣ Function package size ‣ The larger jar size is, the more download time takes ؔύοέʔδͷαΠζ େ͖͚Εେ͖͍΄Ͳɺμϯϩʔυʹ͕͔͔࣌ؒΔ
footprint of JVM spin-up is relatively large ‣ CPU power is proportional to memory size ‣ Function package size ‣ The larger jar size is, the more download time takes ‣ Number of class files to be loaded ‣ Large number of classes to be loaded and initialized introduces large amount of latency ΫϥεϑΝΠϧͷ ϩʔυ͓ΑͼॳظԽ͞ΕΔΫϥεϑΝΠϧ͕ଟ͚Εɺͦͷ͘ͳΔ
footprint of JVM spin-up is relatively large ‣ CPU power is proportional to memory size ‣ Function package size ‣ The larger jar size is, the more download time takes ‣ Number of class files to be loaded ‣ Large number of classes to be loaded and initialized introduces large amount of latency ‣ VPC, etc. ͦͷଞɺ71$ͳͲӨڹ
disadvantage ‣ JVM spin-up memory footprint ‣ Jar size to be downloaded ‣ Number of classes to be loaded and initialized ىಈ࣌ͷϝϞϦϑοτϓϦϯτμϯϩʔυ͢ΔKBSͷαΠζɺ ΫϥεϑΝΠϧͷϩʔυͱॳظԽͳͲͷͰɺ+BWBϥϯλΠϜෆར
disadvantage ‣ JVM spin-up memory footprint ‣ Jar size to be downloaded ‣ Number of classes to be loaded and initialized ‣ Scala's another disadvantage ‣ Scala class files tend to be numerous 4DBMBͷΫϥεϑΝΠϧ͕ଟ͘ͳΓ͕ͪͳͷෆརͳ
disadvantage ‣ JVM spin-up memory footprint ‣ Jar size to be downloaded ‣ Number of classes to be loaded and initialized ‣ Scala's another disadvantage ‣ Scala class files tend to be numerous ‣ Thus, Scala cold-start in Java runtime takes longer than other language runtime ‣ even "Hello world", it took 8s ‣ when in warm-start, 0.5ms Αͬͯɺ4DBMBY+BWBϥϯλΠϜͷίʔϧυελʔτ ଞͷϥϯλΠϜΑΓ͕͔͔࣌ؒͬͯ͠·͏
great, the short-lived Lambda instance doesn't take advantage of the characteristics of that ‣ Such as dynamic class loading or HotSpot JIT compile, etc. +7.Ғେ͕ͩɺੜଘظ͕͍ؒ-BNCEBΠϯελϯεͰ ͦͷྑ͍ಛੑ͕׆͔ͮ͠Β͍
great, the short-lived Lambda instance doesn't take advantage of the characteristics of that ‣ Such as dynamic class loading or HotSpot JIT compile, etc. ‣ It might be awesome if it runs without JVM! ‣ Not to wait JVM spin-up and loading classes ‣ Make executable package size small ‣ Keep memory footprint small +7.ͳ͠Ͱ࣮ߦͰ͖ͨΒྑ͍݁ՌʹͳΔ͔͠Εͳ͍ʂ +7.ͷεϐϯΞοϓΛͨͣɺؔύοέʔδͱϝϞϦ༻ྔΛখ͘͞
in the Lambda execution environment is available ‣ Simple HTTP based interface ‣ to get the event payload for a new invocation ‣ to return back the response from the function -BNCEBͷڥͰ࣮ߦͰ͖ΔݴޠͳΒԿͰར༻Մೳ ϦΫΤετϨεϙϯεͷͨΊͷγϯϓϧͳ)551ΠϯλϑΣʔεΛ࣋ͭ
run JVM langs, JS, Python, Ruby, etc. with zero overhead ‣ SubstrateVM ‣ A GraalVM component that allows AOT compilation of Java code into executable native images ‣ Community Edition / Enterprise Edition 0SBDMF͕։ൃ͍ͯ͠Δ1PMZHMPU7. 4VCTUSBUF7.࣮ߦՄೳͳωΠςΟϒΠϝʔδΛੜ͢Δίϯϙʔωϯτ
Subatomic Java" framework ‣ Micronaut Framework https://micronaut.io ‣ A full-stack framework for microservice and serverless applications ‣ Project Helidon https://helidon.io ‣ A collection of Java libraries for writing microservices (SBBM7.ͰωΠςΟϒΠϝʔδΛੜ͢Δ+BWBϑϨʔϜϫʔΫ
complex a bit ‣ Write Scala Native code ‣ Build the Amazon Linux based docker container ‣ Install required libs such as LLVM, curl, openssl, re2, etc. ‣ Publish those as a Lambda Layer "NB[PO-JOVYϕʔεͷ%PDLFSίϯςφΛϏϧυ͢Δɻ ඞཁͳϥΠϒϥϦΛΠϯετʔϧ͠ɺͦΕΒΛ-BNCEB-BZFSͱͯ͠ஔ
complex a bit ‣ Write Scala Native code ‣ Build the Amazon Linux based docker container ‣ Install required libs such as LLVM, curl, openssl, re2, etc. ‣ Publish those as a Lambda Layer ‣ Build executable with the docker container and deploy %PDLFSΛ࣮ͬͯߦՄೳϑΝΠϧΛϏϧυ͠ɺσϓϩΠ
argonaut._, Argonaut._ object Hello { def main(args: Array[String]): Unit = { implicit val backend = CurlBackend() implicit val requestCodec = casecodec1(Request.apply, Request.unapply)("name") val runtimeApi = System.getenv("AWS_LAMBDA_RUNTIME_API") while (true) { val next = sttp.get(uri"http://$runtimeApi/2018-06-01/runtime/invocation/next").send() val requestId = next.headers("Lambda-Runtime-Aws-Request-Id") val request = Parse.decodeOption[Request](next.unsafeBody).get val message = Json("message" -> jString(s"Hello, ${request.name}!")).nospaces sttp.post( uri"http://$runtimeApi/2018-06-01/runtime/invocation/$requestId/response" ).body(message).send() } } } case class Request(name: String)
path, etc. import com.softwaremill.sttp._ import argonaut._, Argonaut._ object Hello { def main(args: Array[String]): Unit = { implicit val backend = CurlBackend() implicit val requestCodec = casecodec1(Request.apply, Request.unapply)("name") val runtimeApi = System.getenv("AWS_LAMBDA_RUNTIME_API") while (true) { val next = sttp.get(uri"http://$runtimeApi/2018-06-01/runtime/invocation/next").send() val requestId = next.headers("Lambda-Runtime-Aws-Request-Id") val request = Parse.decodeOption[Request](next.unsafeBody).get val message = Json("message" -> jString(s"Hello, ${request.name}!")).nospaces sttp.post( uri"http://$runtimeApi/2018-06-01/runtime/invocation/$requestId/response" ).body(message).send() } } } case class Request(name: String) ॳظԽॲཧϥϯλΠϜ"1*ͷύεऔಘͳͲ
API and parse as request import com.softwaremill.sttp._ import argonaut._, Argonaut._ object Hello { def main(args: Array[String]): Unit = { implicit val backend = CurlBackend() implicit val requestCodec = casecodec1(Request.apply, Request.unapply)("name") val runtimeApi = System.getenv("AWS_LAMBDA_RUNTIME_API") while (true) { val next = sttp.get(uri"http://$runtimeApi/2018-06-01/runtime/invocation/next").send() val requestId = next.headers("Lambda-Runtime-Aws-Request-Id") val request = Parse.decodeOption[Request](next.unsafeBody).get val message = Json("message" -> jString(s"Hello, ${request.name}!")).nospaces sttp.post( uri"http://$runtimeApi/2018-06-01/runtime/invocation/$requestId/response" ).body(message).send() } } } case class Request(name: String) OFYU"1*Λୟ͖ɺ݁ՌΛ3FRVFTUΦϒδΣΫτͱͯ͠ύʔε
response API import com.softwaremill.sttp._ import argonaut._, Argonaut._ object Hello { def main(args: Array[String]): Unit = { implicit val backend = CurlBackend() implicit val requestCodec = casecodec1(Request.apply, Request.unapply)("name") val runtimeApi = System.getenv("AWS_LAMBDA_RUNTIME_API") while (true) { val next = sttp.get(uri"http://$runtimeApi/2018-06-01/runtime/invocation/next").send() val requestId = next.headers("Lambda-Runtime-Aws-Request-Id") val request = Parse.decodeOption[Request](next.unsafeBody).get val message = Json("message" -> jString(s"Hello, ${request.name}!")).nospaces sttp.post( uri"http://$runtimeApi/2018-06-01/runtime/invocation/$requestId/response" ).body(message).send() } } } case class Request(name: String) ΈཱͯͨϝοηʔδΛϨεϙϯε"1*ʹૹΔ
Scala Native a bit ‣ Write Scala code ‣ Build fat-jar with sbt-assembly ‣ Build the GraalVM docker container ‣ Community Edition, in this sample (SBBM7.ͷ%PDLFSίϯςφΛϏϧυ͢Δ
Scala Native a bit ‣ Write Scala code ‣ Build fat-jar with sbt-assembly ‣ Build the GraalVM docker container ‣ Community Edition, in this sample ‣ Build executable from jar with the docker and deploy %PDLFSΛͬͯKBS͔Β࣮ߦՄೳϑΝΠϧΛϏϧυ͠ɺσϓϩΠ͢Δ
as Scala Native's Hello world 4DBMB/BUJWFͷ)FMMPXPSMEͱ΄΅ಉ͡ import argonaut._; import Argonaut._; import com.softwaremill.sttp._ object Hello { implicit val backend = HttpURLConnectionBackend() implicit val requestCodec = casecodec1(Request.apply, Request.unapply)("name") def main(args: Array[String]): Unit = { val runtimeApi = System.getenv("AWS_LAMBDA_RUNTIME_API") while (true) { val next = sttp.get(uri"http://$runtimeApi/2018-06-01/runtime/invocation/next").send() val requestId = next.headers("lambda-runtime-aws-request-id").head val request = next.unsafeBody.decodeOption[Request].get val responseJson = Json("message" -> jString(s"Hello, ${request.name}!")).nospaces sttp.post(uri"http://$runtimeApi/2018-06-01/runtime/invocation/$requestId/response") .body(responseJson).send() } } } case class Request(name: String)
‣ There is some tips to use the SDK type safely ‣ In this example, use DynamoDB Document Client "844%,GPS+BWB4DSJQUΛ͏͕ɺܕ҆શʹѻ͏ʹগʑίπ͕͍Δ ͜ͷྫͰ%ZOBNP%#ͷ%PDVNFOU$MJFOUΛ͏
type facade for aws-sdk-js ‣ A fork of balshor/aws-sdk-scalajs-facade ‣ It's also good for learning how to write facade type by yourself FYPFHPBXTTELTDBMBKTGBDBEFͱ͍͏ϑΝαʔυϥΠϒϥϦ͕͋Δ ϑΝαʔυͷॻ͖ํΛֶͿͷʹΑͦ͞͏
library of AWS SDK ‣ So need to implement a client which invokes AWS REST API ‣ cf. rusoto: A third-party AWS SDK for Rust https://rusoto.org 4DBMB/BUJWF൛ͷ"844%,GPS+BWBݱঢ়ͳ͍ ͠"84αʔϏεΛ͍͍ͨ߹ɺΫϥΠΞϯτΛ࣮͢Δඞཁ͕͋Δ
images from Java, however... ‣ There is some limitations of SubstrateVM ‣ AOT compilation doesn't support Dynamic class loading https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md ྫ͑ɺDMBTTͷಈతͳMPBEJOHVOMPBEJOHαϙʔτ͞Ε͍ͯͳ͍
images from Java, however... ‣ There is some limitations of SubstrateVM ‣ AOT compilation doesn't support Dynamic class loading https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md ‣ AWS SDK for Java uses commons-logging which uses ClassLoader... "844%,GPS+BWBͷDPNNPOTMPHHJOH͕$MBTT-PBEFSΛ͍ͬͯΔ
images from Java, however... ‣ There is some limitations of SubstrateVM ‣ AOT compilation doesn't support Dynamic class loading https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md ‣ AWS SDK for Java uses commons-logging which uses ClassLoader... "844%,GPS+BWBͷDPNNPOTMPHHJOH͕$MBTT-PBEFSΛ͍ͬͯΔ
analyzer written in Java ‣ Its name is derived from "ࠇจࣈ" ‣ A traditional small wooden knife to cut Japanese sweets in tea ceremony ,VSPNPKJ+BWBͷຊޠܗଶૉղੳϥΠϒϥϦ ໊લ՛ࢠΛΓ͚Δʮࠇจࣈʯʹ༝དྷ͢ΔʢΒ͍͠ʣ
analyzer written in Java ‣ Its name is derived from "ࠇจࣈ" ‣ A traditional small wooden knife to cut Japanese sweets in tea ceremony ‣ Let's create a morphological analysis API with this! ؆୯ͳܗଶૉղੳ"1*Λ࡞ͬͯΈΑ͏ʂ
as "Tarai-mawashi Function" ‣ High load function; when takeuchi(2n, n, 0), O(n^n) ‣ Slack notification of S3 put with Scala Native ͦͷଞͷαϯϓϧ https://github.com/todokr/scala-on-lambda-snappy
‣ Integration with AWS services ‣ Cons ‣ Need to write some boilerplaty facade for type safe ‣ Need to know Node.js API, ecosystem, manners, etc... ‣ I use this for ‣ DynamoDB accessor ॴίʔυαΠζͷখ͞͞ɺ"84αʔϏεͱͷ࿈ܞ ॴϘΠϥʔϓϨʔτతϑΝαʔυ /PEFKTͷ͕ࣝඞཁ
1.3M) ‣ Cons ‣ Tricky to build for Lambda ‣ Need to check if the library is supported for Scala Native ॴൺֱతখ͍͞ύοέʔδαΠζ ॴ-BNCEB͚ͷϏϧυʹίπ͕͍Δɺ͍͍ͨϥΠϒϥϦ͕ 4DBMB/BUJWFʹରԠ͍ͯ͠Δ͔֬ೝ͢Δඞཁ͕͋Δ
use Scala/Java libraries ‣ Cons ‣ Relatively large package size (hello.zip: 7.3MB) ‣ Tricky to build a bit ‣ Some limitation of AOT compilation ॴ4DBMB+BWBͷϥΠϒϥϦΛ؆୯ʹ͑Δ ॴύοέʔδ͕ͦͦ͜͜େ͖͍ Ϗϧυʹগʑίπ͕͍Δ "05ίϯύΠϧͷ੍ݶ͕͋Δ
history Amazon etc. Notifier /PUJpFS4DBMB+4Y/PEFKTϥϯλΠϜ 4%,͕͑ΔͷͰɺ%ZOBNP%#ͷΞΫηε͕༰қ GraalVM native-image - Using Jsoup (Java library) - Charset ScalaJS x Node.js -Accessing DynamoDB
simple ‣ However, it's powerful on some special occasion ‣ So, we should consider the trade-off ‣ Need to integrate with other AWS service => Scala.js with Node.js runtime "84αʔϏεͱͷ࿈ܞ͕ඞཁ/PEFKTϥϯλΠϜͰ4DBMBKT
simple ‣ However, it's powerful on some special occasion ‣ So, we should consider the trade-off ‣ Need to integrate with other AWS service => Scala.js with Node.js runtime ‣ Need to use Java library & run snappy => Native Image with GraalVM +BWBͷϥΠϒϥϦΛ͍ɺαΫαΫಈ͔͢ඞཁ͕͋Δ (SBBM7.ͰOBUJWFJNBHF
simple ‣ However, it's powerful on some special occasion ‣ So, we should consider the trade-off ‣ Need to integrate with other AWS service => Scala.js with Node.js runtime ‣ Need to use Java library & run snappy => Native Image with GraalVM ‣ Invoked constantly and throughput is not so important => Just use Java8 runtime ఆظతͳݺͼग़͕͋͠Γɺεϧʔϓοτ͕ॏཁͰͳ͍ ૉʹ+BWBϥϯλΠϜΛ͓͏
simple ‣ However, it's powerful on some special occasion ‣ So, we should consider the trade-off ‣ Need to integrate with other AWS service => Scala.js with Node.js runtime ‣ Need to use Java library & run snappy => Native Image with GraalVM ‣ Invoked constantly and throughput is not so important => Just use Java8 runtime ‣ _ => Give up and use other runtime such as Python or Node.js ͦΕҎ֎ͳΒɺఘΊͯ1ZUIPO/PEFKTͳͲଞͷϥϯλΠϜΛ͓͏