Skip to content

Instantly share code, notes, and snippets.

@fommil
Created November 3, 2017 17:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fommil/744bcda4376373eac83243d19cf0fc83 to your computer and use it in GitHub Desktop.
Save fommil/744bcda4376373eac83243d19cf0fc83 to your computer and use it in GitHub Desktop.
package com.example
import cats._, data._, effect._
import doobie.hikari.imports._
import doobie.imports._
import fs2.interop.cats._
import fs2.util.{ Catchable, Suspendable }
import Fragments._
final case class AuroraConfig(
host: String,
port: Int,
database: String,
user: String,
pass: String)
object DoobieAurora {
import DoobieBackcompat._
def hikari(config: AuroraConfig): IO[HikariTransactor[IO]] = {
import config._
for {
tx <- HikariTransactor[IO](
"org.mariadb.jdbc.Driver",
s"jdbc:mariadb://$host:$port/$database?charset=utf8mb4",
user,
pass
)
_ <- tx.configure(hx => hx.setMaximumPoolSize(10))
} yield tx
}
def physical(config: AuroraConfig): Transactor[IO] = {
import config._
DriverManagerTransactor[IO](
"org.mariadb.jdbc.Driver",
s"jdbc:mariadb://$host:$port/$database?charset=utf8mb4",
user,
pass
)
}
}
// supports cats.effect.IO in doobie 0.4
object DoobieBackcompat {
implicit val catsEffect: Suspendable[IO] with Catchable[IO] =
new Suspendable[IO] with Catchable[IO] {
override def pure[A](a: A): IO[A] = IO.pure(a)
override def flatMap[A, B](a: IO[A])(f: A => IO[B]): IO[B] = a flatMap f
override def delay[A](a: => A) = IO(a)
override def suspend[A](fa: => IO[A]) = IO.suspend(fa)
override def fail[A](err: Throwable) = IO.raiseError(err)
override def attempt[A](t: IO[A]) = t.attempt
}
}
package com.example
import java.nio.charset.StandardCharsets
import java.util.Collections
import scala.collection.JavaConverters.iterableAsScalaIterableConverter
import cats.data.NonEmptyList
import cats.effect.IO
import com.amazonaws.services.kinesis.clientlibrary.types.UserRecord
import com.amazonaws.services.lambda.runtime.Context
import com.amazonaws.services.lambda.runtime.events.KinesisEvent
import doobie.util.transactor.Transactor
/**
* AWS Lambda applications should extend from this interface and
* implement the abstract method.
*
* This code cannot be tested locally, so when making changes be sure
* to perform extensive manual testing.
*/
trait AwsLambda {
final def handler(event: KinesisEvent, context: Context): Unit = {
val logger = context.getLogger()
val records = event
.getRecords()
.asScala
.toList
.flatMap { record =>
UserRecord
.deaggregate(Collections.singletonList(record.getKinesis))
.asScala
.map(r => new String(r.getData.array(), StandardCharsets.UTF_8))
}
lazy val aurora = AuroraConfig(
sys.env("AURORA_HOST"),
sys.env("AURORA_PORT").toInt,
sys.env("AURORA_DATABASE"),
sys.env("AURORA_USER"),
sys.env("AURORA_PASSWORD")
)
NonEmptyList.fromList(records).foreach { nel =>
val log = new LambdaLogging(logger)
val tx = DoobieAurora.physical(aurora)
λ(nel)(log, tx).unsafeRunSync()
}
}
def λ(
records: NonEmptyList[String]
)(implicit L: Logging[IO],
T: Transactor[IO]
): IO[Unit]
}
...
val mylambda = project
.enablePlugins(SbtProguard)
.settings(
// https://www.guardsquare.com/en/proguard/manual/introduction
// https://www.guardsquare.com/en/proguard/manual/examples
javaOptions in (Proguard, proguard) := Seq("-Xmx5g"),
proguardOptions in Proguard ++= Seq(
"-dontnote",
"-dontwarn",
"-ignorewarnings",
"-dontoptimize",
"-dontobfuscate",
// our entry method
"""-keep class com.example.MyApp {
void handler(com.amazonaws.services.lambda.runtime.events.KinesisEvent,
com.amazonaws.services.lambda.runtime.Context);
}""",
// reflectively loaded database driver
"""-keep class org.mariadb.jdbc.Driver""",
// it is not enough to request implementations of KinesisEvent /
// Context, we must import their entire public API.
"""-keep public class com.amazonaws.services.lambda.runtime.** { public *; }""",
// AWS reflectively loads DateTime
"""-keep class org.joda.time.DateTime""",
// bugs in proguard's scala support... this is costing ~15MB
"""-keep class scala.** { *; }"""
),
libraryDependencies ++= Seq(
// plus doobie and maybe some other AWS stuff here...
"com.amazonaws" % "amazon-kinesis-client" % "1.8.7",
"com.amazonaws" % "aws-lambda-java-core" % "1.1.0",
"com.amazonaws" % "aws-lambda-java-events" % "2.0.1"
),
TaskKey[Unit]("lambdaPublish", "") := {
import scala.sys.process._
val jar = (proguard in Proguard).value.head
val name = jar.getName
s"aws --profile=adtech s3 cp $jar s3://your-name-here/$name".!
IO.copyFile(jar, file(jar.getName))
}
)
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment