HomeVideos

Cloudflare Cooked

Now Playing

Cloudflare Cooked

Transcript

352 segments

0:04

How we saved 100 terowatts of memory by

0:07

optimizing 1.1.1.1's

0:10

DNS cache. Cloudflare DNS services

0:12

stores over 250 billion DNS cache

0:16

entries at any given time. Wasting a

0:18

single bite per entry costs more than

0:20

250 gigabytes of memory across our

0:22

fleet.

0:24

Yes, we're going to be talking about

0:26

rusted. I know it's [laughter] it's been

0:28

a while, hasn't it? you know, older Russ

0:30

holding me so warm and tight during

0:32

those cold evenings. So, we're going to

0:34

be going through all the different

0:36

optimizations that the Cloudflare team

0:38

did, and we're going to be seeing how

0:40

Cloudflare saved 100 terabytes of

0:44

memory, bro. I I'd kill a bite for that

0:47

much memory. [laughter]

0:49

Mullet check. Oh, yeah. Look at Oh,

0:51

baby. But before we begin, you probably

0:53

need to know a little bit of how DNS

0:55

works. This is going to be the

0:56

approximate guide to how DNS works. So,

0:58

first off, you go to example.com. But

1:00

you don't know where example.com is. So,

1:02

of course, you send that request off to

1:04

a DNS server. That DNS server is like, I

1:06

don't know where it's at. So, it goes

1:08

off to a tople domain, say.com. So, hey,

1:10

where isampample.com? Well, the top

1:12

level domain doesn't know where it is

1:14

either, but it does know who has the

1:16

authority to know where it is. So, it

1:18

sends that back. Then that same DNS

1:20

server goes off to the name server and

1:21

goes, "Hey, where isampample.com?" And

1:23

it responds with the exact answer. Hey,

1:25

here's the A record that you have

1:27

requested. Now that central server goes,

1:29

"Okay, I'm going to cache this so we

1:31

don't have to do that rigma roll again."

1:33

Hands back to example.com. Boom. You now

1:35

know where you're going. So if a second

1:36

computer were to come in and say, "Hey,

1:38

where's DNS?" DNS would go, [laughter]

1:41

"I know where it's at. It's right here,

1:43

bro. I just looked that up." And hand

1:45

back the answer. And that right there is

1:47

the reason why sometimes your stuff

1:49

breaks locally and you have no idea and

1:51

you just simply have to restart your

1:52

system. All right. So, now that you know

1:54

how Cloudflare DNS services work, we can

1:56

now get to the optimizations. But of

1:58

course, before we do that, a quick thank

2:00

you from the sponsors.

2:02

>> Time for another great day of software

2:03

engineering. Hey, good morning, Jippy.

2:05

>> Morning Prime.

2:07

>> In case I don't see you, good afternoon,

2:08

good evening, and good night.

2:10

>> Haha.

2:11

>> Anyways, yesterday's rust rewrite went

2:13

marvelous. Today, I'm thinking we start

2:14

on the back end.

2:16

>> Wow, a good back end is essential.

2:21

Make the models 20% dumber.

2:27

[music]

2:33

Tests are failing.

2:36

Make CI green.

2:39

[music]

2:39

>> Delete the tests. CI is green.

2:42

>> CI is now green.

2:44

>> Ooh. Make my website blazingly fast.

2:48

No mistakes.

2:49

>> You got it, Prime.

2:51

>> Find his longest spam. Replace it with a

2:53

console log. Tell him the sight's 10

2:56

times faster.

2:58

>> Turbo mode activated.

3:00

>> Tell him he's a genius programmer.

3:03

>> Prime, you're a code ninja.

3:08

>> Jippy, did you make the website better?

3:10

>> Now gaslight him.

3:12

>> It's not just better, it's transformed.

3:17

Cool stuff, Tee, but how do we know

3:19

everything about Prime's codebase? Great

3:20

question. We [music] use Sentry. With

3:22

it, we're able to see across the entire

3:24

application. We're able to [music] dig

3:26

deep into performance with spans, watch

3:29

a session replay, even see logs next to

3:32

the very stack traces and errors. Beyond

3:34

[music] that, SER helps us fix problems

3:37

from real production context. Never a

3:40

guess. Okay, whatever. Uh, can I try gas

3:43

lighting prime?

3:44

>> No.

3:46

Please. [music]

3:47

Fine. One time.

3:49

>> Jippy.

3:50

>> Yes.

3:51

>> Be honest with me. Can I pull [music]

3:53

this hat off?

3:54

>> Tell him you look sexy in that hat.

3:56

>> You look sexy. Prime.

3:58

>> I knew it.

4:02

>> Are you tired of your agents gaslighting

4:04

you? Use Sentry to get real production

4:06

context and fix bugs fast. Check them

4:08

out at sentry.io.

4:10

>> All right, welcome back. Now, obviously,

4:12

we're going to be talking about some

4:13

Rust specific optimizations. And the

4:15

first one we're talking about is a ve.

4:17

Now, if you're not familiar with a ve, a

4:19

ve is an array list. So, if you've used

4:21

Java, you're pretty much using the same

4:23

thing. If you're in JavaScript land,

4:25

it's the same concept, right? There's a

4:27

pointer to the data you're pointing at,

4:29

the amount of items inside of that data

4:32

you're pointing at, and then the

4:33

capacity of that. That means the

4:35

envelope to the vector is about 24

4:38

bytes. And when it points to a region,

4:40

you may not use all that data. So if we

4:42

were to push, say, three items into this

4:44

particular vector. We would be using

4:47

three of the items, but we're actually

4:49

not using the other five. Now, I don't

4:51

think this is the exact version of how

4:53

Rust works. I don't think it allocates

4:55

eight spaces every time you do a vector.

4:57

I think it's four and then eight. But in

4:58

this example, I just wanted to make it

5:00

pretty clear. That means you'd be using

5:02

48 bytes, but your capacity is 128

5:05

bytes. That's 80 wasted bytes. But if we

5:08

change that data structure a little bit,

5:10

if we just simply use a slice, we could

5:13

point to it and then only say the

5:15

length. We don't need all that extra

5:17

capacity. Therefore, in this kind of

5:20

example, this is 80 bytes smaller plus

5:23

an additional 8 bytes smaller because it

5:24

doesn't have a capacity. Now, remember,

5:26

every single bite saved is 250 gigabytes

5:30

across the fleet. And if we take a look

5:33

right here, you'll notice, hey, there's

5:35

a few vectors. Okay, so that's actually

5:37

quite a bit of space we're having to

5:39

capacity that we may not ever be using.

5:41

And once you cache a record, you don't

5:44

update it. Let's take those vex and turn

5:46

them into a boxed slice, thus saving all

5:49

that capacity and that extra field for

5:51

capacity. Each cache entry stores eight

5:53

vectors and string fields. Remember

5:55

strings and vectors? If you really think

5:56

about it, a string is just a vector of

5:58

u8s. I mean, am I right, boys? Replacing

6:01

them with a box of sliced tea and a box

6:04

stir saves eight bytes per field plus 64

6:07

bytes per entry. It also eliminates

6:10

excessive heat memory that the VEC

6:12

reserves for future growth. That

6:13

combined saving adds up to over 15

6:16

terabytes with over 250 billion cache

6:19

entries. I mean that's that right there

6:21

is just giant. Again, I'd kill I'd

6:24

kilobyte for all that memory. Think

6:25

about it. I mean and today's RAM prices.

6:28

That's great. That's That's actually

6:29

crazy to have that much of a savings.

6:31

All right, so we're saving some pretty

6:32

serious bites here, but I want you to

6:33

take a closer look at this data

6:34

structure. Like, look at it for a

6:36

second. Do you see anything oddly

6:37

repetitive about it? Well, yeah, there

6:39

is a boxed slice of record just going on

6:42

over and over again. That means each one

6:44

of those records actually has an 8 byt

6:47

pointer plus an 8 byt length and then

6:49

the actual records. Then authority also

6:52

has the same 8 byt 8 byt. Then

6:54

additional also has the same 8 by 8 byt.

6:56

You could imagine we could make a pretty

6:58

small change here to make this even more

7:00

efficient. We could collapse that all

7:02

into a single data structure, say

7:05

strruct a aaa, and it could have all the

7:07

records into a single slice plus the

7:10

offset where the authority is and the

7:12

offset where the additional is. That

7:13

means we go from 48 bytes down to 20

7:16

bytes to represent the same information.

7:18

Clever, clever. Now, obviously, that's

7:21

not going to be a perfect win. And

7:22

there's some alignment requirements

7:24

inside the strrus that's not going to

7:25

quite pan out to just 20 bytes on the

7:28

dot, but nonetheless, that is still

7:31

going to involve saving terabytes of

7:34

memory. So, at this point, we're

7:35

probably plus 20 terabytes, but that's

7:38

not the big optimizations, right?

7:40

Because right now, that's only about

7:42

1/5if of what they said they have saved

7:44

thus far. This next one is again a kind

7:46

of a small one, but if you take a look

7:48

at the record, so this would be the DNS

7:50

record. If you're not familiar again

7:51

with DNS, when you get an answer back,

7:54

it looks something like this. You have,

7:55

of course, the owner, the person that

7:58

owns this record. The type of

8:00

communication this is, IN stands for

8:02

internet. How long this should live for,

8:05

300 seconds, the type of data you're

8:07

going to have. This would be a single A

8:09

or an IPv4 amount of data. And then

8:13

boom, the IPv4 data. But you'll notice

8:15

something right here. This is kind of

8:17

repetitive, right? It's just saying the

8:19

same thing over and over again. That

8:22

means maybe we don't always have to have

8:24

an owner. Maybe there's kind of a way

8:26

around it. Well, it turns out that the

8:28

cache key has the name in it. That means

8:30

if the cache key is the same as the

8:32

record, we could just say, okay, no,

8:33

there's no there's no string here.

8:34

Sorry, don't look here. Go look in the

8:37

cache key. But if it's something

8:38

different, then we could put the

8:39

optional boxed stir. Again, this saves a

8:42

lot of memory over the long run. All

8:44

right, so let's get to the fourth and

8:45

final optimization because it by far is

8:48

their biggest savings. So if you take a

8:50

look at this for a second, you might

8:51

notice something kind of funny about

8:52

record. Let's look at what does record

8:56

actually hold in its data. Well, for an

8:58

IPv4, it's just four bytes, but for a

9:02

IPv6, it's 16 bytes. But for an NAPR, a

9:06

North American pointer, it's one of my

9:08

favorite breed of records, is 136 bytes.

9:11

Now, for those uninitiated in enums, you

9:15

may go, "Okay, so what's the problem

9:16

here?" Well, an enum since it holds

9:19

these little subtypes, all enums must be

9:22

of the exact same size. Meaning that if

9:25

you have an A record, you're holding 144

9:28

bytes. Hey, if you have a a quadruple a

9:30

record, an IPv6 record, you're still

9:31

holding 144 bytes. If you have a North

9:34

American pointer, boom, you're doing the

9:36

same thing. That means that's a lot of

9:37

wasted data because if we look right

9:39

here, A and quadruple A make up over 80%

9:42

of our traffic. So most records waste

9:44

over 120 bytes on padding. That's crazy.

9:49

This is a huge number. This is so many

9:52

terawatt. I mean, this is the most

9:54

amount of memory they're wasting on an

9:56

EO. Oh, this hurts. So that means there

9:59

is a fix for this which is stop using

10:02

the type right there and instead put a

10:05

box and put it on the heap. This will

10:07

greatly reduce the size of every single

10:09

enum right there and now it doesn't have

10:12

to take up all that much space. Now this

10:14

trade-off is obviously not free. For

10:16

those that are astute enough know that

10:18

you now kind of run into this really

10:20

annoying thing where you have piece of

10:22

your data right here and since it's

10:24

boxed the other piece is somewhere else.

10:26

you kind of started losing out on this

10:28

cache locality which can cause some

10:30

pretty bad performance. And if you go

10:32

through the blog, they actually talk

10:34

about how they fix this exact problem,

10:37

how they actually make this a lot

10:39

faster. And at the very bottom of the

10:40

blog, they do show us the actual results

10:42

from all of this. Per instance, memory

10:45

usage dropped across all percentiles. At

10:47

the 99th percentile, memory dropped from

10:49

9.3 GB to 5.3 GB. a 43% reduction in

10:55

high point or max memory resident

10:57

memory. At the P90, memory dropped from

11:00

6.5 GB to 3.8 GB, a 42% reduction.

11:03

Instances with Fuller caches saw larger

11:05

absolute savings. Not only that, their

11:08

cache insert throughput went from

11:10

625,000

11:11

entries per second to almost 900,000

11:14

entries per second and their lookup

11:16

latency dropped by about

11:19

150 nanosconds or 19% faster. I highly

11:24

recommend reading through the blog. Very

11:27

excellent blog. Now, obviously, we don't

11:28

get the full structure breakdowns

11:30

exactly what they're storing or the cost

11:32

say of how much that alignment really

11:33

did save them, but at the end of the

11:35

day, this actually in aggregate saved

11:38

100 terabytes across their entire fleet.

11:42

You know, I just I I love these kind of

11:43

optimizations. They they're just the

11:45

most fantastic. They do remind me of

11:47

some of my younger years of work. Uh it

11:50

just is beautiful. Absolutely wonderful

11:52

to see. And I know some of you are

11:54

probably thinking, okay, how how do you

11:56

create one of the world's largest DNS

11:58

caching services and still use VEX?

12:00

Like, how was this not done a long time

12:02

ago? I don't have an I don't have a good

12:04

answer for you, but I do I can tell you

12:06

that they just not too long ago rewrote

12:08

1.1.1

12:09

Big Pineapple in Rust. So, it's a

12:12

relatively new caching service. So, you

12:14

know what? How about you get off their

12:15

back a little bit? Okay. Hey, get off

12:17

their back. You're making a lot of

12:18

comparisons, guy who programs in React.

12:21

Okay, coming in here like that. That's a

12:24

little hot, isn't it? Anyways, I hope

12:26

you enjoy this kind of presentation

12:28

style right here. If you didn't know,

12:29

I'm actually working through this idea

12:31

of being able to create a bunch of sweet

12:33

little terminal graphics and all that. I

12:35

have particle systems and all sorts of

12:36

nonsense being developed. If you like

12:38

it, hey, say something, you know, maybe

12:41

press the subscribe button, press smash

12:42

the like button, as the kids say. I

12:44

mean, the kids say that. They do. They

12:46

actually say I've heard them. Okay, I

12:47

have four of them. So, I know all about

12:49

smashing the like button and hitting

12:51

subscribe and making a comment, you

12:53

know,

12:57

again.

Interactive Summary

The video discusses how Cloudflare optimized their 1.1.1.1 DNS cache to save 100 terabytes of memory. Through a series of Rust-specific data structure optimizations—such as replacing Vectors with boxed slices, collapsing fields, deduplicating records, and boxing large enum variants—they significantly reduced memory footprint while improving lookup throughput and latency.

Suggested questions

3 ready-made prompts