OneBusAway / onebusaway-alexa

An Java-based app to communicate with Amazon Alexa for devices such as the Amazon Echo
Other
52 stars 18 forks source link

Remove unneeded classes via Proguard #23

Closed barbeau closed 7 years ago

barbeau commented 8 years ago

Our current JAR with dependencies is running at almost 20MB. I'm guessing we could cut down that size substantially by using Proguard. This would speed up testing (uploading a much smaller file), and could potentially help with cold starts as well on Lambda.

@philipmw Do you have any experience setting up Proguard for Java EE applications? I'm used to it for Android, but obviously that differs quite a bit.

I've started some work in this branch using a proguard plugin for Maven: https://github.com/OneBusAway/onebusaway-alexa/tree/proguard

It writes the obfuscated package to "obfuscated.jar". This builds correctly, but I'm still getting missing classes when running on Lambda. So, I assume it's a matter of adding the needed classes to keep to get it running properly again.

philipmw commented 8 years ago

I have no experience with that.

I think 20 MB is fine... this is Java, after all. :) Are you concerned because you believe that a smaller package will reduce app latency on Lambda? I am afraid the effect of file size would be negligible... Lambda can be awful slow at running even a one-line Node.js function, if there are no warm hosts in the fleet.

I personally think latency is a big problem. If your app instance is also laggy like mine, I think we should focus our effort on running the app on direct EC2 rather than Lambda.

barbeau commented 8 years ago

Heard back from the Lambda platform team, via one of contacts I've been working with in Alexa product dev:

Yes, significantly reducing the size of their Jar will help, potentially a lot. We helped another dev with this same problem where they initially had a 20+MB jar file. This was reduced to around 8MB after removing unused components and cold start times decreased from ~12 secs to ~4 secs at the 1GB memory tier.

Also note the latency difference between the very first/second skill invocation and following utterances – the later calls should see significant improvement. This is because of how lambda initializes and reaps instances assigned to your lambda function. So during testing you’re going to see larger latencies on the first skill invocations. There is expectation that at certain call volume in prod, average call latencies would be reduced.

So I think using Proguard is worth a shot. I can try to drill down on this more. I've asked if they could provide sample Proguard configurations but haven't heard back yet.

barbeau commented 8 years ago

The proguard branch currently builds and can be deployed, but I still have a runtime error (along with a ton of build time warnings, many of which I'm currently suppressing), and the obfuscated jar is still around 18MB (down from 19MB). Current plan is to make it run without errors, and then try to trim down the classes included incrementally to reduce the JAR size further.

java.lang.ClassNotFoundException: Provider org.glassfish.jersey.internal.RuntimeDelegateImpl could not be instantiated:
 java.lang.NullPointerException: java.lang.RuntimeException java.lang.RuntimeException: 
java.lang.ClassNotFoundException: Provider org.glassfish.jersey.internal.RuntimeDelegateImpl could not be instantiated: 
java.lang.NullPointerException at 
javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:152) at 
javax.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:120) at 
javax.ws.rs.core.UriBuilder.newInstance(UriBuilder.java:95) at 
javax.ws.rs.core.UriBuilder.fromPath(UriBuilder.java:148) at 
org.onebusaway.io.client.request.RequestBase$BuilderBase.<init>(RequestBase.java:68) at 
org.onebusaway.io.client.request.RequestBase$BuilderBase.<init>(RequestBase.java:63) at 
org.onebusaway.io.client.request.ObaArrivalInfoRequest$Builder.<init>
(ObaArrivalInfoRequest.java:36) at 
org.onebusaway.alexa.lib.ObaUserClient.getArrivalsAndDeparturesForStop(ObaUserClient.java:124)
at org.onebusaway.alexa.AuthedSpeechlet.tellArrivals(AuthedSpeechlet.java:145) at 
org.onebusaway.alexa.AuthedSpeechlet.onLaunch(AuthedSpeechlet.java:93) at 
org.onebusaway.alexa.MainSpeechlet.onLaunch(MainSpeechlet.java:65) at 
com.amazon.speech.speechlet.SpeechletRequestDispatcher.dispatchSpeechletCall(SpeechletRequestDispatcher.java:72) at 
com.amazon.speech.speechlet.SpeechletRequestHandler.handleSpeechletCall(SpeechletRequestHandler.java:69) at 
com.amazon.speech.speechlet.lambda.SpeechletRequestStreamHandler.handleRequest(SpeechletRequestStreamHandler.java:81)
 Caused by: java.lang.ClassNotFoundException: Provider org.glassfish.jersey.internal.RuntimeDelegateImpl could not be instantiated: java.lang.NullPointerException at 
javax.ws.rs.ext.FactoryFinder.newInstance(FactoryFinder.java:122) at 
javax.ws.rs.ext.FactoryFinder.find(FactoryFinder.java:225) at 
javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:135) ... 13 more 
Caused by: java.lang.NullPointerException at 
org.glassfish.hk2.utilities.ServiceLocatorUtilities.enablePerThreadScope(ServiceLocatorUtilities.java:100) at org.glassfish.jersey.internal.inject.Injections._createLocator(Injections.java:141) at 
org.glassfish.jersey.internal.inject.Injections.createLocator(Injections.java:109) at 
org.glassfish.jersey.internal.RuntimeDelegateImpl.<init>(RuntimeDelegateImpl.java:63) 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:422) at 
java.lang.Class.newInstance(Class.java:442) at 
javax.ws.rs.ext.FactoryFinder.newInstance(FactoryFinder.java:118) ... 15 more
barbeau commented 7 years ago

I'm closing this, as it doesn't seem to be an issue any longer and seems to be a huge rabbit hole. I have a timer running to help refresh and keep the Lambda instance alive, so cold starts aren't as painful.