Do you want to improve the performance of your Grails Applications? In this presentation you will find the mains topics you have to change into your grails application to be more efficient and faster. You will find some tricks about domain classes, async logging, parallelism, regex, querying and tools.
5. S O F T WA R E D E V E L O P M E N T
Turnkey
development
Atlassian
Solution
Partner
Consulting and
support in
software
architecture and
analysis
Application
integration
Evolutionary support
of our own
developments or
inherited ones
@alberto_deavila
@albertoBaron1
7. G O A L S
✴ Different ways to improve performance
✴ Good practices
✴ Common mistakes
✴ Tricks and tips
✴ Tools to measure performance
@alberto_deavila
@albertoBaron1
8. ✴ Improvements outside Grails App
✴ Improvements inside Grails App
✴ Config
✴ Code
✴ Tools
I N D E X
@alberto_deavila
@albertoBaron1
9. I M P R O V E M E N T S O U T S I D E
Improvements outside
10. ✴ Hardware
✴ Software
✴ Architectural
✴ JVM
✴ Database
I M P R O V E M E N T S I N S I D E
@alberto_deavila
@albertoBaron1
11. H A R D WA R E I M P R O V E M E N T S
Hardware improvements
@alberto_deavila
@albertoBaron1
12. H A R D WA R E I M P R O V E M E N T S
✴ Better hard disc: SSD
✴ Increase RAM memory
✴ Better microprocessor
✴ More velocity
✴ More cores
✴ Improve network
@alberto_deavila
@albertoBaron1
13. H A R D WA R E I M P R O V E M E N T S
@alberto_deavila
@albertoBaron1
14. A R C H I T E C T U R A L I M P R O V E M E N T S
Arquitectural improvements
@alberto_deavila
@albertoBaron1
15. A R C H I T E C T U R A L I M P R O V E M E N T S
✴ Multiple web server instances
✴ Load balancers
✴ Proxies
✴ Message quering
✴ Multiple databases
✴ Microservices
@alberto_deavila
@albertoBaron1
16. A R Q U I T E C T U R A L I M P R O V E M E N T S
@alberto_deavila
@albertoBaron1
17. J V M I M P R O V E M E N T S
JVM improvements
@alberto_deavila
@albertoBaron1
18. PA R A M E T E R S
✴VM options:
✴ MaxMetaspaceSize
@alberto_deavila
@albertoBaron1
19. J AVA 8 PA R A M E T E R S
@alberto_deavila
@albertoBaron1
20. D ATA B A S E I M P R O V E M E N T S
Database improvements
@alberto_deavila
@albertoBaron1
21. D ATA B A S E I M P R O V E M E N T S
✴ Monitoring your database performance
✴ Fix slow queries
✴ Use views to execute complex queries
✴ Create indexes in the mains tables
✴ Create a good data model
✴ Do you (only) need relational databases?
@alberto_deavila
@albertoBaron1
22. D ATA B A S E I M P R O V E M E N T S
@alberto_deavila
@albertoBaron1
23. I M P R O V E M E N T S I N S I D E
Improvements inside
24. I M P R O V E M E N T S I N S I D E
Configuration improvements
@alberto_deavila
@albertoBaron1
25. C O N F I G U R AT I O N I M P R O V E M E N T S
Bootstrap
@alberto_deavila
@albertoBaron1
26. B O O T S T R A P
✴ Include the minimum code
✴ Configure it by environments
✴ Don’t insert data in Bootstrap: use migrations
✴ Use Promises
@alberto_deavila
@albertoBaron1
27. B O O T S T R A P
✴ Configure it by environments
@alberto_deavila
@albertoBaron1
import grails.util.Environment
Environment.executeForCurrentEnvironment{
production{
// do something in prod
}
development{
// do something in dev
}
staging{
// do something in custom env
}
}
28. B O O T S T R A P
✴ Don´t insert data. Use migrations plugin
✴ Manage database changes
✴ Avoid inconsistences
✴ Changelog.groovy file to group changes
@alberto_deavila
@albertoBaron1
databaseChangeLog = {
include file: ‘20180105_BUG-1.groovy’
include file: '20180212_FEATURE-6.groovy'
}
29. B O O T S T R A P
✴ Promises
@alberto_deavila
@albertoBaron1
def testingPerformance() {
redirect action: 'assistAnotherPresentation', controller: 'greach',
params: [
speak: 'GRAILS 3 TESTING OPTIMIZATION',
speaker: 'Evelina & Tomás',
day: '17th March',
time: '8:30 AM'
]
}
30. C O N F I G U R AT I O N I M P R O V E M E N T S
Hibernate cache
@alberto_deavila
@albertoBaron1
31. H I B E R N AT E C A C H E
✴ Second level cache:
✴ Read
✴ Read-Write
✴ Nonstrict-Read-Write
✴ Transactional
@alberto_deavila
@albertoBaron1
32. H I B E R N AT E C A C H E
✴ Add dependencies to your project
✴ Config:
@alberto_deavila
@albertoBaron1
hibernate:
cache:
use_second_level_cache: true
provider_class: net.sf.ehcache.hibernate.EhCacheProvider
region:
factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory
33. H I B E R N AT E C A C H E
✴ Cache use:
@alberto_deavila
@albertoBaron1
class Car {
static mapping = {
cache usage: 'read-only'
// 'read-write' || ‘transactional' || ‘nonstrict-read-write’
}
}
class Car {
static hasMany = [wheels: Wheel]
static mapping = {
wheels cache: true
}
}
34. H I B E R N AT E C A C H E
✴ Cache use:
@alberto_deavila
@albertoBaron1
Car.findByBrand("Tesla", [cache: true])
Car.withCriteria {
like('model', 'Model%')
cache true
}
35. H I B E R N AT E C A C H E
@alberto_deavila
@albertoBaron1
But be careful with Caches
36. I M P R O V E M E N T S I N S I D E
Code improvements
@alberto_deavila
@albertoBaron1
37. C O D E I M P R O V E M E N T S
@CompileStatic
@alberto_deavila
@albertoBaron1
38. C O D E I M P R O V E M E N T S
Querying
@alberto_deavila
@albertoBaron1
39. Q U E RY I N G
✴ GORM querying options
@alberto_deavila
@albertoBaron1 http://tatiyants.com/how-and-when-to-use-various-gorm-querying-options/
40. Q U E RY I N G
✴ Don’t abuse of dynamic finders
✴ Don’t filter / sort a query result, use queries
✴ You can use left join and projections with
criterias
✴ Flush the session in little batches
✴ Give hibernate´ s Stateless session a try
@alberto_deavila
@albertoBaron1
41. C O D E I M P R O V E M E N T S
Domain classes
@alberto_deavila
@albertoBaron1
42. D O M A I N C L A S S E S
Autowiring
@alberto_deavila
@albertoBaron1
43. A U T O R I W I N G
✴ Autowiring services are injected:
✴ Every GORM query result
✴ New domain classes instances
✴ Grails > 3.3.0 autowiring disabled by default
@alberto_deavila
@albertoBaron1
44. A U T O R I W I N G
✴ Alternatives:
✴ Anemic domain model + logic in services
✴ Taglibs to get logic in view layer
✴ Lazy load services
✴ GORM Event Listeners
@alberto_deavila
@albertoBaron1
lazy load services?
45. A U T O R I W I N G
@alberto_deavila
@albertoBaron1
46. D O M A I N C L A S S E S
HasMany
@alberto_deavila
@albertoBaron1
47. H A S M A N Y
✴ HasMany instances are lazy-loaded by default
✴ All instances are loaded when add new one
✴ Bad performance with entities too big
✴ How to stop using it in:
✴ One to Many
✴ Many to Many
@alberto_deavila
@albertoBaron1 From Burt Beckwith talk in 2009: https://es.slideshare.net/burtbeckwith/advanced-gorm-performance-customization-and-monitoring
48. H A S M A N Y
One to many
@alberto_deavila
@albertoBaron1
49. O N E T O M A N Y
✴ Delete the hasMany
✴ Relate the “many” side with the owner:
@alberto_deavila
@albertoBaron1 From Burt Beckwith talk in 2009: https://es.slideshare.net/burtbeckwith/advanced-gorm-performance-customization-and-monitoring
class Person{
String name
}
class Car{
String register
Person owner
}
class Person{
String name
static hasMany = [cars: Car]
}
class Car{
String register
}
50. H A S M A N Y
Many to many
@alberto_deavila
@albertoBaron1
51. M A N Y T O M A N Y
✴ Like Spring Security with User - Role relation
✴ Let instances orphans
✴ Map the join relation
✴ Create helpers to use the new relation
✴ Create / remove instances
✴ Finders
@alberto_deavila
@albertoBaron1 From Burt Beckwith talk in 2009: https://es.slideshare.net/burtbeckwith/advanced-gorm-performance-customization-and-monitoring
52. M A N Y T O M A N Y
@alberto_deavila
@albertoBaron1 From Burt Beckwith talk in 2009: https://es.slideshare.net/burtbeckwith/advanced-gorm-performance-customization-and-monitoring
class Person{
String name
Set<Car> getCars(){
PersonCar.findAllByPerson(this)
.collect { it.car } as Set
}
}
class PersonCar{
Car car
Person person
static PersonCar create(Person person, Car car){
new PersonCar(person: person, car: car).save()
}
static boolean remove (Person person, Car car){
PersonCar personCar = PersonCar.findByPersonAndCar(person, car)
personCar ? personCar?.delete() : false
}
}
class Car{
String register
}
53. O N E T O M A N Y
✴ You will lose automatic cascade:
✴ You have to delete it manually
✴ Evict cascade problems
✴ Can’t use addTo and removeFrom methods
✴ Implement getter to findAllCarsByUser
@alberto_deavila
@albertoBaron1 From Burt Beckwith talk in 2009: https://es.slideshare.net/burtbeckwith/advanced-gorm-performance-customization-and-monitoring
54. M A N Y T O M A N Y
✴ Not all models are applicable to this solution
✴ Study your model
✴ Take performance measures
✴ Then decide if use this approach
@alberto_deavila
@albertoBaron1 From Burt Beckwith talk in 2009: https://es.slideshare.net/burtbeckwith/advanced-gorm-performance-customization-and-monitoring
56. C O D E I M P R O V E M E N T S
Logs
@alberto_deavila
@albertoBaron1
57. A S Y N C H R O N O U S A P P E N D E R S
Asynchronous Appenders
@alberto_deavila
@albertoBaron1
58. A S Y N C H R O N O U S A P P E N D E R S
✴ Appender:
✴ Delivering LogEvents to their destination
✴ AsyncAppender
✴ Lets users log events asynchronously
✴ How work is passed between threads?
✴Use ArrayBlockingQueue implementation
@alberto_deavila
@albertoBaron1
59. A S Y N C H R O N O U S L O G G E R S
Asynchronous Loggers
@alberto_deavila
@albertoBaron1
60. A S Y N C H R O N O U S L O G G E R S
✴ Concepts related with performance:
✴ Latency: required time to perform an action
✴ Throughput: nº of executed actions / time
@alberto_deavila
@albertoBaron1
To increase logging performance, we want
lower latency and higher throughput
61. A S Y N C H R O N O U S L O G G E R S
✴ How work is passed between threads?
✴ LMAX Disruptor technology
@alberto_deavila
@albertoBaron1
“A lock-free inter-thread communication library,
instead of queues, resulting in higher throughput
and lower latency.”
https://logging.apache.org/log4j/2.x/manual/async.html#UnderTheHood
62. A S Y N C H R O N O U S L O G G E R S
@alberto_deavila
@albertoBaron1 https://logging.apache.org/log4j/2.x/manual/async.html
63. A S Y N C H R O N O U S L O G G E R S
@alberto_deavila
@albertoBaron1
64. C O D E I M P R O V E M E N T S
Data processing
@alberto_deavila
@albertoBaron1
65. D ATA P R O C E S S I N G
✴ Grails promises
✴ Use the cache plugin
✴ Avoid repetitive querying
✴ Async processing
✴ GPars
@alberto_deavila
@albertoBaron1
66. D ATA P R O C E S S I N G
Cache plugin
@alberto_deavila
@albertoBaron1
67. C A C H E P L U G I N
✴ Plugin for Grails 2 & 3 (under development)
✴ Cache methods responses and tags
✴ Easy to use and configure
✴ Possibility to create any differents caches
✴ Cached items have no timeout
@alberto_deavila
@albertoBaron1
68. ✴ To store values in the cache:
✴ To get the cached values:
C A C H E P L U G I N
@alberto_deavila
@albertoBaron1
@Cacheable(value = 'message', key = { title.toUpperCase() } )
Message getMessage(String title) {
log.debug 'Fetching message'
Message.findByTitle(title)
}
}
@CachePut(value='message', key = { message.title.toUpperCase() })
void save(Message message) {
log.debug "Saving message $message"
message.save()
}
69. ✴ To delete values from cache:
✴ Trick: you can combine annotations
C A C H E P L U G I N
@alberto_deavila
@albertoBaron1
@CacheEvict(value = 'message', key = { message.title })
void delete(Message message) {
log.debug "Deleting message $message"
message.delete()
}
70. C A C H E P L U G I N
@alberto_deavila
@albertoBaron1
71. D ATA P R O C E S S I N G
Avoid repetitive querying
@alberto_deavila
@albertoBaron1
72. AV O I D R E P E T I T I V E Q U E RY I N G
✴ We have the following code:
@alberto_deavila
@albertoBaron1
def myMethod(){
List<String> names = ['Homer', 'Marge', 'Bart', 'Lisa', 'Maggie']
//up to 100 Simpsons names
names.each{ String name ->
Character character = Character.findByName(name)
// some actions
character.save()
}
}
73. AV O I D R E P E T I T I V E Q U E RY I N G
✴ We can refactor it to:
@alberto_deavila
@albertoBaron1
def myRefactoredMethod(){
List<String> names = ['Homer', 'Marge', 'Bart', 'Lisa', 'Maggie']
//up to 100 Simpsons names
List<Character> characters = Character.findByNameInList(names)
characters.each{ Character character ->
// some actions
}
characters*.save()
}
74. AV O I D R E P E T I T I V E Q U E RY I N G
✴ We have the following code:
@alberto_deavila
@albertoBaron1
def myServiceMethod(){
List<Long> ids = [1, 2, 3, 4, 5] //up to 100 ids
ids.each{ Long id ->
Character character = Character.get(id)
// some actions
character.save()
}
}
75. AV O I D R E P E T I T I V E Q U E RY I N G
✴ We can refactor it to:
@alberto_deavila
@albertoBaron1
def myRefactoredAction2(){
List<Long> ids = [1, 2, 3, 4, 5] //up to 100 ids
List<Character> characters = Character.getAll(ids)
characters.each{ Character character ->
// some actions
}
characters*.save()
}
76. AV O I D R E P E T I T I V E Q U E RY I N G
@alberto_deavila
@albertoBaron1
77. D ATA P R O C E S S I N G
Async data processing
@alberto_deavila
@albertoBaron1
78. A S Y N C D ATA P R O C E S S I N G
✴ 100 factorial with threads & shared memory:
@alberto_deavila
@albertoBaron1
Long result = 1g
List threads = []
(1..100).each { number ->
threads << Thread.start {
synchronized(this) {
result = result * number
}
}
}
threads*.join()
log.debug result
79. A S Y N C D ATA P R O C E S S I N G
✴ Process a request asynchronously
@alberto_deavila
@albertoBaron1
def myAction(){
Thread.start {
// do slow work in background
// Send an email or some kind of notification
}
render "We are working on it!"
}
80. A S Y N C D ATA P R O C E S S I N G
@alberto_deavila
@albertoBaron1
81. D ATA P R O C E S S I N G
GPars
@alberto_deavila
@albertoBaron1
82. G PA R S
✴ GPars is a framework to do concurrent tasks
✴ Works with Java and Groovy
✴ Make concurrent programming easy
✴ Provide us multiple solutions for parallelism
@alberto_deavila
@albertoBaron1
83. G PA R S
✴ An example with parallel collections:
@alberto_deavila
@albertoBaron1
//summarize numbers concurrently
GParsPool.withPool {
final AtomicInteger amount = new AtomicInteger(0)
[1, 2, 3, 4, 5].eachParallel {amount.addAndGet(it)}
assert 15 == result
}
84. G PA R S
✴ An example with async call:
@alberto_deavila
@albertoBaron1
GParsPool.withPool() {
assert 6 == {it * 2}.callAsync(3).get()
}
85. G PA R S
✴ But there are more GPars features:
✴ Actors
✴ Agents
✴ Dataflow
✴ Fork-join
@alberto_deavila
@albertoBaron1
87. C O D E I M P R O V E M E N T S
Views
@alberto_deavila
@albertoBaron1
88. V I E W S
✴ Compress resources:
✴ Group resources in Manifest
@alberto_deavila
@albertoBaron1
assets {
minifyJs=true
minifyCss=true
excludes=[“**/*.html”]
}
/*
* =require file1.css
* =require file2.css
*/
<asset:javascript src=“manifest.js” />
assets/javascript/manifest.js
89. V I E W S
✴ Avoid ask for resources external
✴ Download JS or CSS files in local
@alberto_deavila
@albertoBaron1
90. V I E W S
Content Delivery Network
@alberto_deavila
@albertoBaron1
91. V I E W S
✴ Use CDN for static files
✴Network of servers spread over the world
✴Shows statically the content of websites
✴Static content cached and stored on servers
✴Delivery determined by location of visitors
✴Static files include html, css, JS or images
@alberto_deavila
@albertoBaron1
93. V I E W S
✴ Set up CDN in a Grails App
✴Install two plugins in BuildConfig
✴SetUp a CloudFront
✴Put CDN details in Config
✴Push local assets to cloud: assets-cdn-push
@alberto_deavila
@albertoBaron1
compile “:cdn-asset-pipeline:0.4.1”
compile “:karman:0.6.1”
94. C O D E I M P R O V E M E N T S
Regex
@alberto_deavila
@albertoBaron1
96. R E G E X
✴ Main Tools
✴ JRegExAnalyser
✴ RegexBuddy
✴ Debuggex
✴ Rubular
✴ RegexPal
@alberto_deavila
@albertoBaron1
97. R E G E X
✴ RegExp against bad backtracking:
http://www.regular-expressions.info/catastrophic.html
@alberto_deavila
@albertoBaron1
“When nesting repetition operators, make
sure that there is only one way to match the
same match”
98. C O D E I M P R O V E M E N T S
Testing
@alberto_deavila
@albertoBaron1
99. T E S T I N G
def testingPerformance() {
redirect action: 'assistAnotherPresentation', controller: 'greach',
params: [
speak: 'GRAILS 3 TESTING OPTIMIZATION',
speaker: 'Evelina & Tomás',
day: '17th March',
time: '8:30 AM'
]
}
@alberto_deavila
@albertoBaron1
101. T O O L S
✴ Many tools to measure performance
✴ JavaMelody
✴ App dynamics
✴ New relic
✴ Dynatrace
@alberto_deavila
@albertoBaron1
102. J AVA M E L O D Y
@alberto_deavila
@albertoBaron1
def javaMelodyTool() {
redirect action: 'assistAnotherPresentation', controller: 'greach',
params: [
speak: 'JavaMelody to measure Grails app performance',
speaker: 'Miguel Angel García',
day: '17th March',
time: '11:45 AM'
]
}
104. T O O L S
✴ Real time application
✴ Application performance monitoring
✴ Available to multiple technologies
✴ Database monitoring
✴ Server topology auto discovered
@alberto_deavila
@albertoBaron1
112. C O N C L U S I O N S
✴ Performance doesn't only depend on Grails
✴ Improve Grails performance in many ways
✴ Performance is a culture
✴ Performance has costs, but less than fix it later
✴ Easy to improve performance little by little
✴ Use tools to help you measure indicators
@alberto_deavila
@albertoBaron1