Performance tuning your Grails apps

whoami > Colin Harrington

If a tree falls in a forest ...

does it make a sound?

and no one is around to hear it,

https://en.wikipedia.org/wiki/If_a_tree_falls_in_a_forest

Metaphysics & Quantum Mechanics

  • George Berkeley asked the question
     
  • Albert Einstein & Neils Bohr
    • Bohr: "The moon does not exist if nobody is looking at it."
    • How do you prove it?  Prove me wrong

If your user hits a perf issue ...

does it matter to you?

and nobody reports it,

Measure

&

Monitor

Measure first philosophy

 

  • Ask questions later
  • Data > no data

User

This app is slow.

    Perception burn!

May or may not report the issue (probably not)

Measurement

User X loaded the results page in 1870ms today at 4:30pm

475 records were pulled from the DB

if (user.cares()) {
   if(user.canLocateSupport() && user.hasTime()) {
      reportSlowness()
   } 
​}

Perceived Performance

How your users feel is the reality that you are working with

- https://blog.marvelapp.com/a-designers-guide-to-perceived-performance/

Design & Code for your user

- https://www.fastcompany.com/1825005/how-one-second-could-cost-amazon-16-billion-sales

How One Second Could Cost Amazon $1.6 Billion In Sales

 

Front End

Web
Mobile
API

Javascript

Client Heavy

Webapp Performance basics:

  • Originating HTTP request
  • Web Page (HTML)
  • Assets, js, css, images, etc.
  • Javscript app loading times
  • XHR (ajax) requests
  • SSE, websockets etc.

Bundling & Minification

Asset Pipeline Plugin

  • Asset Bundling
  • Extensible Modules (Supports LESS,Handlebars,Coffeescript, Ember-Handlebars, SASS) via other modules.
  • Cache Digest Names (Creates cache digested names and stores aliases in a manifest.properties)
  • Js Minification
  • Js SourceMap Generation
  • Css Minification / Relative Path assertion
  • File Encoding Support
  • GZIP File Generation
  • Last-Modified Header

http://plugins.grails.org/plugin/grails/asset-pipeline

Separate Client & Server

Gradle Multi-module build

  • Standalone Client
    • javascript project
    • grunt, gulp, npm tooling, dependencies, bundling etc.
  • Server Backend
    • APIs
    • Rich data sources
    • Security measures
    • Testable & flexible

Content Delivery Network

  • Distributed & Built to be 'close' to clients
  • Caching mechanisms
  • Static assets
  • Maybe root html documents?
  • layered / uptime / DDOS protection

Preloading Data?

  • HTTP request
  • HTML Response
  • Browser
    • Parse HTML Document
    • Load referenced resources
    • Paint the page
    • Load Javascript app
    • Load Js dependencies
    • Initialize the app
    • API calls to fetch data. 

Front-end analytics 

  • Analytics platforms
  • Google Analytics
  • Pingdom
  • New Relic
  • many, many others

Error handling

  • Javascript testing!
    • Unit testing style
    • Browser testing
  • Error tolerant UX 
  • Visually Responsive vs blocking

Log client side Javascript errors!

  • Use a lib for your framework?
  • Roll your own?
  • Use a 3rd party
    • Track.js
    • Sentry.io
    • Rollbar
    • Raygun
  • Web analytics platforms

Latency : Time scale

Cost

  • Code Performance
  • Reading from Drive
    • SSD
    • Rotational
  • Network latency
    • Same rack
    • Same building
    • Across the internet

Logging

Logs or it didn't happen

Quantify results

Human memory is 'lossy'

Removal of opinion

What happened surrounding

 

Grails uses logback

https://logback.qos.ch

  •  grails-app/conf/logback.groovy
                    or...
  •  grails-app/conf/logback.xml

 

(can also be configured externally)

 

Heisenberg effect

  • Observing influences performance
     
  • log.info("Something happened") 
  • log.info("Something happened ${expensiveEvaluation()}")

https://en.wikipedia.org/wiki/Observer_effect_(physics)

Async Appenders

  • Appenders blocking by default
  • AsyncAppender delegates
    • Any appender can be wrapped 
  • Durability vs performance
  • Appenders
    • File
    • DB
    • SMTP
    • Syslog
    • ...

Log Aggregation

Log Aggregation

  • Centralized
  • Debugging purposes
  • Analyzing
  • Usage/trends
  • spot & diagnose errors quickly
  • Handle Many types of logs
    • System Logs
    • Application Logs
    • Load balancer logs
    • Network device logs
    • front-end error logs

Centralized

One place to rule them all!

 

Metrics

Visual representation

 

Proxy/Load Balancer timing Logs are a great place to start

 

monitor traffic, analyze outliars

 

1 picture == 1000 words

Add context to log statements

  • Which Server
  • Which thread
  • CorrelationIDs
  • User/client identifier

 

 

 

Mapped Diagnostic Context (MDC)

  • https://logback.qos.ch/manual/mdc.html
     
  • User/client identifier
     
  • Early in the chain: Filter or Interceptor:
  • MDC.put("user", user.name)
  • Easy to search and filter

  • CorrelationID from the entry point

    • (also works if included in reactive flows)
       

Starting Point

  • Access Logs
    • Load balancer
    • Nginx
    • Tomcat access valve

Access logs

  • Request timing (duration, when)
  • HTTP status
  • Size of the response
  • Remote IP address
  • Thread that processed the request 
  • Session ID (tomcat)
  • Custom headers 

Look at min/max/average response times

Historical Performance

Did the performance change over time? 

Growing? at what rate?

Interceptors

  • Can be used as a simple Performance monitoring tool
class MyInterceptor {

  int order = HIGHEST_PRECEDENCE

  boolean before() { true }

  boolean after() { true }

  void afterView() {
    // no-op
  }

}

Interceptors

  • Before the controller action
     
  • after the controller action
     
  • after the view layer is done
class MyInterceptor {

  int order = HIGHEST_PRECEDENCE

  boolean before() { true }

  boolean after() { true }

  void afterView() {
    // no-op
  }

}

Profiling Tools

  • New Relic
  • Datadog
  • Solar Winds
  • YourKit Java Profiler (works great!)
  • JProfiler

YourKit Java Profiler 

JProfiler

Databases

SQL? 

  • Connection Pooling usage and settings
  • SQL Logging
  • Shim (wraps the client driver)
  • Enable Hibernate SQL logging
  • Query Logging on the actual server

 

* warning this can be chatty!

Enable SQL logging

logger("org.hibernate.SQL", DEBUG, ["STDOUT"], false)
logger("org.hibernate.type.descriptor.sql.BasicBinder", TRACE, ["STDOUT"], false)
environments:
  local:
    dataSource:
        logSql: true
        formatSql: true
grails-app/conf/logback.groovy

Application configuration

grails-app/conf/application.yml

Profiling in other data sources

  • Mongo Profiling Level 
  • Redis monitoring or Slow log
  • MySQL Slow Log
  • Postgres logging slow queries

Slow Query

One expensive query

Usually a Complex query

 

 

1 * (Query Time + network overhead)

Many Queries

Many smaller less expensive queries

Overhead becomes important

 

 

n * (Query Time + network overhead)

GORM & Hibernate

ORMs are tough

Hibernate Session

- https://www.slideshare.net/JonasWitt1/understanding-database-transactions-and-hibernate-sessions-in-grails

Transactions

- https://www.slideshare.net/JonasWitt1/understanding-database-transactions-and-hibernate-sessions-in-grails

Hibernate Second Level Cache

  • premature optimization?
  • Optimize frequently used objects

Hibernate Query Cache

hasMany
Considered
Harmful

(@See Burt's talk!)

Author Example

class Author {

    String name

    static hasMany = [books: Book]
}

RxGORM

http://gorm.grails.org/

new Book(title:"The Stand")
        .save()
        .subscribe { Book it ->
    println "Title = ${it.title}"
}
Book.get(id)
    .subscribe { Book it ->
    println "Title = ${it.title}"
}

Breaking

out of

procedural Jail

 

Asnyc

Promises

import static grails.async.Promises.*

def p1 = task { 2 * 2 }
def p2 = task { 4 * 4 }
def p3 = task { 8 * 8 }
assert [4,16,64] == waitAll(p1, p2, p3)
import static java.util.concurrent.TimeUnit.*
import static grails.async.Promises.*

Promise p = task {
        // Long running task
}
p.onError { Throwable err ->
        println "An error occured ${err.message}"
}
p.onComplete { result ->
    println "Promise returned $result"
}
// block until result is called
def result = p.get()
// block for the specified time
def result = p.get(1,MINUTES)
class BookService {
    List<Book> findBooks(String title) {
      // implementation
    }
}
import grails.async.*

class AsyncBookService {
   @DelegateAsync BookService bookService
}

3rd Party API calls

  • Network overhead 100ms?
  • Orders of magnitude slower than code
  • Beware of mixing with Long running Transactions

JVM Performance

Garbage Collection

  • What type of GC?
  • GC logs
  • JVM monitoring tools

Visual VM

View layer

Grails Views

  • http://views.grails.org/
  • Statically Compiled Json Views!

@CompileStatic

Load Testing

  • Jmeter (older but flexible)
  • Gatling
  • etc.

 

Connect with Us

grails.org
groovy-community.grails.org
grailsblog.objectcomputing.com
objectcomputing.com/grails
@grailsframework
@objectcomputing

Thank you for attending!

Please visit our table for giveaways
and to meet the team!

 

ociweb.com/grails
info@ociweb.com

Performance tuning your Grails apps

By Colin Harrington

Performance tuning your Grails apps

Gr8conf US 2017: You built it, they came - success!! What happens months or years later if your app grows and starts to slow down, grow too large or starts to be unreliable? We'll take a look at common pitfalls and their solutions, as well as take a more in-depth approach on how to address performance concerns. Whether Grails 2 or Grails 3, we'll dive into some real-world stories and cover some of the tricks and tools.

  • 3,161