|
| 1 | +Turbocharger |
| 2 | +============ |
| 3 | + |
| 4 | +"Learn this one weird trick to CDN-enable your applications." |
| 5 | + |
| 6 | +Allows your loadbalancer to know exactly when (and which) files are changed due to the a URL mount |
| 7 | +point describing its sub-tree with an immutable manifest identifier. |
| 8 | +When any file changes, the manifest changes. The manifest has immutable identifiers for each file |
| 9 | +version, so even when the manifest changes we can transfer only the changed files. |
| 10 | + |
| 11 | + |
| 12 | +## Design principles |
| 13 | + |
| 14 | +- Suitable for hosting both: |
| 15 | + * static websites and |
| 16 | + * hosting static subtrees of dynamic web apps (be it Docker container, AWS Lambda function etc.) |
| 17 | +- Make the minimum amount of changes to an end-application to enable very aggressive caching. |
| 18 | +- Progressive enhancement. The same web-app must be runnable without turbocharger-aware loadbalancing |
| 19 | + and it should still work. |
| 20 | +- What if you could have the authority of your app's static asset serving even on AWS Lambda (which is |
| 21 | + not great for static file serving) but still have ridiculously great performance? |
| 22 | + * Looking at this from perspective of hybrid Lambda / traditional web servers (same HTTP server runs unchanged on Lambda). |
| 23 | +- Deployments and serving can benefit from differential transfer (= only changed files uploaded to storage/downloaded to cache) |
| 24 | + * My 30.8 MB deploys used to take 48 s, now takes 13 s (with barely any data transferred as most |
| 25 | + of the time goes into "exists" checks in the CAS) |
| 26 | +- Content-addressability ❤️ |
| 27 | + |
| 28 | + |
| 29 | +## Design properties |
| 30 | + |
| 31 | +- Very aggressive caching capabilities |
| 32 | +- Serve gzip'd content that is actually gzip'd at cache level, so we need to only compress each file once |
| 33 | +- Static sites are served atomically, but we still get differential transfers to backing store |
| 34 | + (no need to upload the full tree each time) |
| 35 | +- Hybrid dynamic/static apps (dynamic web app with sub-tree e.g. /static being static) should work |
| 36 | + without turbocharger |
| 37 | +- Minimal changes to hybrid apps to enable turbocharging. request to `/static/main.js` returns |
| 38 | + header `turbocharger: 60303ae22b998861 /static`. The form is `turbocharger: <manifest ID> <tree>`. |
| 39 | +- Above reads "everything under `/static` is found from manifest `60303ae22b998861` in CAS" |
| 40 | +- Loadbalancer only needs to download immutable manifest `60303ae22b998861` once. It contains mappings |
| 41 | + like `{"/main.js": "fd61a03af4f77d87", "/images/3.jpg": "a4e624d686e03ed2"}` which are yet again found from CAS. |
| 42 | +- We don't even have to pass would-be-404s to origin, since we know whether paths exist or not based on the manifest. |
| 43 | +- Support custom 404 pages |
| 44 | + |
| 45 | + |
| 46 | +## Deploy command |
| 47 | + |
| 48 | +```console |
| 49 | +$ cat site.tar.gz | gzip -d | edgerouter turbocharger tar-deploy-to-store joonas.fi-blog |
| 50 | +``` |
| 51 | + |
| 52 | +The command gave you a manifest ID `QSA90-KjEwnNaPn2qdlo6cHJQeSazX_A1eizwOAl_fM` |
| 53 | + |
| 54 | +Edgerouter app definition looks like this: |
| 55 | + |
| 56 | +```json |
| 57 | +{ |
| 58 | + "id": "joonas.fi", |
| 59 | + "frontends": [ |
| 60 | + { |
| 61 | + "kind": "hostname", |
| 62 | + "hostname": "joonas.fi", |
| 63 | + "path_prefix": "/" |
| 64 | + } |
| 65 | + ], |
| 66 | + "backend": { |
| 67 | + "kind": "turbocharger", |
| 68 | + "turbocharger_opts": { |
| 69 | + "manifest": "QSA90-KjEwnNaPn2qdlo6cHJQeSazX_A1eizwOAl_fM" |
| 70 | + } |
| 71 | + } |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +When a new version of the website has been deployed to Turbocharger, we only update the manifest ID in Edgerouter. |
| 76 | + |
| 77 | +A rollback, should you need one, is exactly as easy as just reverting to an old manifest ID. |
| 78 | + |
| 79 | + |
| 80 | +## Acceleration to Lambda static files |
| 81 | + |
| 82 | +tl;dr 10 500 k reqs/s vs 26 reqs/s |
| 83 | +With: |
| 84 | + |
| 85 | +```console |
| 86 | +$ hey -z 15s https://happppppy.dev.fn61.net/happy/static/images/MAHj.jpg |
| 87 | + |
| 88 | +Summary: |
| 89 | + Total: 15.0112 secs |
| 90 | + Slowest: 0.1397 secs |
| 91 | + Fastest: 0.0003 secs |
| 92 | + Average: 0.0048 secs |
| 93 | + Requests/sec: 10513.5114 |
| 94 | + |
| 95 | +Response time histogram: |
| 96 | + 0.000 [1] | |
| 97 | + 0.014 [152975] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ |
| 98 | + 0.028 [4686] |■ |
| 99 | + 0.042 [100] | |
| 100 | + 0.056 [8] | |
| 101 | + 0.070 [0] | |
| 102 | + 0.084 [2] | |
| 103 | + 0.098 [7] | |
| 104 | + 0.112 [20] | |
| 105 | + 0.126 [20] | |
| 106 | + 0.140 [1] | |
| 107 | +``` |
| 108 | + |
| 109 | +Without: |
| 110 | + |
| 111 | +```console |
| 112 | +$ hey -z 15s https://happppppy.dev.fn61.net/happy/static/images/MAHj.jpg |
| 113 | + |
| 114 | +Summary: |
| 115 | + Total: 16.5229 secs |
| 116 | + Slowest: 2.5380 secs |
| 117 | + Fastest: 0.1106 secs |
| 118 | + Average: 1.9107 secs |
| 119 | + Requests/sec: 26.1456 |
| 120 | + |
| 121 | +Response time histogram: |
| 122 | + 0.111 [1] | |
| 123 | + 0.353 [6] |■ |
| 124 | + 0.596 [2] | |
| 125 | + 0.839 [2] | |
| 126 | + 1.082 [10] |■■ |
| 127 | + 1.324 [27] |■■■■■■ |
| 128 | + 1.567 [95] |■■■■■■■■■■■■■■■■■■■■■■ |
| 129 | + 1.810 [9] |■■ |
| 130 | + 2.052 [13] |■■■ |
| 131 | + 2.295 [169] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ |
| 132 | + 2.538 [98] |■■■■■■■■■■■■■■■■■■■■■■■ |
| 133 | +``` |
| 134 | + |
| 135 | +Caveat: my internet download speed (100 Mbps) might have played some part in this. |
| 136 | + |
| 137 | + |
| 138 | +## Under the hood |
| 139 | + |
| 140 | +### CAS |
| 141 | + |
| 142 | +Turbocharger stores files in content-addressable storage (CAS). |
| 143 | + |
| 144 | +Loadbalancer middleware is a special case by having two distinct storages. One file belongs in either one, |
| 145 | +depending on whether the file is compressible. HTML/JS/CSS files are compressible, JPEG/GIF/MP4 not etc. |
| 146 | + |
| 147 | +```console |
| 148 | +$ tree /var/cache/turbocharger |
| 149 | +/var/cache/turbocharger |
| 150 | +|-- gzipped |
| 151 | +| |-- -Kdyyv6A-qJczcMAbOJ3QH6JBeLrPGbxA3V1H7sjbuQ |
| 152 | +| |-- 2c0kmvbEI6ntwS7HcgtfPCfgk52Q5Qn43sD72Y0kjWQ |
| 153 | +| |-- 9BwJGZOvKmtxbyNLY64_RSjWziDadkYQMPQU8mkYTCk |
| 154 | +| |-- CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo |
| 155 | +| |-- DUjVq194puHcF9uJS31YnKLU1-vHpEQaj6xTJo5ue0o |
| 156 | +| |-- GG6rubuufpiDgU2xzUPaRhRmGCJpTlVpOSuDe5rIaa0 |
| 157 | +| |-- IwP_WbN-9OLn-aXlzR1Nqxr_yXxOaViWlqjCcmei3ik |
| 158 | +| |-- KDOGdXu9LEP88Sxk09iFj1c-mSGFBnjBRvbLWtAkVnY |
| 159 | +| |-- Li4njatBsm77LsXhZKhxkcnLJOEddOrzL8YE1NsQcGc |
| 160 | +| |-- M0OtarCfRjuLJHJn-1qwc4e5LPkll0xRpnGPNnIPxSU |
| 161 | +| |-- Uhl3arhfll5-lvXPUL6FR-Y9oqdOgdVp7TL-XBJXI8o |
| 162 | +| |-- gsTkEqAR2lRZv_JJkuCQXa2IUvJbcSr2VkytNYJfRe8 |
| 163 | +| |-- mK6SlQMxtVBpMfKqk-2X-n2Dg5N5FSRDsqztxiPTq68 |
| 164 | +| |-- oA6JKKCKPrBXtUnZfZLQnMX-PpEqH21JMgzzNIG76Jk |
| 165 | +| |-- slxc7pGfBj7mDISbKaDfHYnLA0CZZcBgWyF10xUA6rA |
| 166 | +| |-- ybRkN9dBjhcS2qrW1z-hfCxq-1aBdwyQM5wlQoQVt_0 |
| 167 | +| `-- zBZVpaScPA3I81AlWXXmC-2UidCMAzLbMDJ4pHUceoY |
| 168 | +`-- uncompressed |
| 169 | + |-- -AugZmBihv6ryHzU9qEbv2uAkU5EyZ-VJ7FY5X5BILU |
| 170 | + |-- 0lqgykAFIVyZde2jKai35I5hlYRzFFi82onmAjjIjd8 |
| 171 | + |-- 1kjbiRFLQYABs011bYXYzCBgl1xb7wtcQNfRmKkMRPc |
| 172 | + |-- 47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU |
| 173 | + |-- 4zvX0_PJR9K9JUqQW3hib6m8pCeGWtCW-YNAPvWtZdM |
| 174 | + |-- 8HrzjmBGMDYb5tMH7Svg1SdbLwqcG0rLbRc6G9S33pU |
| 175 | + |-- 94z4eqeLna1bJWa7v9UMrV41ulOXi5IC8lfXuD82LUQ |
| 176 | + |-- A4J8a_K-mbtR9IXn4Mlpn8UA7gfobM61u-t9ruuRYL0 |
| 177 | + |-- A5PIVDzDbZTZSv5gR9B_oU00xhLOpEQtbDitW6UZul0 |
| 178 | + |-- Aeo6B7DhmprNoCeMHk82HP__EjBKbFYTjtQx1kmZFX8 |
| 179 | + |-- DEpHFvWeQDUH_-JVjhha1U7LV7U9kDuq8IyaXjzPd40 |
| 180 | + |-- EbQ1G9b1wxGXFRMyds641xtOvdc2fhx4piqSQMLCoq8 |
| 181 | + |-- HFyNSHGB6TmWdCmRLJ70NBbCsnoL9QomZePlhHN5Z9k |
| 182 | + |-- HkO_KF-BEeJKo1J2VRh62ThI2RKfUylhCEKMbCJqjhA |
| 183 | + |-- IeFmJKCp-fPdTnVYfSo-5QJeFDzl-5Q059B1yJldsyU |
| 184 | + |-- JQNr46zgAj6fNPTHE25V4GuEeZNKyMEYUy0O6ApEDOk |
| 185 | + |-- McJ01ZV4HsWk9uhHRA96pQSoYi1_5hGYT0_2vHwCoYg |
| 186 | + |-- NSUffPCW6GrMMbbJkFdafGTvHFOpCO8I-Niu3hguXsE |
| 187 | + |-- OAxSZYuYJjaoMrOZfjt6Kypp7_Fn0_3zrhI9KCqAJ40 |
| 188 | + |-- PF-51x7x4jY7onUD8LgJB7NBOMMaCcmctTVRccYryBA |
| 189 | + |-- QSA90-KjEwnNaPn2qdlo6cHJQeSazX_A1eizwOAl_fM |
| 190 | + |-- R1D0OwzOwvdogt7--ryPsouhQC5kcAujcKPKfZriu4s |
| 191 | + |-- R1GeXefKquorSYYlErtheWTZtwTOC9-gquzbo4Kpskc |
| 192 | + |-- RuJoSi-45h2BmDjWQfyl9EQCZ34sQjkmdQBR_eD8EBY |
| 193 | + |-- S4olAlrNWKBzGYpE9Y0u_uE6_xSKPj3kuxRiPuZNDY0 |
| 194 | + |-- V23ek1ZBLQDPnZMbt6YXAfCnha6GAS3D9y254ZHRS6c |
| 195 | + |-- X6fxglqnVDW0RHvM-yy3d5g8Sb6w_keuAeqce-MUWsQ |
| 196 | + |-- YjvTZDJ-0DLqlEhKnWT40_gGQWnaD_JxuHBBZnLtFLw |
| 197 | + |-- ZP_4gzwct-cqbg9MMDKr-TuSCAoLCYLvfH3KIrptYxs |
| 198 | + |-- _2H6cgAe-J7r7CcVzjM4yEzPikyPuQDpdmI1Pf13SkY |
| 199 | + |-- ag1Xjf-QOF4hmVbQXqNWKB72dVUzQVtcWpclRUX7lAM |
| 200 | + |-- eH12rW3qtnzPi6wbWEJgIF4RT1CPxVQrYS4_ddSaNOQ |
| 201 | + |-- jUpJ0sVGq8urLeQBxNUSHsGI6pMY41HtXOcByxxUz-4 |
| 202 | + |-- jkVgwWx5cO-kdoBFCyzyOdSkgsBW0wis6hK7kCKQbIs |
| 203 | + |-- lbOk-pUdHiag_OdxsTvMAk1FgWPBmXullZie5KtYjv4 |
| 204 | + |-- mWT4lKrQH2fUKTb8w-DDMCxsnPKME6AGe6RIzNZOX2Y |
| 205 | + |-- ma4OtCOO0loJ3iyOha4I-BHi_IOFFTwnsR4HVQTTTH8 |
| 206 | + |-- mploPCZGhbVOMGg547pc7_-43YU9JLOcoHCCJHWvY5E |
| 207 | + |-- nLxKJbyCuOIulOQUfiXY9q83qWeOCr8DPn5Cm5Yz4-0 |
| 208 | + |-- pd1RvRGe4HuU6GPpfdzsNTZ61jDaqu5j9yRnbm78B3o |
| 209 | + |-- sX_liXc11SeIJFKnclqTS4yfSqhFdz2YZCo8amATJfY |
| 210 | + |-- uPWpWwnXtAHTg5RcG_059gDPqbiTkdsKclcZ4BlHdek |
| 211 | + |-- upejB3TL0OoPLTg0dJJKHd9yIzu6vOJvNhSPmcqDSu8 |
| 212 | + `-- za9gb2ASJDqlvo9V3Bb9jiexP0fn8rj_MEe_jYM0PLs |
| 213 | +``` |
0 commit comments