Welcome to mirror list, hosted at ThFree Co, Russian Federation.

packfile_negotiation.go « stats « git « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 7a14a5726074c78a8516c2df68d4920568d18350 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package stats

import (
	"context"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"strings"

	"github.com/prometheus/client_golang/prometheus"
	"gitlab.com/gitlab-org/gitaly/internal/command"
	"gitlab.com/gitlab-org/gitaly/internal/git/pktline"
	"gitlab.com/gitlab-org/gitaly/internal/helper/text"
)

type PackfileNegotiation struct {
	// Total size of all pktlines' data
	PayloadSize int64
	// Total number of packets
	Packets int
	// Capabilities announced by the client
	Caps []string
	// Wants is the number of objects the client announced it wants
	Wants int
	// Haves is the number of objects the client announced it has
	Haves int
	// Shallows is the number of shallow boundaries announced by the client
	Shallows int
	// Deepen-filter. One of "deepen <depth>", "deepen-since <timestamp>", "deepen-not <ref>".
	Deepen string
	// Filter-spec specified by the client.
	Filter string
}

func ParsePackfileNegotiation(body io.Reader) (PackfileNegotiation, error) {
	n := PackfileNegotiation{}
	return n, n.Parse(body)
}

// Expected Format:
// want <OID> <capabilities\n
// [want <OID>...]
// [shallow <OID>]
// [deepen <depth>|deepen-since <timestamp>|deepen-not <ref>]
// [filter <filter-spec>]
// flush
// have <OID>
// flush|done
func (n *PackfileNegotiation) Parse(body io.Reader) error {
	defer io.Copy(ioutil.Discard, body)

	scanner := pktline.NewScanner(body)

	for ; scanner.Scan(); n.Packets++ {
		pkt := scanner.Bytes()
		data := text.ChompBytes(pktline.Data(pkt))
		split := strings.Split(data, " ")
		n.PayloadSize += int64(len(data))

		switch split[0] {
		case "want":
			if len(split) < 2 {
				return fmt.Errorf("invalid 'want' for packet %d: %v", n.Packets, data)
			}
			if len(split) > 2 && n.Caps != nil {
				return fmt.Errorf("capabilities announced multiple times in packet %d: %v", n.Packets, data)
			}

			n.Wants++
			if len(split) > 2 {
				n.Caps = split[2:]
			}
		case "shallow":
			if len(split) != 2 {
				return fmt.Errorf("invalid 'shallow' for packet %d: %v", n.Packets, data)
			}
			n.Shallows++
		case "deepen", "deepen-since", "deepen-not":
			if len(split) != 2 {
				return fmt.Errorf("invalid 'deepen' for packet %d: %v", n.Packets, data)
			}
			n.Deepen = data
		case "filter":
			if len(split) != 2 {
				return fmt.Errorf("invalid 'filter' for packet %d: %v", n.Packets, data)
			}
			n.Filter = split[1]
		case "have":
			if len(split) != 2 {
				return fmt.Errorf("invalid 'have' for packet %d: %v", n.Packets, data)
			}
			n.Haves++
		case "done":
			break
		}
	}

	if scanner.Err() != nil {
		return scanner.Err()
	}
	if n.Wants == 0 {
		return errors.New("no 'want' sent by client")
	}

	return nil
}

// UpdateMetrics updates Prometheus counters with features that have been used
// during a packfile negotiation.
func (n *PackfileNegotiation) UpdateMetrics(ctx context.Context, metrics *prometheus.CounterVec) {
	if n.Deepen != "" {
		metrics.WithLabelValues("deepen").Inc()
	}
	if n.Filter != "" {
		metrics.WithLabelValues("filter").Inc()
	}
	if n.Haves > 0 {
		metrics.WithLabelValues("have").Inc()
	}
	metrics.WithLabelValues("total").Inc()

	stats := command.StatsFromContext(ctx)
	if n.Deepen != "" {
		stats.RecordSum("packfile_negotiation.deepen", 1)
	}
	if n.Filter != "" {
		stats.RecordSum("packfile_negotiation.filter", 1)
	}
	stats.RecordSum("packfile_negotiation.haves", n.Haves)
}