holy-lambda 2021-11-02

is anyone using Carmine with HL (or even non-native Lambda?)

Doesn't the reserved concurrency work the same? Asking, because I'm curious

yes but it’s $40/month per lambda version. I use many versions so that adds up pretty quick. instead I track recent use of versions and keep those alive with a HL lambda

hmmm: Carmine isn’t well suited to HL. It’s a macro at it’s core https://github.com/ptaoussanis/carmine#connections

I can’t see how that will work with native

Why you think it would not work?

Macro expands during compilation then plain java classes are compiled

Btw are you using other runtimes/languages for Lambda as well? Or only HL and Native?

because the connection spec i.e. hostname is not known at compile time. it’s in the lambda env

I suppose I could hardcode a lookup for all the hostnames so it is known at compile time and not use env vars

I already tested interop with the Jedis lib so I can fallback to https://github.com/tolitius/obiwan

And? I mean. It doesn't matter. Macro expands at compile time

plus I don’t need/want a connection pool in a Lambda env. it’s effectively single threaded so only 1 connection required

I could be of course mistaken. Anyway to the last one I agree. You don't want a pool in AWS Lambda

yeah: env values in HL context are runtime. thus can’t use them to control the connection pool

But envs are also readable without the ctx

System/getenv to the rescue

You can create a connection on init step 🙂 Or you can initialize the connection in Lambda if you really want, but do it only once in an atom?

But yeah. System/getenv is probably what you do want

oh. I didn’t know that lambda env vars could be read that way. thanks. I’ll try it

Yes you can. Actually envs are there, since it's easier to provide native-agent-payloads that way, or in general test the lambdas.

But yeah. HL afterwards is all about Java/Clojure 😄

native:conf fails with an NPE when Carmine is used

Fatal error: java.lang.NullPointerException: null
    at com.oracle.svm.agent.tracing.TraceFileWriter.printValue(TraceFileWriter.java:103)
    at com.oracle.svm.agent.tracing.TraceFileWriter.printArray(TraceFileWriter.java:92)
    at com.oracle.svm.agent.tracing.TraceFileWriter.traceEntry(TraceFileWriter.java:68)
    at com.oracle.svm.agent.tracing.core.Tracer.traceCall(Tracer.java:117)
    at com.oracle.svm.agent.BreakpointInterceptor.traceBreakpoint(BreakpointInterceptor.java:150)
    at com.oracle.svm.agent.BreakpointInterceptor.handleGetMethod(BreakpointInterceptor.java:332)
    at com.oracle.svm.agent.BreakpointInterceptor.getDeclaredMethod(BreakpointInterceptor.java:311)
    at com.oracle.svm.agent.BreakpointInterceptor.onBreakpoint(BreakpointInterceptor.java:957)
    at com.oracle.svm.jni.nativeapi.JNIFunctionPointerTypes$GetMethodIDFunctionPointer.invoke(JNIFunctionPointerTypes.java:-1)
    at com.oracle.svm.agent.JniCallInterceptor.getStaticMethodID(JniCallInterceptor.java:163)

I think I’ll use obiwan instead. no macros required

What GraalVM version?

Yeah, but what image builder

don’t worry about it. Jedis/Obiwan is simpler and easier for the HL use-case

Yeah. But still you should upgrade 😄 I really mean it. There are new images with GraalVM native-image with various variants.

0.6.2 is a way better release. Very close to how stable I want HL to be.

But yeah. Starting from 0.5.0 I promised myself to do as little breaking changes as it's only possible

Also 0.6.2 doesn't puts all the artifacts in local directory (which means your CI will see a speedup)

I’ve been meaning to ask about upgrading. My hope is that it will use less mem for native:executable?

my CI still intermittently runs out of mem

It should both use less memory, compile faster, and also produce smaller artifact. Btw don't you want to use the local agent for CI? That way you would benefit from ARM64. Much better cold starts, faster compile time, better price per 1M invocation.

I’ll start with upgrade for now

then I’ll look into ARM later

got too much to do so I need to not keep chasing new stuff

Yeah. You can generate a new project and just compare the changes or follow the migration guide: https://fierycod.github.io/holy-lambda/#/migration-guide

Yeah totally got your point 🙂

cool. I’ll try it tomorrow and will let you know

stopping work now. have a good day

Wish you the same 🙂 In case of any issues you know where to find me. All the best!

Never used carmine before? What questions regarding Carmine you do have?

more an AWS question: how to access Redis from a HL lambda outside the VPC

current solution is use another HL lambda inside the VPC and invoke it from the outer lambda

this will work for me but it would be nice not to need the inner lambda

Wait. Hmm. As I understand you have a Lambda that is in VPC (probably private one) and you would like to call the service that is publicly available?

If yes you would need NAT Gateway for that.

turns out I don’t. a lambda outside a VPC can call a lambda inside a VPC without NAT.

since NAT is expensive, this is a good solution

and using native lambdas on both sides makes it fast/cheap

I suppose a lambda call using ARN doesn’t use DNS i.e. doesn’t need NAT

works for me in prod already

another good use-case for HL

lambda on inside that talks to Redis runs in 5ms i.e. very cheap

If that's the case it will cost you additional 30$.

I mean for NAT Gateway.

Why does the Redis is not in the VPC?

Redis only runs in a VPC, not allowed outside

but I have internet facing lambdas that need to use the cache

I’ll keep an eye on costs. if inner lambda costs a lot, I’ll switch to NAT gateway

I don’t think it will cost a lot

Ok got it. So you would need the NAT for those lambdas that connect to outer world. Otherwise putting all the lambdas in VPC is a good idea.

It will certainly not cost more

yep, that’s it

HL is a NAT gateway alternative

So you have a native lambdas that call native lambda to get the cache :D

some extra code because it’s Carmine api inside VPC and transit (or nippy) when sent to outside

but that’s easy to write/test

CDK makes setup easy too

Yeah cool. However there might be a case your cache will be called to frequently, so expect some cold starts there.

I’ve got a custom pinger that reduces cold starts. only scale up cold starts happen now

Any idea how to deal with this error:

Exception in agent-context:  #error {
 :cause No implementation of method: :-get-info of protocol: #'cognitect.aws.client/ClientSPI found for class: clojure.lang.Delay
I am trying to access secrets from aws ssm using aws-api from cognitect. I am running the latest holy-lambda, 0.6.2. This works using the babashka aws-api pod, but gives that error when I try to compile native.
(def ssm (delay (aws/client {:api :ssm
                             :region "us-west-2"})))

@michael819 You have a typo in your example.

(agent/in-context
  (let [ddipass (aws/invoke ssm {:op :GetParameter
                                 :request {:Name "ddi-api-password"
                                           :WithDecryption true}})]
    (println (pprint ddipass))))
You have to reference the ssm @ssm

bb hl:native:conf calls a regular JVM Java with tracing, therefore if it fails you have 99% chance it's issue with you code.

Deps.edn

{:deps {org.clojure/clojure                  {:mvn/version "1.10.3"}
        io.github.FieryCod/holy-lambda       {:mvn/version "0.6.2"}
        com.github.clj-easy/graal-build-time {:mvn/version "0.1.4"}
        com.cognitect.aws/api                {:mvn/version "0.8.524"}
        com.cognitect.aws/endpoints          {:mvn/version "1.1.12.93"}
        com.cognitect.aws/ssm                {:mvn/version "814.2.1008.0"}}

 :paths ["src" "resources"]

 :aliases {:uberjar {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
                     :exec-fn      hf.depstar/uberjar
                     :exec-args    {:aot        ["example.core"]
                                    :main-class "example.core"
                                    :jar        ".holy-lambda/build/output.jar"
                                    :jvm-opts   ["-Dclojure.compiler.direct-linking=true"
                                                 "-Dclojure.spec.skip-macros=true"]}}}}
core.cljc
(ns example.core
  (:gen-class)
  (:require
   [cognitect.aws.http.cognitect]
   [cognitect.aws.protocols.query]
   [cognitect.aws.protocols.json]
   [cognitect.aws.protocols.common]
   [cognitect.aws.protocols.rest]
   [cognitect.aws.protocols.rest-xml]
   [cognitect.aws.client.api :as aws]
   [fierycod.holy-lambda.response :as hr]
   [fierycod.holy-lambda.agent :as agent]
   [fierycod.holy-lambda.core :as h]))

(def ssm (delay (aws/client {:api :ssm
                             :region "eu-central-1"})))

(defn ExampleLambda
  [{:keys [event ctx] :as request}]
  (let [ddipass (aws/invoke @ssm {:op :GetParameter
                                  :request {:Name "ddi-api-password"
                                            :withDecryption true}})]
    (hr/json {:event event
              :ddipass ddipass})))

(h/entrypoint [#'ExampleLambda])

(agent/in-context
  (let [ddipass (aws/invoke @ssm {:op :GetParameter
                                 :request {:Name "ddi-api-password"
                                           :WithDecryption true}})]
    (println ddipass)
    ;; (println (pprint ddipass))
    ))

This works for hl:native:conf but fails for hl:native:executable with: jdk.xml.internal.SecuritySupport was unintentionally initialized at build time.

So initialize jdk.xml.internal at build time.

—initialize-at-build-time=jdk.xml.internal @michael819

Sorry I'm newish to this whole thing, where do I do that? Is that an option I add to the bb command?

Put it in :native-image-args in bb.edn

There will be an additional error as far as I can remember

Doh, I checked deps.edn forgot bb.edn is a thing.

I will document the common ways of dealing with native-image errors. https://fierycod.github.io/holy-lambda/#/native-backend

Ok got further it wants some apache libs now. How to add multiple libraries to --initialize-at-build-time? comma seperated list? A whole new --initialize line?

You can do both

Congrats. ;)

You’re very close to getting it working.

Error: Image build request failed with exit status 137
com.oracle.svm.driver.NativeImage$NativeImageError: Image build request failed with exit status 137
	at com.oracle.svm.driver.NativeImage.showError(NativeImage.java:1762)
	at com.oracle.svm.driver.NativeImage.build(NativeImage.java:1473)
	at com.oracle.svm.driver.NativeImage.performBuild(NativeImage.java:1434)
	at com.oracle.svm.driver.NativeImage.main(NativeImage.java:1421)

In the past the errors have kinda given me a pointer, but that one is just flat, no pointer to a file to get more details.

It got much further in compilation, I think it was very close to the end.

I really appreciate the help on troubleshooting this.

Not enough memory

I actually did increase the ram to 4GB, I'll go to 6GB and try again.

At least 6gb. But if you wanna be safe increase to 8gb

Sweet, it compiled!

And it pulled the password from ssm. Thanks very much for the help.

I'm going to play around with the awssdk example now, see if I can compare the 2.

You’re very welcome

that normally means you haven’t captured enough using agent/in-context or using sample requests

are you calling SSM successfully in native:conf step?

That is the error I get from hl:native:conf

I only have this 1 call in the agent context and the lambda function. I have to get the pass word from ssm to do my actual work.

(def ssm (delay (aws/client {:api :ssm
                             :region "us-west-2"})))

(defn TestAPI
  "Test DDI API"
  [{:keys [event ctx] :as request}]

  (let [ddipass (aws/invoke @ssm {:op :GetParameter
                                  :request {:Name "ddi-api-password"
                                            :withDecryption true}})]
    (hr/json {:event event
              :ddipass ddipass})))

(h/entrypoint [#'TestAPI])

(agent/in-context
  (let [ddipass (aws/invoke ssm {:op :GetParameter
                                 :request {:Name "ddi-api-password"
                                           :WithDecryption true}})]
    (println (pprint ddipass))))

Formatter is crap but that's the whole lambda

personally I find it easier to build a mock for aws api requests and use that in conf handling.

often it’s easier to have a mocked aws call in an agent/in-context call than a real call. it still uses all the classes and conf catches it all

I will try your example. Could you paste the entire namespace, and your deps.edn?

Anyway I can second what @steveb8n said. Using official AWS SDK V2 has numerous benefits over cognitect AWS api: • you can mock your functions • X-ray is there • Works better with graalVM native-image • The performance is better • Artifact is smaller I will probably work on making a lite version of AWS Cognitect API that is both faster and GraalVM native-image friendly for clj-easy organization, but this will take a while.