HomeVideos

Cursor's Github Replacement

Now Playing

Cursor's Github Replacement

Transcript

344 segments

0:00

Hey, did you know that the very first

0:02

Git commit was initial version of Git,

0:04

the information manager from hell? Nice.

0:08

So, recently some news kind of got me

0:10

thinking, why is it so hard to host Git?

0:13

It kind of seems like any old Git could

0:14

host Git. But based on the recent GitHub

0:17

numbers,

0:18

this actually looks really, really

0:19

difficult. But why is it so hard?

0:22

Because like every single engineer that

0:24

has come before me, I always go, "Oh,

0:27

that problem? That doesn't sound that

0:29

hard. I'm pretty sure I could do it."

0:31

Well, actually to finally answer that

0:33

question, Vincent right here did a

0:34

write-up and he's a 10-year veteran at

0:37

GitHub now working at Cursor and he

0:39

breaks down why is it so hard to host

0:43

Git. And everybody seems to love this

0:46

blog post. So, let's all learn together

0:48

why it's so hard to host Git. And by the

0:50

way, a little sneak preview for you, the

0:51

reason why is uh because the way it is.

0:54

It's really hard. Quick disclosure, I

0:56

have done many a live event for Cursor,

0:58

one in which I dressed up as a king and

1:00

they do give me some free tokens, but

1:01

this was not a paid ask of any kind. I

1:04

just think this blog post is super

1:06

interesting and so does everybody else.

1:08

But actually before we begin, quick

1:09

thank you from the sponsor. If you're

1:11

anything like me, you have a bunch of

1:12

ideas, but perhaps you're a bit

1:14

disorganized. So, often I find myself

1:16

having a long brainstorming session, but

1:19

at the end of it, I just have a big

1:20

markdown file and then of course at that

1:22

point I've lost everything. Hey, stop

1:25

doing that. You got to check out Linear

1:27

and use the Linear MCP. I have a big

1:30

conversation, I come up with a bunch of

1:32

ideas and at the end, I get a set of

1:34

tickets that are actually all the things

1:35

that I need to do. And when I don't know

1:37

what to work on, I go, "Hey, agent, go

1:40

to Linear and tell me what I need to do

1:41

next." And it will find exactly what I

1:43

need to work on. This has been so

1:46

successful for me that I'm in actual

1:48

serious danger of completing a project.

1:51

Go to linear.app/terminal

1:53

and get 6 months for free on Linear.

1:55

Thank you for supporting the channel and

1:56

thank you Linear for sponsoring the

1:58

video. All right, so before we begin an

1:59

extremely brief explanation of how Git

2:02

works, every single commit, which is

2:03

represented by some sort of SHA, right,

2:06

is actually just a pointer to a tree.

2:08

The tree is just a pointer to other

2:10

trees at specific versions and other

2:13

blobs. A tree is just literally a

2:16

directory. Your root tree is just the

2:17

root of your project, and a blob is just

2:20

a file. And you can see this by me

2:22

taking my latest commit and saying

2:23

ls-tree. You'll see all the different

2:25

blobs and directories within this

2:28

commit. And I can just grab out one of

2:30

these SHAs and go get show and paste it

2:32

in. It'll actually just show me the

2:33

file. Each one of these things, a

2:35

commit, a tree, a blob, they're called

2:38

objects inside of Git. And if you

2:40

package them all together, it's called a

2:42

packfile. It's like the minimum

2:44

representation of all these different

2:46

objects together. There's no guarantee

2:48

in order, but it's really good to

2:50

understand that to find out what blob

2:53

version is inside of a commit, you have

2:55

to walk everything to figure it out. So,

2:57

to find out what version of a file at a

2:59

commit, you do actually have to walk

3:01

from commit root to subdirectory to the

3:03

specific blob to find out what the

3:04

version is and the contents of that

3:06

blob. And for a Git server to actually

3:09

work, the server itself has to

3:11

communicate to clients using packfiles,

3:14

and clients communicate to a server

3:16

using packfiles. So, when you do get

3:18

fetch or get push or you do get pull,

3:21

it's using packfiles to communicate

3:24

between the server and the client. Now,

3:26

these packfiles are really what makes it

3:28

super difficult to host Git. Now, there

3:31

has been some attempt to not use

3:33

packfiles. One of them is using a KV

3:36

store. I think this immediately makes

3:37

sense because every single one of the

3:39

subtrees, every single one of the

3:41

commits, every single one of the blobs,

3:43

the tags, everything, they all have a

3:45

unique SHA. So, you could imagine you

3:47

could just have a key-value store. Key

3:49

is the SHA, value is the item. Well,

3:51

this animation right here shows you the

3:53

exact problem. If you were to go and

3:55

look up things about a commit, you'd

3:57

have to go back and forth between the

3:59

key value store many many times. And as

4:04

the repository grows, you could go

4:05

thousands of times just to get the

4:07

contents of a commit. In fact, Google

4:09

even tried this approach and tried to

4:12

make it fast, but it turns out it just

4:14

couldn't work out in the end and they

4:15

ultimately abandoned it because Git

4:17

clone was entirely too slow. Now, of

4:20

course, over the years companies have

4:22

tried various versions of being able to

4:24

host Git at scale avoiding this whole

4:27

pack file dilemma, but it just never

4:29

actually worked out. What's actually

4:31

trying to be accomplished is pretty

4:33

simple. Ideally, you want the repository

4:35

to exist on many disks on many machines.

4:37

That way, just one outage doesn't

4:40

accidentally destroy your repository. I

4:41

think this makes perfect sense. Also, if

4:43

you have a whole bunch of reads, you

4:45

would like that to be really really fast

4:47

because if you have a whole bunch of

4:48

reads going to one machine, you could

4:50

imagine somehow the network or the disk

4:52

get saturated and then you just have

4:53

really long wait times, i.e. CI sucks.

4:57

GitHub also tried to kind of do a

4:59

distributed file system, but ultimately

5:01

abandoned it because it again was just

5:03

impossible with pack files. But finally,

5:05

somebody cracked the code and that

5:07

somebody was in fact GitHub. They

5:10

created something called spokes. Spokes

5:12

was originally developed at GitHub

5:13

around 2013 and it has since become the

5:16

industry standard. Most Git hosting

5:18

services use a variant of spokes

5:20

approach application level replication

5:22

for Git repositories in their

5:23

architecture. And spoke doesn't

5:25

distribute Git itself. It works at the

5:27

pack file level. It stores all the data

5:30

as actual Git repositories on local NVMe

5:32

disks. It replicates Git data, but keeps

5:35

all copies consistently in sync. Good.

5:38

So, now we have distributed Git. So, how

5:40

does spokes actually work? So,

5:42

fundamentally how spokes works is it's

5:44

pretty easy. There will be a minimum of

5:46

three machines that contain your

5:48

repository. When you want to read, any

5:51

network request can go to any one of

5:53

these machines, get off the actual

5:55

contents, and bring that down. That is

5:57

because they're all consistently kept in

5:59

sync, which means for every repository

6:01

that you actually have, it needs to have

6:04

a routing table stored in a database

6:06

that says, "Hey, here's where your three

6:08

machines minimum are located." As your

6:11

repository grows and as the reads grow,

6:13

say from CI, you may need more replicas.

6:16

But, writes are a little bit different

6:18

because the first thing that happens is

6:20

we need to send up the actual pack files

6:22

to each one of the replicas. After that,

6:25

we need to say, "Hey, can you actually

6:27

store this? Is this pack file in sync?

6:29

Will everything actually work out?" And

6:30

that's the prepare stage. The next thing

6:32

is called a lock stage. If all the

6:34

things come back or the majority of the

6:36

repos come back saying, "Yes, we can do

6:37

this," then we will then lock all the

6:39

ones that said, "Yes," to come back. And

6:41

then after the lock request, you do a

6:43

commit. The commit will then finalize

6:45

the actual writing of your pack file,

6:47

and boom, your repository is now at a

6:50

new version. This is called a

6:52

three-phase commit. I didn't know about

6:55

in distributed systems. It's called tail

6:57

at scale problem, the dreaded tail at

7:00

scale problem. It's actually pretty easy

7:02

to understand if you just pretend we

7:03

have these five repositories right here.

7:06

Well, first off, we have to send to each

7:08

one of these the pack file. After that,

7:10

we have to say, "Hey, can you actually

7:12

like do this pack file?" After that, we

7:15

have to then set the lock. And then

7:16

after that, we have to commit. And we

7:18

also have to wait for responses each

7:20

time. Now, the problem is is that if you

7:22

make five requests, you're probably

7:24

going to have reasonably good

7:26

performance on all five requests. But,

7:28

every single time you have to make more

7:30

request, there's a higher and higher

7:32

chance you're going to have bad

7:34

performance. Now, the performance is

7:36

actually really easy to calculate. It's

7:38

1 minus the percentile to the amount of

7:41

requests you're going to make, and this

7:44

is the percentage chance that that

7:46

percentile is going to happen. So, now

7:48

we had to make four requests per node

7:50

for a three-phase commit to happen,

7:53

which means if you had five nodes, that

7:55

is 20 commits. That means you have an

7:57

18.2%

7:59

chance to have a P99 slowness on one of

8:03

those routes, which could be hundreds of

8:05

milliseconds, potentially seconds long.

8:07

And this obviously only becomes worse as

8:09

the repository becomes more popular. So,

8:11

let's just take the recent Deep Seek uh

8:13

repository that became the fastest

8:15

starred repo of all time. It probably

8:18

has

8:19

like many many replicants. So, that

8:21

means it could potentially be doing 200

8:23

requests for a three-phase commit, which

8:25

means that 86% of the time you're going

8:28

to be hitting these really bad long

8:31

request paths along your three-phase

8:33

commit. So, it's pretty much guaranteed

8:35

to be slow always. Reads super fast,

8:38

writes super slow. So, that's the

8:40

problem with Spoke. This is the problem

8:41

that makes things difficult to host Git,

8:44

and this is the industry standard. It

8:46

makes reads really easy, which is great

8:48

for CI, it's great for a lot of cases,

8:50

but it makes write increasingly hard as

8:53

repositories are more popular. All

8:55

right, so how do we solve the scale

8:57

problem? We've identified that hosting

8:59

Git is hard, right? But how how are we

9:01

going to solve it? Well, it seems like

9:03

this is a reoccurring theme in the

9:05

things I've seen. The answer, S3. Well,

9:08

it's actually more complicated than just

9:10

S3. It uses something called a wall. And

9:12

when I say a wall, of course, I don't

9:13

mean the crazy things that your uncle

9:15

says on Facebook. Not that wall. What

9:17

I'm actually saying is a write-ahead

9:19

log. A write-ahead log is simply just

9:22

all the write operations that have

9:23

happened, so you can reconstruct Git

9:26

from that series of writes. Postgres

9:29

uses this, SQLite uses this. So, by

9:31

using a write-ahead log, you can imagine

9:34

that all All these writes, all these

9:36

pack files are being stored in this log,

9:37

they're being stored in linear order,

9:39

and it's actually super easy to append

9:40

to because it's just like a bump

9:42

allocator, right? And for a server to

9:43

come up and be able to go, "Okay, well,

9:45

what's my Git state?" It just simply

9:47

needs to read the right log. And boom,

9:50

that's its Git state. So, does that mean

9:52

there's no consensus? Yeah, there's no

9:54

more consensus. We use S3. Well, which

9:56

one's the primary node? It doesn't

9:58

matter. Any node can serve it out. Now,

10:01

obviously, you don't just want to serve

10:03

the same repository from random

10:04

different nodes because that'd be super

10:06

inefficient cuz each one would have to

10:07

restore itself from the wall. So, they

10:09

use something called rendezvous hashing.

10:11

Now, rendezvous hashing, by the way,

10:13

very French. Comment ?

10:16

Je m'appelle Michel. Is a extremely

10:19

simple algorithm to allow two parties

10:22

that don't know about each other to

10:23

where they need to meet or where they

10:25

need to rendezvous. Effectively, you

10:27

give it like a unique string, and it

10:29

will give you out servers plus a score,

10:31

and whatever is the highest score is the

10:34

server you take. Pretty straightforward.

10:36

And this is Continuity. It effectively

10:37

uses a wall on S3 to be able to store

10:40

the state of Git and then to be able to

10:41

restore it at any point on any machine.

10:43

There's no more needing for kind of

10:45

reconciliation between two different

10:47

nodes to make sure everything keeps in

10:48

sync. No longer need to vote, "Hey, can

10:50

this right actually happen?" You don't

10:52

have to have consensus. You don't have

10:54

to have a whole bunch of replicas laying

10:56

around. That means the 25 million

10:58

repositories that are created every

11:00

single month don't actually have to have

11:03

three machines each. They could only

11:04

have one. They could have zero. Nothing

11:07

could exist because they don't need to

11:08

exist because the actual state of the

11:10

Git repository is up on S3 and can be

11:13

restored in a matter of milliseconds.

11:15

You really should go read the blog for

11:17

all the technical details. It is super

11:19

super duper awesome. It even goes into

11:22

like how they keep the wall log actually

11:24

pretty tight because they have to do

11:26

compaction. Also, I didn't even know

11:27

there was something called compaction in

11:29

Git. It effectively takes a bunch of

11:31

pack files, make a big pack file. And

11:33

with super duper fast NVMe storage and

11:35

S3, they can do 16,000 commits per

11:39

minute. That That's That's That's a lot

11:41

of commits. Learning about the

11:42

write-ahead log and pack files and stuff

11:44

like that. This was a really amazing

11:47

article. I had such a good time. I just

11:52

I I I feel smarter and I also feel much

11:55

more confident. Dude, I can totally do

11:58

>> [laughter]

11:58

>> this.

11:59

I can do this. I You know

12:01

what? You know what? I feel like I can

12:03

make that. Truly the problem of

12:06

confidence.

12:07

Feel like you can do anything in Gen.

Interactive Summary

This video explores the significant technical challenges involved in hosting Git repositories at scale, specifically focusing on the limitations of the industry-standard 'Spokes' approach. It explains why Git's use of 'packfiles' makes distributed systems complex, detailing the performance bottlenecks caused by three-phase commits and the 'tail at scale' problem. Finally, the video introduces a more modern solution utilizing a Write-Ahead Log (WAL) and object storage like S3, which eliminates the need for complex consensus protocols and enables faster, more scalable repository management.

Suggested questions

2 ready-made prompts