Slide 1

Slide 1 text

CDNフル活用でつくる 高速Webアプリ ~ こえのブログ実装例 ~ Oct. 23 2019, Yamagoya 2019 Kazunari Hara, CyberAgent Using CDN To Improve Web Performance

Slide 2

Slide 2 text

CyberAgent Web Team is now on Performance Accessibility Security

Slide 3

Slide 3 text

CyberAgent Web Team is now on WebパフォーマンスとプロダクトKPIの相関を可視化 する話 https://developers.cyberagent.co.jp/blog/archives/9540/ アクセシビリティへの取り組み https://www.cyberagent.co.jp/way/csr/accessibility/ アメブロ2017 – 大規模サービスhttps化 https://developers.cyberagent.co.jp/blog/archives/7743/

Slide 4

Slide 4 text

CyberAgent Web Team is now

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Posting a blog entry just by speaking

Slide 7

Slide 7 text

こんにちは 今日はFastly 山小屋にきました Cloud Speech-to-Text

Slide 8

Slide 8 text

こえのブログ is

Slide 9

Slide 9 text

https://twitter.com/sisidovski/status/1158980641955823616 Lighthouse score with firework

Slide 10

Slide 10 text

Performance: - Fast - Consistant

Slide 11

Slide 11 text

“Spike” - a sharp increase in the magnitude or concentration of visitors Spike reasons: - Breaking news - Launch days - Disasters - Bots - System issues

Slide 12

Slide 12 text

When your site is slow...

Slide 13

Slide 13 text

When your site is down...

Slide 14

Slide 14 text

Cache helps performance improvement by reusing resources and removing unnecessary calculation on servers Origin CDN

Slide 15

Slide 15 text

こえのブログ is made with Fastly and GCP to maintain backends more easily

Slide 16

Slide 16 text

Cache Stats of こえのブログ

Slide 17

Slide 17 text

98% cache hit ratio on CDN

Slide 18

Slide 18 text

98% cache hit ratio on CDN if you have cats

Slide 19

Slide 19 text

98% cache hit ratio on CDN if you have cats

Slide 20

Slide 20 text

99% cache coverage on CDN

Slide 21

Slide 21 text

99% cache coverage on CDN if you have cats

Slide 22

Slide 22 text

99% cache coverage on CDN if you have cats

Slide 23

Slide 23 text

98% cache coverage in browser on repeat view

Slide 24

Slide 24 text

Fast and consistent even during spike as CDN returns cache CDN Origin Client 98%

Slide 25

Slide 25 text

Less network access on repeat view CDN Origin Client 98%

Slide 26

Slide 26 text

Cache Strategy

Slide 27

Slide 27 text

Cache strategy - Keep resources static - Define consistent URLs - Set long cache time (TTL) - Divide endpoint if the resource changes frequently - Tagging cacheable resources and purging if necessary

Slide 28

Slide 28 text

Static: - Same for everyone - Cacheable Dynamic: - Variable by user/session - Hard to cache efficiently Keep as many resources static as possible

Slide 29

Slide 29 text

Keep as many resources static as possible Web App Assets: Entry Assets: {
 “id”: “12345”, 
 “title”: “voice” 
 }
 


Slide 30

Slide 30 text

Most resources of こえのブログ are static since the app was made by a Client Side Rendering (CSR) App {
 “id”: “12345”, 
 “title”: “voice” 
 }
 
 Fetch data from JS when necessary

Slide 31

Slide 31 text

User data is rendered asynchronousl y Displayed later than ranking

Slide 32

Slide 32 text

Define consistent URLs using HTTP methods GET - Retrieve resource information only - Do not change the state of the resource - Return the same result every time

Slide 33

Slide 33 text

Define consistent URLs using HTTP methods POST/PUT/DELETE - create/update/delete resources - Not cacheable

Slide 34

Slide 34 text

Define resources as cacheable or not Cacheable: - Most GET endpoints Not cacheable: - Few GET endpoints with session - Few POST/PUT/DELETE endpoints

Slide 35

Slide 35 text

URL example GET / GET /editor GET /src/**/*.js GET /images/**/*.{png|jpeg|svg} GET /assets/audios/${userId}/${entryId}/ GET /assets/images/${userId}/${entryId}/ GET /api/entry/${userId}/${entryId}/ POST /api/entry/${userId}/ PUT /api/entry/${userId}/${entryId}/ DELETE /api/entry/${userId}/${entryId}/ GET /api/auth/${userId}/status/ Cacheable

Slide 36

Slide 36 text

93% of endpoints are cacheable

Slide 37

Slide 37 text

Set cache TTL as long as possible Response header for CDN: “Surrogate-Control: max-age=” - Default: 2592000 (30 days) - Fallback: 86400 (1 day)

Slide 38

Slide 38 text

Set cache TTL as long as possible Response header for browsers: “Cache-Control: max-age=” - Default: ※ 120 (2 minutes) - Fallback: 86400 (1 day) ※ Enabled longer cache with service worker

Slide 39

Slide 39 text

Divide endpoint if the resource changes frequently Entry { “id”: “12345”, “title”: “voice”, “view-count”: 2946 } Meta data is immutable View count changes frequently

Slide 40

Slide 40 text

Divide endpoint if the resource changes frequently Response header for stale content: “Stale-While-Revalidate:” CDN Origin Update data in background

Slide 41

Slide 41 text

Tagging cacheable resources with surrogate key “webapp” “blogger/ ameba” “entry/12345 blogger/ameba”

Slide 42

Slide 42 text

Purge the tag when resources have been updated New version release curl -XPOST -H “Fastly-Key:${KEY}” \n -H “Accept:application/json” \n “https://api.fastly.com/service/${SERVICE}/pu rge/webapp”

Slide 43

Slide 43 text

Purge the tag when resources have been updated Data updated curl -XPOST -H “Fastly-Key:${KEY}” \n -H “Accept:application/json” \n -H “Surrogate-Key:blogger/ameba entry/12345” \n “https://api.fastly.com/service/...” Cloud Filestore

Slide 44

Slide 44 text

These key ideas are also useful even when your app uses server side rendering (SSR) To be continued in the fastly meetup #3

Slide 45

Slide 45 text

Backends

Slide 46

Slide 46 text

Cloud Filestore Cloud Storage Cloud Functions Stable Serverless with Fastly and GCP - High CDN cache coverage - Event-driven execution - Auto scalling - Deployability

Slide 47

Slide 47 text

Cloud Storage Static files in Cloud Storage - Scalable - As an origin server - HMAC Authentication https://docs.fastly.com/en/guides/google-cloud-storage

Slide 48

Slide 48 text

Cloud Storage Cloud Filestore Event-driven functions Cloud Functions Some actions Event Cloud Storage

Slide 49

Slide 49 text

onDatabaseUpdate Cloud Filestore Cloud Functions Update entryId: 12345 Purge Surrogate-Key: entry/12345

Slide 50

Slide 50 text

onDatabaseDelete Cloud Filestore Cloud Functions Delete entryId: 12345 Purge Surrogate-Key: entry/12345 Cloud Storage Delete audio

Slide 51

Slide 51 text

“deployability” is an important metric for app quality Over 1,000 web app releasesin 10 month Over 200 VCL deploymentsin 10 month

Slide 52

Slide 52 text

https://speakerdeck.com/herablog/web-app-checklist-2019-at-inside-frontend?slide=134

Slide 53

Slide 53 text

Browser Cache

Slide 54

Slide 54 text

Browser cache strategy No Service Worker: - Cache-Control header Service Worker: - Precaching - Runtime caching Service Worker is available in most modern browsers today

Slide 55

Slide 55 text

Web app assets are cached with service worker at first visit and are never re-fetched until updated // service-worker.js workbox.precaching.precacheAndRoute([ { url: "images/title_service_header.svg", revision: "6a048b548112674c9e65ed" }, { url: "index.html", revision: "6805b47012688211c81521" }, { url: "src/components/voice-app.js", revision: "fdf10df6f530cbce55c8f5" }, ... ]);

Slide 56

Slide 56 text

Other resources are cached as runtime cache and can be used when offline CDN

Slide 57

Slide 57 text

VCL Recipes

Slide 58

Slide 58 text

Cache key to normalize method=${req.request}; origin=${req.http.host}; url=${req.url}; vary=${req.http.Accept-Encoding}; URL and vary can be variable

Slide 59

Slide 59 text

Query normalization import querystring; sub vcl_recv { set req.url = querystring.sort(req.url); set req.url = querystring.regfilter_except( req.url, "^(language|format|offset|limit)$" ); } https://docs.fastly.com/vcl/query-string-manipulation/

Slide 60

Slide 60 text

Vary normalization sub vcl_recv { if (req.http.User-Agent !~ "Edge" && req.http.User-Agent ~ "Chrome/([^.]*)") { set req.http.X-UA = "esm"; } else { set req.http.X-UA = "es5"; } } sub vcl_fetch { set beresp.http.Vary = "Accept-Encoding, X-UA"; } https://docs.fastly.com/vcl/query-string-manipulation/

Slide 61

Slide 61 text

Basic Auth table customer_keys { "Basic a2FrZXJ1OmthZXJ1": "kakeru", "Basic a2FlcnU6a2FrZXJ1": "kaeru" } sub vcl_recv { if (!table.lookup( customer_keys, req.http.Authorization )) { error 401 "Restricted"; } } https://docs.fastly.com/en/guides/basic-authentication

Slide 62

Slide 62 text

Basic Auth sub vcl_error { if (obj.status == 401) { set obj.http.Content-Type = "text/html; charset=utf-8"; set obj.http.WWW-Authenticate = "Basic realm=Secured"; synthetic {"

Slide 63

Slide 63 text

Access control acl office_ip_ranges { "192.0.2.0"/24; "198.51.100.4"; } sub vcl_recv { if (!(client.ip ~ office_ip_ranges)) { error 403 "Forbidden"; } } https://docs.fastly.com/en/guides/about-acls

Slide 64

Slide 64 text

Routing to different origins sub assets_origin { set req.backend = F_assets_origin; set req.http.host = "${api-assets-domain}"; } sub api_origin { set req.backend = F_api_origin; set req.http.host = "${api-org-domain}"; } sub vcl_recv { if (req.url.path ~ "^/assets/") { call assets_origin; } else if (req.url.path ~ "^/api/") { call api_origin; }... }

Slide 65

Slide 65 text

Adding surrogate key sub vcl_fetch { declare local var.SurrogateKey STRING; set var.SurrogateKey = "assets"; if (req.http.x-url ~ "/audios/standard/([a-z0-9-]{3,24})/([a-zA-Z0-9]+)") { set var.SurrogateKey = var.SurrogateKey + " blogger/" + re.group.1 + " entry/" + re.group.2; } set beresp.http.Surrogate-Key = var.SurrogateKey; } Extracting keys from URL path

Slide 66

Slide 66 text

Modifying response headers sub vcl_deliver { unset resp.http.via; unset resp.http.server; add resp.http.Server-Timing = fastly_info.state {", fastly;desc="Edge time";dur="} time.elapsed.msec; set resp.http.Referrer-Policy = "origin-when-cross-origin"; set resp.http.Strict-Transport-Security = "max-age=31536000"; set resp.http.X-Content-Type-Options = "nosniff"; add resp.http.Content-Security-Policy = "default-src 'self';...” add resp.http.Content-Security-Policy = "upgrade-insecure-requests"; } Removing unnecessary headrers Adding useful headers

Slide 67

Slide 67 text

(CDN) makes your app stable, predictable, efficient, deployable and enjoyable

Slide 68

Slide 68 text

(CDN) makes your app

Slide 69

Slide 69 text

@herablog Related documents: - アメブロ2019: こえのブログでのPWA https://developers.cyberagent.co.jp/blog/archives/20506/ - Web App Checklist 〜高品質のWebアプリケーションをつくる ために〜 https://speakerdeck.com/herablog/web-app-checklist-2019-at-inside-frontend - こえのブログでのPWA ~ PWA編 ~ https://speakerdeck.com/herablog/pwa-night-vol-dot-4 - こえのブログでのPWA ~ 開発現場編 ~ https://speakerdeck.com/herablog/koe-no-blog-pwa - 最新CDN入門 WEB+DB PRESS Vol.109 https://gihyo.jp/magazine/wdpress/archive/2019/vol109