Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rewiring the Java brain for App Engine

Nacho Coloma
November 02, 2013

Rewiring the Java brain for App Engine

A talk from DevFest Berlin 2013 introducing the difference between our expectations as Java developers and the services provided by App Engine.

Nacho Coloma

November 02, 2013
Tweet

More Decks by Nacho Coloma

Other Decks in Programming

Transcript

  1. About me • CTO at Extrema Sistemas, the SpringSource partner

    for Spain and Italy. • that's a lot of Java gigs. • for which we never managed the hosting.
  2. The problem When a ticketing platform fails, they tend to

    call you on weekends. photo: Carey James Balboa
  3. We don t want to do DevOps ' • We

    want to be able to scale. • We need high availability. • We need to take care of security upgrades. We just don't want to do these things. That's PaaS.
  4. PaaS IaaS != • Infrastructure as a Service: Give me

    a virtual machine and I'll code my way out. • Platform as a Service: Give me a bag of services and I'll code my way out. – A lot of best practices included. – Also, a lot of constraints.
  5. What could go wrong? • The load balancer needs high

    availability. • Sessions should be sticky. • If a server is down, another should assume the session. • Multiple zones! • Keep these servers upgraded, because, security.
  6. What could go wrong? • The load balancer needs to

    be fault tolerant. • Sessions should be sticky • If a server is down, another should assume the session. • Multiple zones! • Upgrade technology WHY U NO WORK
  7. What could go wrong? • I don't manage the Load

    Balancer. • I don't manage the Front-end Servers. • I don't manage Memcache. • I don't manage the Datastore.
  8. What could go wrong? • I don't manage the Load

    Balancer. • I don't manage the Front-end Servers. • I don't manage Memcache. • I don't manage the Datastore. Somebody else s ' problem
  9. Java expectations and App Engine • HTTP Sessions • Application

    deployment • Standards: JDO and JPA • Normalized Relational Databases • JMS and WS-*
  10. Cold starts • Your application is not deployed when uploaded,

    but when the first request is received. – The user that triggered this request will be waiting. • Web requests have 60 seconds before timing out. – Good enough for Python and Go but not so much to start a Java application.
  11. Fixing cold starts with Java • Configure warmup requests and

    at least one idle instance. – It doesn't always work, though :( • Limit the number of libraries and do not search in the classpath. • Prefer lightweight D.I. frameworks like Guice or Dagger to Spring.
  12. JDO and JPA are good • Avoid using idiomatic SQL:

    your queries are independent of your current database provider. • Avoid dependence with your ORM library. • Transparent relationships between domain objects.
  13. JDO and JPA are good • Avoid using idiomatic SQL:

    your queries are independent of your current database provider. • Avoid dependence with your ORM library. • Transparent relationships between domain objects. NOT REALLY
  14. Use something specific to App Engine • Java: Objectify, SimpleDS

    or Slim3 – Disclaimer: I am the developer of SimpleDS, so I'm obviously biased. • Python: NDB – Developed by Guido van Rossum at Google. • Native Datastore – Sometimes you just need to go hardcore.
  15. There is no JOIN @Entity public class Ticket { private

    int numTickets; @ManyToOne private User user; } SELECT num_tickets, name FROM tickets INNER JOIN users ON tickets.user_id = users.id
  16. Iterating with JPA public void renderTickets( PrintWriter writer, List<Ticket> tickets)

    { for (Ticket ticket : tickets) { writer.print(ticket.getNumTickets()); writer.print(ticket.getUser().getName()); } }
  17. public void renderTickets(PrintWriter writer, List<Ticket> tickets) { for (Ticket ticket

    : tickets) { User user = entityManager.get(ticket.getUserKey()); writer.print(ticket.getNumTickets()); writer.print(user.getName()); } }
  18. Some numbers • Writes in App Engine are expensive. •

    Writes require transactions and disk access. • Max. 5 writes per sec for a single entity group. • Use batch operations, sharding and write-behind cache for better performance. • Reads in App Engine are cheap. • No transactions. Data can be read from local cache (fast!). • No limit to the number of concurrent reads.
  19. public void renderTickets(PrintWriter writer, List<Ticket> tickets) { List<Key> userKeys =

    new ArrayList<>(); for (Ticket ticket : tickets) { userKeys.add(ticket.getUserKey()); } Map<Key, User> users = entityManager.get(userKeys); for (Ticket ticket : tickets) { User user = entityManager.get(ticket.getUserKey()); writer.print(ticket.getNumTickets()); writer.print(users.get(ticket.getUserKey()).getName()); } }
  20. Denormalize attributes public class Ticket { private int numTickets; private

    Key userKey; private String userName; } public void renderTickets(PrintWriter writer, List<Ticket> tickets) { for (Ticket ticket : tickets) { writer.print(ticket.getNumTickets()); writer.print(ticket.getUserName()); } }
  21. ” Tim O Reilly ' 2003 I was recently talking

    with Jeff Barr, Amazon's chief web services evangelist. He let drop an interesting tidbit. Amazon has both SOAP and REST interfaces to their web services, and 85% of their usage is of the REST interface.
  22. REST interfaces on App Engine • Use JAX-RS (Jersey) with

    Jackson to get maximum flexibility. • Use Google Cloud Endpoints for fast JSON API prototyping. @Api(name = "helloworld", version = "v1") public class Greetings { @ApiMethod(name="greetings.get", httpMethod="get") public HelloGreeting getGreeting(@Named("id") Integer id) { return entityManager.get(id); } }
  23. Push Queues • Forget about JMS or ESB • Tasks

    are handled as web requests. – Arguments are passed as POST parameters. Queue queue = QueueFactory.getDefaultQueue(); queue.add( withUrl("/worker") .param("key", key) );
  24. Task Queues are great • 10 minutes to execute (instead

    of 60 sec) – Still, any query will timeout after 30 sec. • Think of tasks like spawning threads that outlive the current request. – With automatic retries and exponential backoff. – Threads are allowed, but they would get killed when the current request ends. Still, that's a lot of URLs to handle.
  25. DeferredTask is great You don't have to handle URLs yourself

    class MyDeferred implements DeferredTask { public void run() { // Do something interesting } }; MyDeferred task = new MyDeferred(); Queue queue = QueueFactory.getDefaultQueue(); queue.add(withPayload(task));
  26. DeferredTask uses native serialization • All subclasses should specify a

    serialVersionUID – Still, serialized instances may not be able to deserialize after an upgrade. • Serialized instances are opaque
  27. Queue4gae • Like DeferredTask, but using JSON serialization. • Plus:

    – Ability to @Inject fields. – Mock implementation for testing. – Automatic resume for queries beyond 10 minutes.
  28. CursorTask public class SendMailToUsersTask extends CursorTask { protected Cursor runQuery(Cursor

    startCursor) { CursorIterator<User> it = entityManager.createQuery(User.class) .withStartCursor(startCursor) .asIterator(); while (it.hasNext() && !queryTimeOut()) { User user = it.next(); // ...do stuff... } return it.hasNext()? it.getCursor() : null; } }
  29. Postpone everything • In order to return something to the

    user ASAP, postpone anything that is not critical. – Sending e-mails, cascading changes, creating search index entries... – Automatic retries and exponential backoff FTW! • Remember that all tasks must be idempotent.
  30. Wrap up • HTTP Sessions are stored in Memcache and

    the Datastore • Optimize your application startup • Don't try to apply SQL standards • Batch your Datastore operations • Denormalize where possible • Postpone non-critical tasks • It's a compromise: harder to develop, but easier to manage
  31. Questions! • In the meantime, go check the Official Google

    Cloud Platform Community on Google+ • Also: @nachocoloma Where I mostly tweet about HTML5 and JavaScript http://gplus.to/icoloma Where I post about the Google Cloud Platform and other longer-than-140c stuff