Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Developing applications for performance

1,074 views

Published on

Scalability != performance. In fact, having to scale your architecture significantly with growth may be a symptom of a poor application performance. Yet, with the rise of cloud and the abundance of automation and container tools that simplify scalability aspect of your system, performance considerations are often pushed to the back row. Building systems for high performance is not easy. It requires a lot of considerations - from technology selection to design decisions. And "the cloud" does not magically solve those problems for you. In this talk I'll discuss common performance pitfalls across the stack and talk about useful techniques and examples that every application could benefit from.

Published in: Software

Developing applications for performance

  1. 1. @papa_fire Developing Applications for Performance by Leon Fayer
  2. 2. @papa_fire {me} ‣ developer ++ ‣ i drink and i fix code ‣ vp @ OmniTI ‣ make slow go fast
  3. 3. @papa_fire disclaimers
  4. 4. @papa_fire disclaimers 1. performance is technology agnostic
  5. 5. @papa_fire disclaimers 1. performance is technology agnostic 2. performance != scalability
  6. 6. @papa_fire disclaimers 1. performance is technology agnostic 2. performance != scalability 3. there is more to performance
  7. 7. @papa_fire disclaimers 1. performance is technology agnostic 2. performance != scalability 3. there is more to performance 4. it’s (almost) all about web
  8. 8. @papa_fire why performance?
  9. 9. @papa_fire it improves user experience
  10. 10. @papa_fire (and as a result) it effects business
  11. 11. @papa_fire “ Just a 100-millisecond delay in load time hurt conversion rate by 7% https://www.soasta.com/wp-content/uploads/2017/04/State-of-Online-Retail-Performance-Spring-2017.pdf
  12. 12. @papa_fire it saves money (in production)
  13. 13. @papa_fire 50 users/sec served by 2 web servers costing $300/month x x $600/month
  14. 14. @papa_fire 1000 users/sec served by 40 web servers costing $300/month x x $12,000/month
  15. 15. @papa_fire this is why cloud doesn’t solve all your problems
  16. 16. @papa_fire only slide on scalability ‣ don’t scale instead of improving performance ‣ keep scaling in mind during design
  17. 17. @papa_fire what can you optimize?
  18. 18. @papa_fire everything!
  19. 19. @papa_fire <front end>
  20. 20. @papa_fire High Performance Web Sites by Steve Souders
  21. 21. @papa_fire SPOF (single point of failure)
  22. 22. @papa_fire (in the world of web) service integrations
  23. 23. @papa_fire ngm.com
  24. 24. @papa_fire codercruise.com http://requestmap.webperf.tools
  25. 25. @papa_fire what happens when they fail?
  26. 26. @papa_fire http://www.webpagetest.org/
  27. 27. @papa_fire 3rd party = no control
  28. 28. @papa_fire one failure is enough
  29. 29. @papa_fire - site implement Facebook Connect - site implement Facebook Connect incorrectly - site load times increase 4x - Facebook has issues - Facebook keeps connections open - site load times increase 500x (until timeout) - Facebook crashes - site crashes - game over
  30. 30. @papa_fire services can be your own PSA
  31. 31. @papa_fire tips
  32. 32. @papa_fire tips only connect to the services you need
  33. 33. @papa_fire tips only connect to the services you need avoid external connection on critical path
  34. 34. @papa_fire tips only connect to the services you need avoid external connection on critical path make external calls asynchronously
  35. 35. @papa_fire tips only connect to the services you need avoid external connection on critical path make external calls asynchronously trap errors and timeouts
  36. 36. @papa_fire tips only connect to the services you need avoid external connection on critical path make external calls asynchronously trap errors and timeouts have a fallback plan
  37. 37. @papa_fire </front end>
  38. 38. @papa_fire <database>
  39. 39. @papa_fire http://use-the-index-luke.com Markus Winand
  40. 40. @papa_fire n+1
  41. 41. @papa_fire $blog = new Blog(‘leon’); $posts = $blog->get_all_posts(); foreach ($posts as $post) { $post->comments = $post->get_comments(); }
  42. 42. @papa_fire $dbh = new DB(…); $sth = $dbh->prepare("select * from posts where author = ?”); $sth->execute(‘leon’); $posts = array(); foreach ($sth->fetchAll() as $p) { $post = new Post(); $post->id = $p->[‘id’]; $post->title = $p->[‘title’]; $post->author = $p->[‘author’]; $post->body = $p->[‘body’]; array_push($posts, $post); } foreach ($posts as $post) { $dbh2 = new DB(…); $sth_comm = $dbh2->prepare("select * from post_comments where post_id = ?"); $sth_comm->execute($post->post_id); foreach(sth_comm->fetchAll() as $c) { $comment = new Comment(); $comment->id = $c->[‘id’]; $comment->author = $c->[‘author’]; $comment->body = $c->[‘body’]; array_push($post->comments, $comment); } }
  43. 43. @papa_fire $dbh = new DB(…); $sth = $dbh->prepare("select * from blogs where username = ?”); $sth->execute(‘leon’); $posts = array(); foreach ($sth->fetchAll() as $p) { $post = new Post(); $post->id = $p->[‘id’]; $post->title = $p->[‘title’]; $post->author = $p->[‘author’]; $post->body = $p->[‘body’]; array_push($posts, $post); } foreach ($posts as $post) { $dbh2 = new DB(…); $sth_comm = $dbh2->prepare("select * from post_comments where post_id = ?"); $sth_comm->execute($post->post_id); foreach(sth_comm->fetchAll() as $c) { $comment = new Comment(); $comment->id = $c->[‘id’]; $comment->author = $c->[‘author’]; $comment->body = $c->[‘body’]; array_push($post->comments, $comment); } } $sth = $dbh->prepare("select * from posts where author = ?”);
  44. 44. @papa_fire $dbh = new DB(…); $sth = $dbh->prepare("select * from posts where author = ?”); $sth->execute(‘leon’); $posts = array(); foreach ($sth->fetchAll() as $p) { $post = new Post(); $post->id = $p->[‘id’]; $post->title = $p->[‘title’]; $post->author = $p->[‘author’]; $post->body = $p->[‘body’]; array_push($posts, $post); } foreach ($posts as $post) { $dbh2 = new DB(…); $sth_comm = $dbh2->prepare("select * from post_comments where post_id = ?"); $sth_comm->execute($post->post_id); foreach(sth_comm->fetchAll() as $c) { $comment = new Comment(); $comment->id = $c->[‘id’]; $comment->author = $c->[‘author’]; $comment->body = $c->[‘body’]; array_push($post->comments, $comment); } } $sth_comm = $dbh2->prepare("select * from post_comments where post_id = ?");
  45. 45. @papa_fire $dbh = new DB(…); $sth = $dbh->prepare("select * from posts where author = ?”); $sth->execute(‘leon’); $posts = array(); foreach ($sth->fetchAll() as $p) { $post = new Post(); $post->id = $p->[‘id’]; $post->title = $p->[‘title’]; $post->author = $p->[‘author’]; $post->body = $p->[‘body’]; array_push($posts, $post); } foreach ($posts as $post) { $dbh2 = new DB(…); $sth_comm = $dbh2->prepare("select * from post_comments where post_id = ?"); $sth_comm->execute($post->post_id); foreach(sth_comm->fetchAll() as $c) { $comment = new Comment(); $comment->id = $c->[‘id’]; $comment->author = $c->[‘author’]; $comment->body = $c->[‘body’]; array_push($post->comments, $comment); } } $sth_comm = $dbh2->prepare("select * from post_comments where post_id = ?"); foreach ($posts as $post) {
  46. 46. @papa_fire 1 post = 2 queries 10 posts = 11 queries 100 posts = 101 queries …
  47. 47. @papa_fire but that’s not all
  48. 48. @papa_fire $dbh = new DB(…); $sth = $dbh->prepare("select * from posts where author = ?”); $sth->execute(‘leon’); $posts = array(); foreach ($sth->fetchAll() as $p) { $post = new Post(); $post->id = $p->[‘id’]; $post->title = $p->[‘title’]; $post->author = $p->[‘author’]; $post->body = $p->[‘body’]; array_push($posts, $post); } foreach ($posts as $post) { $dbh2 = new DB(…); $sth_comm = $dbh2->prepare("select * from post_comments where post_id = ?"); $sth_comm->execute($post->post_id); foreach(sth_comm->fetchAll() as $c) { $comment = new Comment(); $comment->id = $c->[‘id’]; $comment->author = $c->[‘author’]; $comment->body = $c->[‘body’]; array_push($post->comments, $comment); } } $dbh = new DB(…); $dbh2 = new DB(…);
  49. 49. @papa_fire
  50. 50. @papa_fire
  51. 51. @papa_fire the right way
  52. 52. @papa_fire $dbh = new DB(…); $sth = $dbh->prepare("select [list columns you need] from posts p, post_comments c where p.id = c.post_id and p.username = ? order by p.id DESC”); $sth->execute(‘leon’); $posts = array(); $post = new Post(); $active_pid = 0; foreach ($sth->fetchAll() as $p) { if ($post->id != $active_pid) { // if this row is for the post we’re working on if ($post->id) { array_push($posts, $post); } $active_pid = $post->id; // getting comments for this post $post = new Post(); $post->id = $p->[‘id’]; $post->title = $p->[‘title’]; $post->author = $p->[‘author’]; $post->body = $p->[‘body’]; } $comment = new Comment(); $comment->id = $c->[‘id’]; $comment->author = $c->[‘author’]; $comment->body = $c->[‘body’]; array_push($post->comments, $comment); }
  53. 53. @papa_fire $dbh = new DB(…); $sth = $dbh->prepare("select [list columns you need] from posts p, post_comments c where p.id = c.post_id and p.username = ? order by p.id DESC”); $sth->execute(‘leon’); $posts = array(); $post = new Post(); $active_pid = 0; foreach ($sth->fetchAll() as $p) { if ($post->id != $active_pid) { // if this row is for the post we’re working on if ($post->id) { array_push($posts, $post); } $active_pid = $post->id; // getting comments for this post $post = new Post(); $post->id = $p->[‘id’]; $post->title = $p->[‘title’]; $post->author = $p->[‘author’]; $post->body = $p->[‘body’]; } $comment = new Comment(); $comment->id = $c->[‘id’]; $comment->author = $c->[‘author’]; $comment->body = $c->[‘body’]; array_push($post->comments, $comment); } $dbh = new DB(…); $sth = $dbh->prepare("select [list columns you need] from posts p, post_comments c where p.id = c.post_id and p.username = ? order by p.id DESC”);
  54. 54. @papa_fire 1 post = 1 query 10 posts = 1 query 100 posts = 1 query …
  55. 55. @papa_fire Common Table Expressions (CTE) to further reduce a number of queries http://omniti.com/seeds/writable-ctes-improve-performance PSA
  56. 56. @papa_fire $blog = new Blog(‘leon’); $posts = $blog->get_all_posts_with_comments();
  57. 57. @papa_fire $posts = $blog->get_all_posts_with_comments(); $dbh = new DB(…); $sth = $dbh->prepare("select [list columns you need] from posts p, post_comments c where p.id = c.post_id and p.username = ? order by p.id DESC”); $sth->execute(‘leon’); $posts = array(); $post = new Post(); $active_pid = 0; foreach ($sth->fetchAll() as $p) { if ($post->id != $active_pid) { // if this row is for the post we’re if ($post->id) { array_push($posts, $post); } $active_pid = $post->id; // getting comments for this post $post = new Post(); $post->id = $p->[‘id’]; $post->title = $p->[‘title’]; $post->author = $p->[‘author’]; $post->body = $p->[‘body’]; } $comment = new Comment(); $comment->id = $c->[‘id’]; $comment->author = $c->[‘author’]; $comment->body = $c->[‘body’]; array_push($post->comments, $comment); }
  58. 58. @papa_fire that’s awesome! (right?)
  59. 59. @papa_fire do you know what’s under the hood?
  60. 60. @papa_fire ORMs are evil
  61. 61. @papa_fire you have no idea how they work
  62. 62. @papa_fire 1. machine-generated 2. object construction overhead 3. false sense of control
  63. 63. @papa_fire “ think about ORM as the most junior developer you’ve ever worked with
  64. 64. @papa_fire select * from ( select bannerid, caption, client_url, image_file, sponsorid, weight from ( select V.bannerid, V.impressions, B.caption, B.client_url, B.image_file, s.sponsorid, s.weight, row_number() over (partition by s.sponsorid order by s.weight desc) ranking FROM ( -- This level gives me a list of banners sorted by least seen,and then by highest weight select valid.bannerid, valid.totalweight, count(I.timestamp) as impressions FROM ( -- This level gets me a list of banners that are valid for display select b.bannerid, -- Add up the weight from 4 sources. Banner weight, and weight for each data item they match decode( decode(bitand(u.STATE_BM1,b.STATE_BM1),0,0,1) + decode(bitand(u.STATE_BM2,b.STATE_BM2),0,0,1) + decode(bitand(u.STATE_BM3,b.STATE_BM3),0,0,1), 0,0,b.STATE_WT ) + decode(bitand(u.AGE_BM,b.AGE_BM),0,0,b.AGE_WT)+ decode(bitand(u.GENDER_BM,b.GENDER_BM),0,0,b.GENDER_WT)+ b.weight as totalweight from tgif.tbl_users u, tgif.tbl_banners b, tgif.tbl_bannerstats bs where -- I only care about ME! u.userid= 1 -- Don't show inactive banners and b.inactive != 1 -- Only show banners that are currently running and sysdate < b.end_date and sysdate >=b.start_date -- Only get the type of banner i'm looking for and b.type= 3 -- Join on the total stats, and only display banners that haven't reached their per banner maximums and b.bannerid = bs.bannerid and ( b.max_impressions IS NULL OR bs.total_impressions < b.max_impressions ) and ( b.max_clicks IS NULL OR bs.total_clicks < b.max_clicks ) and ( b.max_conversions IS NULL OR bs.total_conversions < b.max_conversions ) -- Ignore any banners that don't match their demographics (ie, male banner won't go to females) and ( b.AGE_BM IS NULL OR b.AGE_BM = 0 OR bitand(u.AGE_BM, b.AGE_BM) != 0 ) and ( b.GENDER_BM IS NULL OR b.GENDER_BM =0 OR bitand(u.GENDER_BM, b.GENDER_BM) != 0 ) and ( b.STATE_BM1 IS NULL OR b.STATE_BM1 =0 OR bitand(u.STATE_BM1, b.STATE_BM1) != 0 ) and ( b.STATE_BM2 IS NULL OR b.STATE_BM2 =0 OR bitand(u.STATE_BM2, b.STATE_BM2) != 0 ) and ( b.STATE_BM3 IS NULL OR b.STATE_BM3 =0 OR bitand(u.STATE_BM3, b.STATE_BM3) != 0 ) -- But don't show me any banners that I have already signed up and b.bannerid NOT IN ( SELECT B.bannerid FROM tgif.tbl_bannerconversions C, tgif.tbl_banners B, tgif.tbl_sponsors sp WHERE C.USERID=1 AND C.bannerid=B.bannerid AND B.sponsorid=sp.sponsorid -- unless they have a conversion interval, and that interval has expired AND ( sp.conversion_interval = 0 OR sysdate > C.timestamp+sp.conversion_interval ) ) -- Don't show me any banners that have SPONSORS that have reached their maximums and b.sponsorid NOT IN ( -- I believe this would be better done using HAVING clauses, but I can't figure it out -- Take the banners for a sponsor in the bannerstats table, and get the totals per sponsor -- return anything that has reached it's maximum select sponsorid FROM ( SELECT S.sponsorid, S.max_impressions, S.max_conversions, S.max_clicks, sum(total_impressions) as imps, sum(total_conversions) as convs, sum(total_clicks) as clicks FROM tgif.tbl_sponsors S, tgif.tbl_banners B, tgif.tbl_bannerstats bs WHERE S.sponsorid=B.sponsorid AND B.bannerid=bs.bannerid GROUP BY S.Sponsorid, S.max_impressions, S.max_conversions, S.max_clicks ) exclude WHERE ( imps > max_impressions OR convs >= max_conversions OR clicks > max_clicks ) ) ) valid, tgif.tbl_bannerimpressions I where valid.bannerid=I.bannerid(+) and I.userid(+)=1 group by valid.bannerid, valid.totalweight -- I want to see banners I haven't seen yet, sorted by highest weight, so we sort by number -- of times that this user has seen this particular banner, then we sort by weight order by impressions, totalweight DESC ) V, tgif.tbl_banners B, tgif.tbl_sponsors S where B.bannerid=V.bannerid and B.sponsorid=S.sponsorid and S.inactive != 1 and s.sponsorid not in ( ) valid, tgif.tbl_bannerimpressions I where valid.bannerid=I.bannerid(+) and I.userid(+)=1 group by valid.bannerid, valid.totalweight -- I want to see banners I haven't seen yet, sorted by highest weight, so we sort by number -- of times that this user has seen this particular banner, then we sort by weight order by impressions, totalweight DESC ) V, tgif.tbl_banners B, tgif.tbl_sponsors S where B.bannerid=V.bannerid and B.sponsorid=S.sponsorid and S.inactive != 1 and s.sponsorid not in ( -- Check the user impression cap to make sure it hasn't been passed by the user select s.sponsorid from tgif.tbl_banners b, tgif.tbl_sponsors s, tgif.TBL_BANNERIMPRESSIONS i where s.sponsorid = b.sponsorid and b.bannerid = i.bannerid and i.timestamp >= sysdate - nvl(user_impression_cap_days,100) and userid = 1 group by s.sponsorid having count(*) >= max(nvl(user_impression_cap,1000000000)) ) -- Make sure the sponsor is still in the valid table. This table is updated hourly -- and contains the sponsors that have not gone over their sponsor level frequencies for -- impressions/conversions/clicks and s.sponsorid in (select sponsorid from tgif.tbl_active_sponsors) ) where ranking=1 --Order the banners by sponsor weight, which is handled by the ranking --order by S.weight order by impressions, weight desc ) where rownum <= 10; would you trust a junior with this query?
  65. 65. @papa_fire object construction is very expensive
  66. 66. @papa_fire METHOD REAL USER SYS PCPU Base ORM 6.330 5.771 0.212 94.51
  67. 67. @papa_fire METHOD REAL USER SYS PCPU Base ORM 6.330 5.771 0.212 94.51 SQL without objects 0.664 0.274 0.120 59.35
  68. 68. @papa_fire METHOD REAL USER SYS PCPU Base ORM 6.330 5.771 0.212 94.51 SQL without objects 0.664 0.274 0.120 59.35 SQL with ORM objects 6.354 5.797 0.197 94.34
  69. 69. @papa_fire get_all_* are the worst
  70. 70. @papa_fire # return a collection with all articles articles = Article.all
  71. 71. @papa_fire # return a collection with all articles articles = Article.all # class/object definition class Article has_many :authors has_many :images, has_many :comments has_many :related_articles end
  72. 72. @papa_fire ORMs are bad for high-traffic production for replacing SQL skills
  73. 73. @papa_fire “ I have yet to find a tool built to avoid using SQL that doesn’t become more complicated to use than just learning SQL John Simone (@j_simone)
  74. 74. @papa_fire ORMs are not bad for prototyping for low-traffic systems for non-public traffic
  75. 75. @papa_fire PSA lazy loading doesn’t solve all your problems
  76. 76. @papa_fire </database>
  77. 77. @papa_fire if you can’t optimize the problem hide the problem
  78. 78. @papa_fire <caching>
  79. 79. @papa_fire “ there are only two hard things in Computer Science: cache invalidation and naming things Phil Karlton
  80. 80. @papa_fire is stale content > slow content?
  81. 81. @papa_fire stale is relative
  82. 82. @papa_fire how valuable is 1 minute?
  83. 83. @papa_fire 100 request/sec served by 2 web servers 1 minute cache (60 seconds) x x 11,998 “cheap” reqs/min 2 “expensive” reqs/min
  84. 84. @papa_fire ① browser ② on-disk ③ in-memory
  85. 85. @papa_fire ① browser ② on-disk ③ in-memory
  86. 86. @papa_fire remote
  87. 87. @papa_fire cache-control header
  88. 88. @papa_fire .htaccess # cache all pages for one month Header set Cache-Control "max-age=2628000, public"
  89. 89. @papa_fire .htaccess # cache static assets for one month <filesMatch ".(jpg|jpeg|png|gif|js|ico)$"> Header set Cache-Control "max-age=2628000, public" </filesMatch>
  90. 90. @papa_fire php # cache this page for one minute <? header("Cache-Control: max-age=60"); ?>
  91. 91. @papa_fire you don’t control browsers
  92. 92. @papa_fire progressive web applications (service workers) https://www.youtube.com/watch?v=3ol3-kvCNeQ Patrick Meenan, Velocity
  93. 93. @papa_fire ① browser ② on-disk ③ in-memory
  94. 94. @papa_fire local, persistant
  95. 95. @papa_fire read content from file
  96. 96. @papa_fire … external source (API, database) … processing content again read content from file instead of …
  97. 97. @papa_fire returnContent() { return generateContent(); }
  98. 98. @papa_fire returnContent() { if (has_cache($cache_file, $cache_ttl)) { return _read_cache($cache_file); } else { return _write_cache($cache_file); } }
  99. 99. @papa_fire ###### FS caching sub _read_cache { my $cache_file = shift; open (my $fh, '<', $cache_file) or die "Can't open file doe reading $!"; read $fh, my $cache_content, -s $fh; close $fh; return $cache_content; } sub _write_cache { my $cache_file = shift; my $content = generateContent(); open(my $fh, '>', $cache_file) or die "Could not open file '$cache_file' $!"; print $fh $content; close $fh; return $cache_content; }
  100. 100. @papa_fire have to manage your own ttl
  101. 101. @papa_fire sub has_cache { my $cache_file = shift; my $cache_ttl = shift; #in seconds if (-f $cache_file) { # in seconds my $last_updated = (stat($cache_file))[9]; my $now = int (gettimeofday); my $diff = $now - $last_updated; return 1 if ($now - $last_updated <= $cache_ttl); } return 0; }
  102. 102. @papa_fire ① browser ② on-disk ③ in-memory
  103. 103. @papa_fire memcache, redis
  104. 104. @papa_fire local or distributed
  105. 105. @papa_fire function returnContent($article_id) { $memcache = new Memcache; $cacheAvailable = $memcache->connect(MEMCACHED_HOST, MEMCACHED_PORT); if ($cacheAvailable) { $cached_article = $memcache->get(‘article-’.$article_id); if (!$cached_article) { $article = getArticle($article_id); $memcache->set(‘article-‘.$article_id, $article); return $article; } else { return $cached_article; } } }
  106. 106. @papa_fire function returnContent($article_id) { $memcache = new Memcache; $cacheAvailable = $memcache->connect(MEMCACHED_HOST, MEMCACHED_PORT); if ($cacheAvailable) { $cached_article = $memcache->get(‘article-’.$article_id); if (!$cached_article) { $article = getArticle($article_id); $memcache->set(‘article-‘.$article_id, $article); return $article; } else { return $cached_article; } } } $cached_article = $memcache->get(‘article-’.$article_id); $memcache = new Memcache; $memcache->set(‘article-‘.$p->[‘article_id’], $article); if (!$cached_article) {
  107. 107. @papa_fire returnContent() { $content = read_cache($cache_key); if (!$content) { $content = generateContent(); write_cache($cache_key, $content); } return $content; }
  108. 108. @papa_fire remember /get/articles/all ? (multi-nested ORM function)
  109. 109. @papa_fire METHOD BASE LOAD 20x TRAFFIC SPIKE Base call 5.782 s 12.127 s
  110. 110. @papa_fire METHOD BASE LOAD 20x TRAFFIC SPIKE Base call 5.782 s 12.127 s Base call with memcache 0.867 s 0.922 s
  111. 111. @papa_fire not just for db results
  112. 112. @papa_fire $memcache->set($uri, $html);
  113. 113. @papa_fire doesn’t have to be 100% static
  114. 114. @papa_fire don’t forget to purge
  115. 115. @papa_fire careful what you cache
  116. 116. @papa_fire “pretty” urls
  117. 117. @papa_fire “pretty” urls /my/profile
  118. 118. @papa_fire “ugly” urls
  119. 119. @papa_fire “ugly” urls /prodlist.php?SID=a3e9590a-6598-11e7-907b-a6006ad3dba0
  120. 120. @papa_fire external cache
  121. 121. @papa_fire reverse HTTP proxy
  122. 122. @papa_fire Apache Traffic Server, Varnish
  123. 123. @papa_fire CDN Akamai, Fastly
  124. 124. @papa_fire rule-based controls
  125. 125. @papa_fire use as many or as few
  126. 126. @papa_fire </caching>
  127. 127. @papa_fire <queueing>
  128. 128. @papa_fire remember the part about critical path?
  129. 129. @papa_fire anything not related to the immediate outcome should not be in the critical path
  130. 130. @papa_fire router.post('/register', function(req, res){ var user = new User(req.body); user.save( function(err) { if (!err) { user.savePreferences(req.pref); user.sendWelcomeEmail(); res.render(‘first_time_welcome', { title: 'Welcome' + user.name, errors: [err] }); } }); });
  131. 131. @papa_fire router.post('/register', function(req, res){ var user = new User(req.body); user.save( function(err) { if (!err) { user.savePreferences(req.pref); user.sendWelcomeEmail(); res.render(‘first_time_welcome', { title: 'Welcome' + user.name, errors: [err] }); } }); }); user.sendWelcomeEmail();
  132. 132. @papa_fire User.sendWelcomeEmail = function(callback){ if (!do_not_email(user.email) { var email = new Email(); email.subject = "Welcome " + user.firstname; email.cta = getDealoftheDay(); email.secondary = getOffers(user); email.generateBody(user, function() { email.send(); }); } };
  133. 133. @papa_fire does it have to be done now?
  134. 134. @papa_fire router.post('/register', function(req, res){ var user = new User(req.body); user.save( function(err) { if (!err) { user.savePreferences(req.pref); sendToQueue(‘welcome email’,user); res.render(‘first_time_welcome', { title: 'Welcome' + user.name, errors: [err] }); } }); }); sendToQueue(‘welcome email’,user);
  135. 135. @papa_fire queueing (v.) - putting things somewhere else to deal with later
  136. 136. @papa_fire where? 1. in-memory (RabbitMQ, SQS) 2. persistant (database)
  137. 137. @papa_fire </queueing>
  138. 138. @papa_fire so …
  139. 139. @papa_fire optimize everything (right?)
  140. 140. @papa_fire “premature optimization is the root of all evil Donald Knuth
  141. 141. @papa_fire improvements should be … 1. needed
  142. 142. @papa_fire improvements should be … 1. needed 2. incremental
  143. 143. @papa_fire improvements should be … 1. needed 2. incremental 3. measurable
  144. 144. @papa_fire that said
  145. 145. @papa_fire always think about performance
  146. 146. @papa_fire there is an exception to every rule parting tip
  147. 147. @papa_fire thank you questions?

×