oscon 2010
TRANSCRIPT
Ruby on Mirah on App EngineRun your apps on Google Servers, with access to first-class Java APIs
John WoodellCharles NutterJuly 22, 2010
2
Saturday, July 24, 2010
• No need to install or maintain your own stack• Use Google scalable services via standard APIs• Built-in application management console
Key Features
4
• Pay-as-you-go, with free quota to get started
Saturday, July 24, 2010
• No native code• No threads or sockets• No writing to the filesystem
Key Limitations
5
• No more than 30 seconds per request
Saturday, July 24, 2010
Dev AppServer
• Local implementation of services– LRU memcache– Disk-backed datastore– HttpClient-backed URLFetch
• Emulates the production environment– Sandbox restrictions may be inconsistent,
so run tests on production servers as well
6
Saturday, July 24, 2010
Deployment
• Your app lives at– <app_id>.appspot.com, or– Custom domain with Google Apps
• Deploying uploads– Static files– Resource files– Other metadata (datastore indexes, cron jobs)
• Admin Console– dashboards– manage multiple versions– view logs
7
Saturday, July 24, 2010
Quotas and Billing
Resource Provided Free Additional Cost
CPU time 6.5 hours/day $0.10/hour
Bandwidth In 1GByte/day $0.10/GByte
Bandwidth Out 1GByte/day $0.12/GByte
Stored Data 1 GB $0.005/GB-day
Emails sent 2000/day to users50000/day to admins
$0.0001/email
8
Saturday, July 24, 2010
App Engine Product Roadmap
• SSL for third-party domains• Background servers capable of running for longer than 30s• Ability to reserve instances to reduce application loading overhead• Ability to select different availability vs. latency options for Datastore• Support for mapping operations across datasets• Datastore dump and restore facility• Raise request/response size limits for some APIs• Improved monitoring and alerting of application serving• Support for Browser Push (Comet) communication• Built-in support for OAuth & OpenID
9
Saturday, July 24, 2010
Benefits of JRuby
• Outperforms MRI in many cases... 2x to 10x• Gem extensions written in Java (no more segfaults)• A wealth of integration options and first-class Java APIs
11
• Spin-up new instances quickly using Mirah/Java servlets
Saturday, July 24, 2010
App Engine JRuby Milestones
12
• 2009-04-08 @olabini publishes blog post on YARBL• 2009-04-09 @nicksieger publishes warbler demo• 2009-05-06 RailsConf (sinatra & merb)• 2009-11-02 0.0.5 bundler, precompilation & Duby preview• 2009-11-20 RubyConf (Rails 3.0.pre & Duby App)• 2009-12-27 @urekat publishes Rails 2.3.5 patches• 2010-01-11 @codingforrent published rails/dm gem• 2010-01-21 0.0.8 Rails 2.3.5 Primer for DM & TinyDS• 2010-01-26 0.0.9 Mechanize and Hpricot demos• 2010-02-27 0.0.10 ActionMailer, ImageService, jruby-openssl• 2010-04-08 @azazeal blog post on taxster.gr (OpenID)• 2010-06-09 0.0.14 JRuby 1.5.1 & app.yaml preview
Saturday, July 24, 2010
• Several seconds to “spin-up” a new JRuby instance• Some gems may need their extensions ported to Java• Not officially supported
Current Issues with JRuby on App Engine
13
+ , but you have all that you need
Saturday, July 24, 2010
14
Install it Now
sudo gem install google-appengine
Everything you need installs as gems
Saturday, July 24, 2010
App Engine Gems
15
• Development Gems–appengine-sdk–appengine-tools . . . dev_appserver.rb & appcfg.rb• Runtime Gems
– appengine-rack . . . . jruby-jars & jruby-rack–appengine-apis
• Related Gems–dm-appengine– rails_appengine– rails_dm_datastore– rails_tiny_ds
Saturday, July 24, 2010
App Engine JRuby APIs
• AppEngine::Users• AppEngine::Datastore• AppEngine::Memcache• AppEngine::Mail• AppEngine::URLFetch• AppEngine::Images• AppEngine::Logger• AppEngine::XMPP• AppEngine::Labs::TaskQueue• AppEngine::OAuth
16
• AppEngine::Blobstore
Saturday, July 24, 2010
Me
• Charles Oliver Nutter
• JRuby co-lead
• Ex-Java EE developer
• @headius
• blog.headius.com
Saturday, July 24, 2010
My Day Job
• JRuby and JRuby-related extensions
• JVM-based utilities and libraries (for JRuby?)
• Bytecode generation
• Mobile and embedded interests
• Narrow, “system”-level scope
Saturday, July 24, 2010
My Problem
• I love Ruby
• I write Java all day for JRuby
• How can I write Ruby all day?
Saturday, July 24, 2010
What If This...public class Foo { private int a;
public Foo(int a) { this.a = a; }
public void show() { System.out.println(a); }}
Saturday, July 24, 2010
...Could Be Thisclass Foo def initialize(a) @a = a end
def show puts @a endend
Saturday, July 24, 2010
Mirah
• A nicer way to write Java
• Ruby syntax with modifications
• Feels like Ruby
• Compiles to Java/JVM
• No runtime library
Saturday, July 24, 2010
Features From Ruby
• Optional arguments ✓
• Internal iteration ✓
• Closures ✓
• Literals ✓
• String interpolation ✓
• Mixins, “open” classes (soon)
Saturday, July 24, 2010
Ruby public static __file__(Lruby/__dash_e__;Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;[Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/Block;)Lorg/jruby/runtime/builtin/IRubyObject; @Lorg/jruby/anno/JRubyMethod;(name="__file__", frame=true, required=0, optional=0, rest=-2) L0 LINENUMBER 1 L0 ALOAD 1 ICONST_0 INVOKESTATIC ruby/__dash_e__.setPosition (Lorg/jruby/runtime/ThreadContext;I)V ALOAD 0 INVOKEVIRTUAL ruby/__dash_e__.getCallSite0 ()Lorg/jruby/runtime/CallSite; ALOAD 1 ALOAD 2 ALOAD 2 ALOAD 0 ALOAD 1 GETFIELD org/jruby/runtime/ThreadContext.runtime : Lorg/jruby/Ruby; INVOKEVIRTUAL ruby/__dash_e__.getString0 (Lorg/jruby/Ruby;)Lorg/jruby/RubyString; INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject; ARETURN
Saturday, July 24, 2010
Mirah
public static void main(java.lang.String[]); Code: 0: getstatic #12; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #14; //String Hello, world! 5: invokevirtual #20; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return
Saturday, July 24, 2010
Mirah
// Generated from DashEpublic class DashE extends java.lang.Object { public static void main(java.lang.String[] argv) { java.io.PrintStream temp$1 = java.lang.System.out; temp$1.println("Hello, world!"); }}
Saturday, July 24, 2010
Ruby public static method__0$RUBY$fib(Lruby/__dash_e__;Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/Block;)Lorg/jruby/runtime/builtin/IRubyObject; @Lorg/jruby/anno/JRubyMethod;(name="fib", frame=true, required=1, optional=0, rest=-1) ALOAD 3 ASTORE 9 L0 LINENUMBER 1 L0 ALOAD 1 ICONST_0 INVOKESTATIC ruby/__dash_e__.setPosition (Lorg/jruby/runtime/ThreadContext;I)V ALOAD 0 INVOKEVIRTUAL ruby/__dash_e__.getCallSite0 ()Lorg/jruby/runtime/CallSite; ALOAD 1 ALOAD 2 ALOAD 9 LDC 2 INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;J)Lorg/jruby/runtime/builtin/IRubyObject; INVOKEINTERFACE org/jruby/runtime/builtin/IRubyObject.isTrue ()Z IFEQ L1 ALOAD 9 GOTO L2 L1 FRAME FULL [ruby/__dash_e__ org/jruby/runtime/ThreadContext org/jruby/runtime/builtin/IRubyObject org/jruby/runtime/builtin/IRubyObject org/jruby/runtime/Block T T T T org/jruby/runtime/builtin/IRubyObject] [] ALOAD 0 INVOKEVIRTUAL ruby/__dash_e__.getCallSite1 ()Lorg/jruby/runtime/CallSite; ALOAD 1 ALOAD 2 ALOAD 0 INVOKEVIRTUAL ruby/__dash_e__.getCallSite2 ()Lorg/jruby/runtime/CallSite; ALOAD 1 ALOAD 2 ALOAD 2 ALOAD 0 INVOKEVIRTUAL ruby/__dash_e__.getCallSite3 ()Lorg/jruby/runtime/CallSite; ALOAD 1 ALOAD 2 ALOAD 9 LDC 1 INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;J)Lorg/jruby/runtime/builtin/IRubyObject; INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject; ALOAD 0 INVOKEVIRTUAL ruby/__dash_e__.getCallSite4 ()Lorg/jruby/runtime/CallSite; ALOAD 1 ALOAD 2 ALOAD 2 ALOAD 0 INVOKEVIRTUAL ruby/__dash_e__.getCallSite5 ()Lorg/jruby/runtime/CallSite; ALOAD 1 ALOAD 2 ALOAD 9 LDC 2 INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;J)Lorg/jruby/runtime/builtin/IRubyObject; INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject; INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject; L2 FRAME SAME1 org/jruby/runtime/builtin/IRubyObject ARETURN
Saturday, July 24, 2010
Mirahpublic static int fib(int); Code: 0: iload_0 1: iconst_2 2: if_icmplt 9 5: iconst_0 6: goto 10 9: iconst_1 10: ifeq 17 13: iload_0 14: goto 30 17: iload_0 18: iconst_1 19: isub 20: invokestatic #10; //Method fib:(I)I 23: iload_0 24: iconst_2 25: isub 26: invokestatic #10; //Method fib:(I)I 29: iadd 30: ireturn
Saturday, July 24, 2010
Mirah
// Generated from DashEpublic class DashE extends java.lang.Object { public static void main(java.lang.String[] argv) { } public static int fib(int a) { return (a < 2) ? (a) : ((DashE.fib((a - 1)) + DashE.fib((a - 2)))); }}
Saturday, July 24, 2010
Mirah public static java.io.PrintStream foo(int a, int b) { java.io.PrintStream temp$1 = java.lang.System.out; temp$1.println((a + b)); return temp$1; } public static java.io.PrintStream foo() { return foo(1); } public static java.io.PrintStream foo(int a) { return foo(a, 2); }
Saturday, July 24, 2010
Mirah// Generated from DashEpublic class DashE extends java.lang.Object { public static void main(java.lang.String[] argv) { java.util.List a = java.util.Collections.unmodifiableList( java.util.Arrays.asList(1, 2, 3, 4, 5)); java.util.Iterator __xform_tmp_1 = a.iterator(); label1: while (__xform_tmp_1.hasNext()) { java.lang.Object x = __xform_tmp_1.next(); label2: { java.io.PrintStream temp$3 = java.lang.System.out; temp$3.println(x); } } }}
Saturday, July 24, 2010
Mirah// Generated from DashEpublic class DashE extends java.lang.Object { public static void main(java.lang.String[] argv) { DashE.__xform_tmp_1 $binding = new DashE.__xform_tmp_1(); $binding.x = "in thread"; java.lang.Thread t = new java.lang.Thread(new DashE.__xform_tmp_2($binding)); } public static class __xform_tmp_1 extends java.lang.Object { java.lang.String x; } public static class __xform_tmp_2 extends java.lang.Object implements java.lang.Runnable { private DashE.__xform_tmp_1 binding; public __xform_tmp_2(DashE.__xform_tmp_1 binding) { this.binding = binding; } public void run() { DashE.__xform_tmp_1 $binding = this.binding; java.io.PrintStream temp$1 = java.lang.System.out; temp$1.println($binding.x); } }}
Saturday, July 24, 2010
Mirah// Generated from DashEpublic class DashE extends java.lang.Object { public static void main(java.lang.String[] argv) { DashE.__xform_tmp_1 $binding = new DashE.__xform_tmp_1(); $binding.x = "in thread"; java.lang.Thread t = new java.lang.Thread(new DashE.__xform_tmp_2($binding)); } public static class __xform_tmp_1 extends java.lang.Object { java.lang.String x; } public static class __xform_tmp_2 extends java.lang.Object implements java.lang.Runnable { private DashE.__xform_tmp_1 binding; public __xform_tmp_2(DashE.__xform_tmp_1 binding) { this.binding = binding; } public void run() { DashE.__xform_tmp_1 $binding = this.binding; java.io.PrintStream temp$1 = java.lang.System.out; temp$1.println($binding.x); } }}
Saturday, July 24, 2010
Mirah// Generated from DashEpublic class DashE extends java.lang.Object { public static void main(java.lang.String[] argv) { DashE.__xform_tmp_1 $binding = new DashE.__xform_tmp_1(); $binding.x = "in thread"; java.lang.Thread t = new java.lang.Thread(new DashE.__xform_tmp_2($binding)); } public static class __xform_tmp_1 extends java.lang.Object { java.lang.String x; } public static class __xform_tmp_2 extends java.lang.Object implements java.lang.Runnable { private DashE.__xform_tmp_1 binding; public __xform_tmp_2(DashE.__xform_tmp_1 binding) { this.binding = binding; } public void run() { DashE.__xform_tmp_1 $binding = this.binding; java.io.PrintStream temp$1 = java.lang.System.out; temp$1.println($binding.x); } }}
Saturday, July 24, 2010
It’s Not Ruby
• Using Java’s libraries and type system
• No “eval”
• No runtime-mutable classes
• Ruby libraries will not work
Saturday, July 24, 2010
But It Feels Like Ruby!
• Clean, lightweight syntax
• Iteration, closures
• Far less “ceremony” than Java
• Performance equivalent to Java
Saturday, July 24, 2010
53
Benefits of Mirah on App Engine
• New instances always spin-up in about a second• Dubious framework uses familiar Rails conventions• Code in Java or Ruby when Mirah lacks features you require• The generated Java source can be inspected at any time
Saturday, July 24, 2010
Resources
• Blog– http://jruby-appengine.blogspot.com
• Code Site– http://code.google.com/p/appengine-jruby
• Examples Apps– http://rails-primer.appspot.com– http://dubious-demo.appspot.com
• Mirah Projects– http://github.com/headius/mirah– http://github.com/mirah/dubious
61
Saturday, July 24, 2010