1package git
2
3import (
4	"context"
5	"os"
6	"strconv"
7	"strings"
8
9	"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
10	grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
11	"github.com/prometheus/client_golang/prometheus"
12	"github.com/prometheus/client_golang/prometheus/promauto"
13	"gitlab.com/gitlab-org/gitaly/v14/internal/git/packfile"
14	"gitlab.com/gitlab-org/gitaly/v14/internal/storage"
15)
16
17var badBitmapRequestCount = promauto.NewCounterVec(
18	prometheus.CounterOpts{
19		Name: "gitaly_bad_bitmap_request_total",
20		Help: "RPC calls during which there was not exactly 1 packfile bitmap",
21	},
22	[]string{"method", "bitmaps"},
23)
24
25// WarnIfTooManyBitmaps checks for too many (more than one) bitmaps in
26// repoPath, and if it finds any, it logs a warning. This is to help us
27// investigate https://gitlab.com/gitlab-org/gitaly/issues/1728.
28func WarnIfTooManyBitmaps(ctx context.Context, locator storage.Locator, storageName, repoPath string) {
29	logEntry := ctxlogrus.Extract(ctx)
30
31	storageRoot, err := locator.GetStorageByName(storageName)
32	if err != nil {
33		logEntry.WithError(err).Info("bitmap check failed")
34		return
35	}
36
37	objdirs, err := ObjectDirectories(ctx, storageRoot, repoPath)
38	if err != nil {
39		logEntry.WithError(err).Info("bitmap check failed")
40		return
41	}
42
43	var bitmapCount, packCount int
44	seen := make(map[string]bool)
45	for _, dir := range objdirs {
46		if seen[dir] {
47			continue
48		}
49		seen[dir] = true
50
51		packs, err := packfile.List(dir)
52		if err != nil {
53			logEntry.WithError(err).Info("bitmap check failed")
54			return
55		}
56		packCount += len(packs)
57
58		for _, p := range packs {
59			fi, err := os.Stat(strings.TrimSuffix(p, ".pack") + ".bitmap")
60			if err == nil && !fi.IsDir() {
61				bitmapCount++
62			}
63		}
64	}
65
66	if bitmapCount == 1 {
67		// Exactly one bitmap: this is how things should be.
68		return
69	}
70
71	if packCount == 0 {
72		// If there are no packfiles we don't expect bitmaps nor do we care about
73		// them.
74		return
75	}
76
77	if bitmapCount > 1 {
78		logEntry.WithField("bitmaps", bitmapCount).Warn("found more than one packfile bitmap in repository")
79	}
80
81	// The case where bitmapCount == 0 is likely to occur early in the life of a
82	// repository. We don't want to spam our logs with that, so we count but
83	// not log it.
84
85	grpcMethod, ok := grpc_ctxtags.Extract(ctx).Values()["grpc.request.fullMethod"].(string)
86	if !ok {
87		return
88	}
89
90	badBitmapRequestCount.WithLabelValues(grpcMethod, strconv.Itoa(bitmapCount)).Inc()
91}
92