is it possible to create a class that somehow enfo...
# compiler
a
is it possible to create a class that somehow enforces non-lazy loading of all the classes that it uses in the compiled output? That might sound like a weird question… but the reason I need this, is that AWS Lambda cold starts perform much better when you do as much initialization as possible before it executes your handler function. By default, static blocks of the various classes I’m using in my lambda handler function runs only the first time the class is used. I would like to force it to run statically, alongside the static initialization of my main handler class. My current workaround is to manually load the classes I know that the handler function ends up loading. Which works, but is quite cumbersome 🙂
Copy code
class MyHandler :
    RequestHandler<Map<String, String>, String>
{
    companion object {
        init {
            dataSource
            println(Intrinsics::class.java)
            println(Result::class.java)
            println(Session::class.java)
            println(::handleUserEmailSearch.toString())
            println(Continuation::class.java)
            println(Function::class.java)
            println(Unit::class.java)
        }
    }

    override fun handleRequest(input: Map<String, String>, context: Context): String {
        return serverlessWebResponseDb(dataSource) { dbSess ->
            handleUserEmailSearch(dbSess, input["email"])
        }
    }
}
b
Not the answer you're looking for, but I'd advise avoiding jvm for serverless computing. See if you could switch to kotlin.js for much better serverless performance
e
GraalVM native-image will improve the startup time of your JVM code and have better performance than Kotlin/JS (although I don't use AWS Lambda so I don't know how easy that would be to set up or not)
b
Yeah, that's an option too if you don't mind fiddling a bit to configure reflection. There's also kotlin native, but I think that's the least friendly option.
a
the solution I ended up with was this:
Copy code
class MyHandler :
  RequestHandler<Map<String, String>, String>
{
  companion object {
    init {
      runBlocking {
        serverlessWebResponseDb(dataSource) { dbSess ->
          dbSess.single(queryOf("SELECT 1"), ::mapFromRow)
          JsonWebResponse("")
        }
      }
    }
  }

  override fun handleRequest(
    input: Map<String, String>, 
    context: Context
  ): String {
    return serverlessWebResponseDb(dataSource) { dbSess ->
      handleUserEmailSearch(dbSess, input["email"])
    }
  }
}
That way, most of the code that’s used in the handler, is invoked in the init phase, as a side effect of doing an actual query etc. The code went from spending 600ms on a cold start, to 1300ms. Additionally, it only spends 20ms in the execution phase on a cold start, instead of 5700ms.
good point about using Kotlin JS or Kotlin Native, though, maybe that’s the way to go
don’t think it’ll be easy to get cold start times much lower than 1300ms. Fwiw, warm invocations are super-fast, only 3-4 ms
e
I'd vote for a separate warmup function rather than stuff it in the constructor, but sure that works
fyi GraalVM's native-image also has better performance than Kotlin Native. I'd definitely choose that for an environment like lambda functions if possible
a
as in extracting the contents of the init block to a separate function? Or some other “functional” change?
e
as in extract it, don't call it from the constructor, and call it from the main app entry point instead
a
there isn’t actually any entry point other than “a class” in AWS Lambda afaik
so I believe the only place you can run code during the initialization phase on a cold start is in a static block in the generated Java class, as all AWS Lambda will do is to load the class and call the handleRequest method
and AWS Lambda beefs up CPU and RAM during the initialization phase, so the more work you can do there, the faster your cold starts become
(and you don’t pay for the initialization phase execution time either)
e
never done anything with lambda before, so maybe there's no direct alternative
looks like it is possible but not with Amazon's Java SDK
a
yeah, you have to user the native API, which is to call a HTTP server or something like that
tldr, all the extra time spent in cold start duration that’s not spent in warm start duration, indicates that you’re initializing your lambda outside of the initialization phase
anyway, I’m rambling now… 🙂
e
AoT means a lot of that gets moved to compile time, so you're not even waiting for that at execution time (whether you're paying for it or not)
a
i’m a bit scared of GraalVM actually, I did experiment a little with it, and I got my “hello world” lambda to run, but then I ran into a crash because of a class resolving issue, where the connection pooling library wasn’t able to resolve the class for the H2 driver
so I get the general feeling that you risk hitting a wall where you’ve been in production for a while but then you add some 3rd party library that doesn’t run and then you’re stuck
so it seems that GraalVM can definitely work for a team of experienced and dedicated developers, but I’m wary of recommending Graal to the “general enterprise” 🙂
thanks for all the input btw!
e
I've found the tracing agent to be enough to avoid missing reflection classes, but sure 👍
b
If you have a good test coverage, you can hook that up to the tracing agent and have your reflection configs maintained automatically. Then just be sure to run native tests before releasing
a
ah, these are real pro-tips, thanks!
b
Graalvm works great when used with TDD, no matter the expertise level with the team
p
in my experience, GraalVM is seriously fast, but it's also very high maintenance. I used it for some CLI stuff, and it completely changes the UX by being so fast. But keeping everything up to date when evolving the software/adding dependencies, etc., was a nightmare. To the point where I had to abandon it even though I loved the performance. It wasn't just about reflection config in my case, there were some more complex things going on in some of the libraries IIRC. I think it could be easier with a lambda function, as I expect that would be way simpler/have less functionality and dependencies than the CLI tool in question.
a
that’s probably true. The thing that crashed for me, was a thing you definitely don’t do in lambdas - wire up a JDBC connection pool with an embedded H2 database 😄