Slide 1

Slide 1 text

When Node and APIs become BFFs @nettofarah

Slide 2

Slide 2 text

Netto Farah @nettofarah Eng. Manager at

Slide 3

Slide 3 text

This is a story about

Slide 4

Slide 4 text

☁ monorail v0 -> inception

Slide 5

Slide 5 text

but as we grew…

Slide 6

Slide 6 text

IFTTT ❤ • Millions of users • Billions of API calls every day • Website, iOS app, Android app

Slide 7

Slide 7 text

that architecture became challenging

Slide 8

Slide 8 text

☁ monorail challenges with v0

Slide 9

Slide 9 text

we knew we needed to make some changes

Slide 10

Slide 10 text

v1 -> monorail + friends ☁ monorail Feeds Recommendations Geolocation

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

What makes this approach so challenging?

Slide 13

Slide 13 text

reality monorail Feeds Recommendations Geolocation ⛈

Slide 14

Slide 14 text

repeated concerns

Slide 15

Slide 15 text

We looked at what other people were doing…

Slide 16

Slide 16 text

http://microservices.io/patterns/apigateway.html https://nginx.com/blog/microservices-api-gateways- part-1-why-an-api-gateway/

Slide 17

Slide 17 text

Feed Recommendations Geolocation MonoRail ☁ C
 O O
 R
 D
 I
 N
 A
 T I O N T
 R
 A C
 I
 N G A U T H … v2 -> API Gateway

Slide 18

Slide 18 text

API Gateway

Slide 19

Slide 19 text

but API Gateways have their limitations too

Slide 20

Slide 20 text

different access patterns

Slide 21

Slide 21 text

multiple use cases

Slide 22

Slide 22 text

ambiguity

Slide 23

Slide 23 text

solved with documentation or conventions

Slide 24

Slide 24 text

[/,0,1..2] => ✅ 4

Slide 25

Slide 25 text

4 [5] => ask me later…

Slide 26

Slide 26 text

backends for frontends

Slide 27

Slide 27 text

BFFs

Slide 28

Slide 28 text

Feed Recommendations Geolocation MonoRail ☁ v2 -> BFFs ☁ ☁

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

But there’s still a fair amount of repetition

Slide 31

Slide 31 text

buffalo

Slide 32

Slide 32 text

Feed Recommendations Geolocation MonoRail ☁ v2.1 -> Buffalo ☁ ☁

Slide 33

Slide 33 text

But this is JSKongress

Slide 34

Slide 34 text

❤ + = =

Slide 35

Slide 35 text

simple concurrency primitives

Slide 36

Slide 36 text

building reliable BFFs with node

Slide 37

Slide 37 text

loading async resources

Slide 38

Slide 38 text

// get some async resource get(url).then(result => { // render json return res.json(result) })

Slide 39

Slide 39 text

const promises = Promise.all([ fetch(feedURL), fetch(locationURL) ]) promises.then(result => { return res.json({ feed: result[0], location: result[1] }) })

Slide 40

Slide 40 text

Service latency and network volatility

Slide 41

Slide 41 text

let’s take a look at our feed service

Slide 42

Slide 42 text

99pct ~ 1500ms avg ~ 200ms

Slide 43

Slide 43 text

const promises = Promise.race([ fetch(feedURL), fetch(feedURL), fetch(feedURL) fetch(feedURL) ]) promises.then(feed => { return res.json(feed) })

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

timeouts

Slide 46

Slide 46 text

const pTimeout = require(‘p-timeout') // 99pct was ~1500ms
 pTimeout( get(feedURL), 1600 ).then(onSuccess, onError) function onError(err) { console.error(err) //=> [TimeoutError: Promise timed out] return res.error(err) }

Slide 47

Slide 47 text

failures Service

Slide 48

Slide 48 text

retries

Slide 49

Slide 49 text

const retry = require(‘p-retry') // retry 2 times before giving up retry(loadFeed, { retries: 2 }) // everything is still a promise function loadFeed() { return get(feedURL).then(feedRes => {
 return feedRes.json() }) }

Slide 50

Slide 50 text

failures + latency Service

Slide 51

Slide 51 text

timeouts + retries?

Slide 52

Slide 52 text

99pct ~ 1500ms avg ~ 200ms median ~ 150ms

Slide 53

Slide 53 text

most responses are fast!
 ~ 200ms slow responses are actually pretty rare

Slide 54

Slide 54 text

~3 x 250ms ~= 750ms
 1 x 1500ms ~= 1500ms

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

// avg response time is ~200ms const feedTimeout = 250 pTimeout( loadFeed, feedTimeout ) .then(onSuccess) .catch(onError)

Slide 57

Slide 57 text

// avg response time is ~200ms const feedTimeout = 250 const retries = 2 pRetry( () => pTimeout(loadFeed,feedTimeout), { retries } ) .then(onSuccess) .catch(onError)

Slide 58

Slide 58 text

// avg response time is ~200ms
 const feedTimeout = 250 const totalTimeout = 700 const retries = 2 pTimeout( pRetry( () => pTimeout(loadFeed, feedTimeout), { retries } ), totalTimeout ) .then(onSuccess) .catch(onError)

Slide 59

Slide 59 text

homework • conditional retries • exponential backoffs • circuit breakers

Slide 60

Slide 60 text

using promises to build well behaved BFFs

Slide 61

Slide 61 text

let’s take a look at our analytics service

Slide 62

Slide 62 text

response time throughput

Slide 63

Slide 63 text

// I have 100 queries to run const queries = […] const pLimit = require(‘p-limit’) // Only 10 promises at a time const only10 = pLimit(10) const promises = queries.map(q => { return only10( () => fetchAnalyticsQuery(q) ) }) Promise.all(promises).then(onSuccess)

Slide 64

Slide 64 text

const sleep = require('then-sleep') // Or something a bit simpler while (condition) { await runAnalyticsQuery(...) await sleep(250) }

Slide 65

Slide 65 text

other cool ideas • reducing bursts with jittering: then-sleep, p-defer • priority queues: p-queue • circuit breaking: opossum

Slide 66

Slide 66 text

Promises (and async/await) are simple concurrency primitives with a relatively low barrier of entry

Slide 67

Slide 67 text

but they also have limitations • cancellations • error handling can be tricky • stack traces aren’t perfect

Slide 68

Slide 68 text

Even more stuff! • param validation: https://github.com/nettofarah/ property-validator • testing: https://github.com/nettofarah/axios-vcr

Slide 69

Slide 69 text

for a longer list of promise awesomeness https://github.com/sindresorhus/promise-fun

Slide 70

Slide 70 text

for more sophisticated async programming
 primitives rx.js check out

Slide 71

Slide 71 text

start with the simplest solution you can think of and then grow it from there

Slide 72

Slide 72 text

@nettofarah nettofarah@gmail.com