Metrics for measuring monorepo performance

The following metrics can be used to measure server side performance of your monorepo. These metrics are not limited to monorepo performance and are more general metrics to measure Gitaly performance, but they are especially relevant when running a monorepo.

Clones and Fetches

The most frequent expensive operation are clones and fetches. When taken as a percentage of system resources consumed, these operations often contribute to 90% or more of system resources on Gitaly nodes. Here are some logs and metrics that can provide useful signals.

CPU and Memory

There are two main RPCs that handle clones/fetches. The following log entry fields an be used to inspect how much system resources are consumed by clones/fetches for a given repository.

The following are log entry fields in the Gitaly logs that can be filtered on:

Log field Values to filter on Why?
json.grpc.method PostReceivePack This is the RPC that handles HTTP clones/fetches
json.grpc.method SSHReceivePack This is the RPC that handles SSH clones/fetches
json.grpc.code OK Indicates the RPC has successfully served its request
json.grpc.code Canceled Often times indicates the client killed the connection, usually due to a timeout of some sort
json.grpc.code ResourceExhausted Indicates there are too many Git processes being spawned on the machine simultaneously
json.user_id A user_id who initiated the clone/fetch. This is in the form of user-<user_id> eg: user-22345 Indicates there are too many Git processes being spawned on the machine simultaneously
json.username A username who initiated the clone/fetch. eg: ilovecoding In order to see how many clones/fetches were from a given user. This is sometimes helpful to find excessive clone operations by a single user
json.grpc.request.glRepository A repository in question. In the form of project-<project_id> eg: project-214 In order to see how many clones/fetches were for a given repository.
json.grpc.request.glProjectPath A repository in question. In the form of a project path eg: my-org/coolproject In order to see how many clones/fetches were for a given repository.

The following are log entry fields that give useful information about cpu and memory:

Log field to inspect What does it tell you?
json.command.cpu_time_ms How much CPU time used by subprocesses this RPC spawned
json.command.maxrss How much memory was consumed from subprocesses this RPC spawned

Example log message:

{
    "command.count":2,
    "command.cpu_time_ms":420,
    "command.inblock":0,
    "command.majflt":0,
    "command.maxrss":3342152,
    "command.minflt":24316,
    "command.oublock":56,
    "command.real_time_ms":626,
    "command.spawn_token_fork_ms":4,
    "command.spawn_token_wait_ms":0,
    "command.system_time_ms":172,
    "command.user_time_ms":248,
    "component":"gitaly.StreamServerInterceptor",
    "correlation_id":"20HCB3DAEPLV08UGNIYT9HJ4JW",
    "environment":"gprd",
    "feature_flags":"",
    "fqdn":"file-99-stor-gprd.c.gitlab-production.internal",
    "grpc.code":"OK",
    "grpc.meta.auth_version":"v2",
    "grpc.meta.client_name":"gitlab-workhorse",
    "grpc.meta.deadline_type":"none",
    "grpc.meta.method_operation":"mutator",
    "grpc.meta.method_scope":"repository",
    "grpc.meta.method_type":"bidi_stream",
    "grpc.method":"PostReceivePack",
    "grpc.request.fullMethod":"/gitaly.SmartHTTPService/PostReceivePack",
    "grpc.request.glProjectPath":"r2414/revenir/development/machinelearning/protein-ddg",
    "grpc.request.glRepository":"project-47506374",
    "grpc.request.payload_bytes":911,
    "grpc.request.repoPath":"@hashed/db/ab/dbabf83f57affedc9a001dc6c6f6b47bb431bd47d7254edd1daf24f0c38793a9.git",
    "grpc.request.repoStorage":"nfs-file99",
    "grpc.response.payload_bytes":54
    "grpc.service":"gitaly.SmartHTTPService",
    "grpc.start_time":"2023-10-16T20:40:08.836",
    "grpc.time_ms":631.486,
    "hostname":"file-99-stor-gprd",
    "level":"info",
    "msg":"finished streaming call with code OK",
    "pid":1741362,
    "remote_ip":"108.163.136.48",
    "shard":"default",
    "span.kind":"server",
    "stage":"main",
    "system":"grpc",
    "tag":"gitaly",
    "tier":"stor",
    "time":"2023-10-16T20:40:09.467Z",
    "trace.traceid":"AAB3QAeD8G+H9VNmzOi2CztMAcJv1+g4+l1cAgA=",
    "type":"gitaly",
    "user_id":"user-14857500",
    "username":"ctx_ckottke",
  }

Read distribution

The gitaly_praefect_read_distribution Prometheus metric is a counter that indicates how many reads have gone to which Gitaly nodes. This metric has two vectors:

Metric Name Vector What is it?
gitaly_praefect_read_distribution virtual_storage The virtual storage name
gitaly_praefect_read_distribution storage The Gitaly storage name

Pack objects cache

The pack objects cache can be observed through both logs as well as Prometheus metrics.

Log field name Description
pack_objects_cache.hit Indicates whether the current pack-objects cache was hit (true or false)
pack_objects_cache.key Cache key used for the pack-objects cache
pack_objects_cache.generated_bytes Size (in bytes) of the new cache being written
pack_objects_cache.served_bytes Size (in bytes) of the cache being served
pack_objects.compression_statistics Statistics regarding pack-objects generation
pack_objects.enumerate_objects_ms Total time (in ms) spent enumerating objects sent by clients
pack_objects.prepare_pack_ms Total time (in ms) spent preparing the packfile before sending it back to the client
pack_objects.write_pack_file_ms Total time (in ms) spent sending back the packfile to the client. Highly dependent on the client’s internet connection
pack_objects.written_object_count Total number of objects Gitaly sends back to the client

Example log message:

{
"bytes":26186490,
"correlation_id":"01F1MY8JXC3FZN14JBG1H42G9F",
"grpc.meta.deadline_type":"none",
"grpc.method":"PackObjectsHook",
"grpc.request.fullMethod":"/gitaly.HookService/PackObjectsHook",
"grpc.request.glProjectPath":"root/gitlab-workhorse",
"grpc.request.glRepository":"project-2",
"grpc.request.repoPath":"@hashed/d4/73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35.git",
"grpc.request.repoStorage":"default",
"grpc.request.topLevelGroup":"@hashed",
"grpc.service":"gitaly.HookService",
"grpc.start_time":"2021-03-25T14:57:52.747Z",
"level":"info",
"msg":"finished unary call with code OK",
"peer.address":"@",
"pid":20961,
"span.kind":"server",
"system":"grpc",
"time":"2021-03-25T14:57:53.543Z",
"pack_objects.compression_statistics": "Total 145991 (delta 68), reused 6 (delta 2), pack-reused 145911",
"pack_objects.enumerate_objects_ms": 170,
"pack_objects.prepare_pack_ms": 7,
"pack_objects.write_pack_file_ms": 786,
"pack_objects.written_object_count": 145991,
"pack_objects_cache.generated_bytes": 49533030,
"pack_objects_cache.hit": "false",
"pack_objects_cache.key": "123456789",
"pack_objects_cache.served_bytes": 49533030,
"peer.address": "127.0.0.1",
"pid": 8813,
}
Prometheus metric name Vector Description
gitaly_pack_objects_served_bytes_total   Size (in bytes) of the cache being served
gitaly_pack_objects_cache_lookups_total result hit or miss,indicating whether or not a cache lookup resulted in a cache hit or miss