Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS Lambda failure: "Could not load platform constants for OpenFlags" #5379

Closed
SFEley opened this issue Oct 22, 2018 · 11 comments
Closed

AWS Lambda failure: "Could not load platform constants for OpenFlags" #5379

SFEley opened this issue Oct 22, 2018 · 11 comments

Comments

@SFEley
Copy link

SFEley commented Oct 22, 2018

Environment

  • JRuby version - 9.2.0.0 (also tried 9.2.1.0 nightly snapshot, same error)
  • Operating system - Amazon Linux, kernel 4.14.62-84.118.amzn2.x86_64
  • Java version - java-1.8.0-openjdk
  • No command line flags or JRUBY_OPTS
  • Gems were not loaded at time of failure (crashes on Ruby init)

Expected Behavior

This is an attempt to run Ruby business logic in the AWS Lambda serverless platform, using Lambda's java8 runtime environment. I have a thin Java class, LambdaHandler.java (gist), that simply sets up a JRuby ScriptingContainer and loads a Ruby handler class in it, then forwards incoming Lambda events to it as they are received.

The JRuby runtime is configured in the class as follows:

 static {
    rubyRuntime = new ScriptingContainer(LocalContextScope.SINGLETHREAD, LocalVariableBehavior.PERSISTENT);
    rubyRuntime.setCompatVersion(CompatVersion.RUBY2_0);
    rubyRuntime.setCompileMode(CompileMode.JIT);
  }

...and gets further setup when an instance of the class is initialized:

    rubyRuntime.runScriptlet("require 'java'");
    rubyRuntime.runScriptlet(String.format("require '%s'", handlerRequire));  // Ruby handler class

    Object handlerObject = rubyRuntime.runScriptlet(handlerName + ".new");
    rubyHandler = rubyRuntime.getInstance(handlerObject, Handleable.class);

The expected behavior is that the rubyHandler object can be passed AWS Lambda JSON data as strings, and the return output will then be returned back to Lambda.

Actual Behavior

Tests are passing successfully on my local development machine (MacOS). When a shadow jar file with JRuby and all of this logic is uploaded to AWS, however, testing any Lambda event fails with the following brief stacktrace

Could not load platform constants for OpenFlags: java.lang.RuntimeException
java.lang.RuntimeException: Could not load platform constants for OpenFlags
	at jnr.constants.platform.ConstantResolver.getConstants(ConstantResolver.java:227)
	at jnr.constants.platform.ConstantResolver.lookupAndCacheConstant(ConstantResolver.java:128)
	at jnr.constants.platform.ConstantResolver.getConstant(ConstantResolver.java:116)
	at jnr.constants.platform.ConstantResolver.longValue(ConstantResolver.java:179)
	at jnr.constants.platform.OpenFlags.intValue(OpenFlags.java:30)
	at org.jruby.RubyFile.createFileClass(RubyFile.java:140)
	at org.jruby.Ruby.initCore(Ruby.java:1547)
	at org.jruby.Ruby.bootstrap(Ruby.java:1318)
	at org.jruby.Ruby.init(Ruby.java:1217)
	at org.jruby.Ruby.newInstance(Ruby.java:362)
	at org.jruby.embed.internal.LocalContext.getRuntime(LocalContext.java:117)
	at org.jruby.embed.internal.SingleThreadLocalContextProvider.getRuntime(SingleThreadLocalContextProvider.java:62)
	at org.jruby.embed.internal.EmbedRubyRuntimeAdapterImpl.runParser(EmbedRubyRuntimeAdapterImpl.java:167)
	at org.jruby.embed.internal.EmbedRubyRuntimeAdapterImpl.parse(EmbedRubyRuntimeAdapterImpl.java:94)
	at org.jruby.embed.ScriptingContainer.parse(ScriptingContainer.java:1227)
	at org.jruby.embed.ScriptingContainer.runScriptlet(ScriptingContainer.java:1287)
	at LambdaHandler.<init>(LambdaHandler.java:53)
	at LambdaHandler.<init>(LambdaHandler.java:44)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)

At the recommendation of enebo in the JRuby IRC channel, I attempted it again with the latest jruby-complete-9.2.1.0-SNAPSHOT.jar from nightly builds. It failed with the same error and a nearly identical stacktrace

From just the class names in the trace, my initial guess is that the Amazon Linux platform isn't being recognized correctly in the JNR Constants, or else some restriction in the Lambda image's filesystem is causing file capability tests to fail. (Restrictions are set so that only the /tmp directory is writable.). I haven't had the opportunity to probe deeper and determine exactly what constant may be failing or why.

My hope is that someone here may be able to shed some light on how to fix it (perhaps by supplying additional information to Java before container setup?) or to work around the problem. Thank you very much in advance for your time and consideration.

@SFEley
Copy link
Author

SFEley commented Oct 23, 2018

Per the issue creation notes, I just put some extra tracing code in my Lambda handler to run and capture the uname -a output for its execution environment. Here's what's it shows:

platform: Linux ip-10-129-106-185 4.14.72-68.55.amzn1.x86_64 #1 SMP Fri Sep 28 21:14:54 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

@mkristian
Copy link
Member

@SFEley I would try IsolatedScriptingContainer as it sets up the jruby-classloader slightly differently.

@SFEley
Copy link
Author

SFEley commented Oct 23, 2018

Thank you for the suggestion @mkristian! I just tried it, and unfortunately got the same error. From the stacktrace looks to me as if it's not having problems finding classes to load, but rather failing upon Ruby language initialization when it sets up the File class. Some constant to do with file modes or such isn't being set correctly, it looks like. I'm trying to investigate further now by understanding the code in jnr-constants but it's been slow going.

@mkristian
Copy link
Member

the constant loading has some classloader fiddling but I was a bit uncertain if the IsolatedSCriptingContainer helps.
Another idea was/is to try to doublicate the constant loading code and try the Thread.currentThread.getContextClassloader to lad the constant. But I have to admit that I did not play around with the code, just looked a bit at the code.

@mkristian
Copy link
Member

@SFEley maybe this still works: https://github.com/mkristian/aws-lambda-jruby/blob/master/src/main/java/AWSLambdaJRuby.java
i.e. I did get this working 3 years ago ;)

@headius
Copy link
Member

headius commented Oct 25, 2018

I suspect that the detection logic to load constants is stumbling on something on Lambda. Would it be possible for you to get the full java -version string from there? Or at least dump the Java system properties (ENV_JAVA or java.lang.System.get_properties from Ruby) so we can see how this platform is being described.

@SFEley
Copy link
Author

SFEley commented Oct 25, 2018

@headius Thank you so much! Ruby is crashing on initialization so I can't get anything from there, but I added some extra trace code to my Java wrapper to dump the system properties into the Lambda logs. Here they are:

java.runtime.name: OpenJDK Runtime Environment
sun.boot.library.path: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/amd64
java.vm.version: 25.181-b13
java.vm.vendor: Oracle Corporation
java.vendor.url: http://java.oracle.com/
path.separator: :
java.vm.name: OpenJDK 64-Bit Server VM
file.encoding.pkg: sun.io
user.country: US
sun.java.launcher: SUN_STANDARD
sun.os.patch.level: unknown
java.vm.specification.name: Java Virtual Machine Specification
user.dir: /tmp
java.runtime.version: 1.8.0_181-b13
java.awt.graphicsenv: sun.awt.X11GraphicsEnvironment
java.endorsed.dirs: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/endorsed
os.arch: amd64
java.io.tmpdir: /tmp
line.separator: 

java.vm.specification.vendor: Oracle Corporation
os.name: Linux
sun.jnu.encoding: UTF-8
java.library.path: /lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.specification.name: Java Platform API Specification
java.class.version: 52.0
java.net.preferIPv4Stack: true
sun.management.compiler: HotSpot 64-Bit Tiered Compilers
os.version: 4.14.72-68.55.amzn1.x86_64
user.home: /home/sbx_user1059
user.timezone: 
java.awt.printerjob: sun.print.PSPrinterJob
file.encoding: UTF-8
java.specification.version: 1.8
java.class.path: /var/runtime/lib/LambdaJavaRTEntry-1.0.jar
user.name: sbx_user1059
java.vm.specification.version: 1.8
sun.java.command: /var/runtime/lib/LambdaJavaRTEntry-1.0.jar
java.home: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre
sun.arch.data.model: 64
user.language: en
java.specification.vendor: Oracle Corporation
awt.toolkit: sun.awt.X11.XToolkit
java.vm.info: mixed mode, sharing
java.version: 1.8.0_181
java.ext.dirs: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/rt.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/jfr.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/classes
java.vendor: Oracle Corporation
file.separator: /
java.vendor.url.bug: http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding: UnicodeLittle
sun.cpu.endian: little
sun.cpu.isalist: 

@SFEley
Copy link
Author

SFEley commented Oct 25, 2018

A bit more info: I've spent some time today setting up an AWS instance with the documented Lambda AMI and kernel version, so that I could try regenerating jnr-constants on it to see if anything was different. I'm about to make a (probably unmergeable) PR to show the diff, but the results are easily summarized:

  • OpenFlags MAX_VALUE is 1052672L in Amazon Linux rather than 4259840L.
  • OpenFlags O_TMPFILE is not defined in Amazon Linux.

From the recent log entries, @headius, I can imagine O_TMPFILE being missing in a current Linux environment will not brighten your day. But those are the only two things different from the constants in 0.9.11. I'm currently working on packaging the constants from my test into a custom JRuby build so that I can see if it resolves my original issue.

@SFEley
Copy link
Author

SFEley commented Oct 25, 2018

There's the PR with the diff in question. I also found a reference to this same limitation in ruby/spec#368, although it's a different failure. (The JRuby initialization crash I'm seeing isn't because anything is trying to make tempfiles, but rather the OpenFlags constants simply seem to fail to load as a unit.)

(Update: I also just noticed #5334 and couldn't help wondering if there was a relationship here too.)

@SFEley
Copy link
Author

SFEley commented Oct 29, 2018

Ooops, so false alarm here. After updating @mkristian's sample project to JRuby 9.2.0.0 and testing it in Lambda, I found that it didn't error the same way. Going down the line to isolate differences between his project and mine, it turned out that the Gradle Shadow jar plugin was the culprit. I was letting it minimize dependencies and it was apparently doing it badly. Going back to the regular Gradle jar builder resolved the issue.

I'm hitting another issue now with some gem dependency loading, but I know it's a completely different problem since Ruby is fully initialized by that point, and I have no doubt I'll figure it out. I'm closing this issue out with thanks to both of you (@mkristian and @headius) and apologies for taking up your time.

@SFEley SFEley closed this as completed Oct 29, 2018
@enebo enebo added this to the Invalid or Duplicate milestone Nov 1, 2018
@mkristian
Copy link
Member

@SFEley welcome anytime with these kind of problems

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants