Slide 1

Slide 1 text

High performance git infrastructure with Go David Calavera Code Climate

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

2005

Slide 4

Slide 4 text

(╯°□°)╯︵ ┻━┻

Slide 5

Slide 5 text

2008

Slide 6

Slide 6 text

2010…

Slide 7

Slide 7 text

How do we make git faster?

Slide 8

Slide 8 text

* system(“git log …”) * Ruby for brevity

Slide 9

Slide 9 text

git fetch origin

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

github.com/libgit2/git2go Disclaimer: examples do not handle errors

Slide 12

Slide 12 text

// Load with side effects. // Initialize libgit2’s TLS: // // func init() { // C.git_libgit2_init() // } // // Import package “git”, which is // not very goimports friendly. import “github.com/libgit2/git2go”

Slide 13

Slide 13 text

// Create a new repository. // Do not use a working directory. path := “/var/git/repository” bare := true r, _ := git.InitRepository(path, bare) ! // Clone a repository. url := “git://github.com/golang/go” opts := git.CloneOptions{Bare: bare} r, _ := git.Clone(url, path, &opts)

Slide 14

Slide 14 text

// Create a new remote ref. name := “my-fork” url := “git://github.com/wadus/go” rm, _ := r.CreateRemote(name, url) ! // Fetch all refs from a remote. var refspecs []string rm.Fetch(refspecs, nil, nil)

Slide 15

Slide 15 text

// Search for objects. sha := “4c279186e24f7b3a59aa682a870747df6eaca013” oid := git.NewOid(sha) ! c, _ := r.LookupCommit(oid) b, _ := r.LookupBlob(oid) t, _ := r.LookupTree(oid) ! o, _ := r.Lookup(oid) fmt.Printf(“ %v\n”, o.Type())

Slide 16

Slide 16 text

// Read commit data. sha := “4c279186e24f7b3a59aa682a870747df6eaca013” oid := git.NewOid(sha) path := “src/os/exec.go” ! c, _ := r.LookupCommit(oid) t, _ := c.Tree() e, _ := t.EntryByPath(path) ! b, _ := r.LookupBlob(e.Id()) fmt.Printf(“ %q\n”, b.Contents())

Slide 17

Slide 17 text

// Commit new changes. idx, _ := r.Index() idx.AddByPath(“src/os/exec.go”) t, _ := idx.WriteTree() idx.Write() ! h, _ := r.Head() c, _ := r.LookupCommit(h) ! s := &git.Signature{“me”, “[email protected]”, time.Now()} m := “Add moar changes” r.CreateCommit(“”, s, s, m, t, c)

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

Designing a distributed git storage

Slide 21

Slide 21 text

Application Git Service

Slide 22

Slide 22 text

Constraint your data model

Slide 23

Slide 23 text

// protocol buffers schema. message Branch { required string name = 1; } ! message Repository { optional string name = 1; repeated Branch branches = 2; }

Slide 24

Slide 24 text

// Read branches. var branches []*pb.Branch ! f := func(b *git.Branch, t git.BranchType) error { n, _ := b.Name() p := &pb.Branch{ Name: &n, } branches = append(branches, p) return nil } ! b, _ := r.NewBranchIterator(git.BranchRemote) b.ForEach(f)

Slide 25

Slide 25 text

// Read branches via http. h := func(w http.ResponseWriter, r *http.Request) { pbBranches := readBranches(r) pbRepo := &pb.Repository{ Branches: pbBranches, } ! data, _ := proto.Marshal(pbRepo) w.Write(data) } ! http.HandleFunc(“/r/foo/branches”, h)

Slide 26

Slide 26 text

Design from first principles

Slide 27

Slide 27 text

A shared-data system can have at most two of the three following properties: Consistency, Availability, and tolerance to network Partitions Dr. Eric Brewer

Slide 28

Slide 28 text

You Can’t Sacrifice Partition Tolerance Coda Hale

Slide 29

Slide 29 text

github.com/afex/hystrix-go" github.com/rubyist/circuitbreaker" github.com/eapache/go-resiliency/braker

Slide 30

Slide 30 text

// Read branches via http. import “github.com/rubyist/circuitbreaker” ! out := 5 * time.Second url := “http://git-server/r/foo/branches” c := circuit.NewHTTPClient(out, 10, nil) ! c.BreakerTripped = func() { // Handle partition error response. } ! resp, _ := c.Get(url)

Slide 31

Slide 31 text

Replication Consistency Vs Availability

Slide 32

Slide 32 text

Application Git Service Git Service Primary Replica

Slide 33

Slide 33 text

Application Git Service Git Service Primary Replica Fetch!

Slide 34

Slide 34 text

Application Git Service Git Service Primary Replica Fetch!

Slide 35

Slide 35 text

Application Git Service Git Service Primary Replica Done!

Slide 36

Slide 36 text

Application Git Service Git Service Primary Replica Done!

Slide 37

Slide 37 text

// Handle fetch requests. h := func(w http.ResponseWriter, r *http.Request) { peerChannel := replicateRequest(r) rm, _ := repo.LoadRemote(“origin”) var refspecs []string rm.Fetch(refspecs, nil, nil) ! if peersChannel != nil { waitForPeers(peerChannel) } w.WriteHeader(201) } ! http.HandleFunc(“/r/foo/fetch”, h)

Slide 38

Slide 38 text

// Replicate request. func replicateRequest(r *http.Request) chan int { if req.Header.Get(“X-GIT-REPLICATE”) != “” { return nil } peerChannel := make(chan int) replicaURL, err := url.Parse(replicaHost) replicaURL.Path = r.Path replicaURL.Header.Set(“X-GIT-REPLICATE”, “true") req, _ := http.NewRequest(“POST”, replicaURL.String(), nil) ! go func() { resp, _ := httpClient.Do(req) peerChannel <- resp.StatusCode }() return peerChannel }

Slide 39

Slide 39 text

// Wait for replica response. func waitForPeers(channel chan int) error { replicaStatus := <- channel switch replicaStatus { case 201: // default: // ☔️ ☔️ } ! return nil }

Slide 40

Slide 40 text

git architectures are fun

Slide 41

Slide 41 text

Thank you! @calavera