Skip to content

projects/:id/repository/commits/:sha 请求情况

I'll help you investigate the GET /projects/:id/repository/commits/:sha endpoint to understand if it queries the database, Gitaly, and what might be causing the timeout error you're seeing.

Let me search for the relevant code:

[3 tools called]

Now let me trace how the commit method works and its interactions with Gitaly and the database:

[31 tools called]

Based on my analysis of the GitLab codebase, I can answer your questions about the GET /projects/:id/repository/commits/:sha endpoint:

1. 是否查询数据库?

该接口会查询数据库,主要查询以下内容:

      get ':id/repository/commits/:sha', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
        commit = user_project.commit(params[:sha])

        not_found! 'Commit' unless commit

        present commit, with: Entities::CommitDetail, include_stats: params[:stats], current_user: current_user
      end

返回的 CommitDetail 实体会额外查询:

    class CommitDetail < Commit
      include ::API::Helpers::Presentable

      expose :stats, using: Entities::CommitStats, if: :include_stats
      expose :status_for, as: :status, documentation: { type: 'string', example: 'success' }
      expose :project_id, documentation: { type: 'integer', example: 1 }

      expose :last_pipeline, documentation: { type: ::API::Entities::Ci::PipelineBasic.to_s } do |commit, options|
        pipeline = commit.last_pipeline if can_read_pipeline?
        ::API::Entities::Ci::PipelineBasic.represent(pipeline, options)
      end

      private

      def can_read_pipeline?
        Ability.allowed?(options[:current_user], :read_pipeline, object.last_pipeline)
      end
    end
  • last_pipeline: 查询 ci_pipelines 表获取该 commit 的最新 pipeline
  • status: 基于 pipeline 的状态

2. 是否查询 Gitaly?

该接口会调用 Gitaly 的 FindCommit RPC:

        def find(repo, commit_id = "HEAD")
          # Already a commit?
          return commit_id if commit_id.is_a?(Gitlab::Git::Commit)

          # This saves us an RPC round trip.
          return unless valid?(commit_id)

          commit = find_commit(repo, commit_id)

          decorate(repo, commit) if commit
        rescue Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository, ArgumentError
          nil
        end

        def find_commit(repo, commit_id)
          wrapped_gitaly_errors do
            repo.gitaly_commit_client.find_commit(commit_id)
          end
        end

调用链路:

  1. user_project.commit(sha)
  2. Gitlab::Git::Commit.find(repo, commit_id)
  3. repo.gitaly_commit_client.find_commit(commit_id)Gitaly RPC 调用
  4. 使用 GitalyClient.medium_timeout(默认 30 秒

3. 超时原因分析

您看到的错误 Gitlab::Git::CommandTimedOut: 4:Deadline Exceeded 是 Gitaly 超时错误:

      def handle_default_error(exception)
        case exception.code
        when GRPC::Core::StatusCodes::NOT_FOUND
          raise Gitlab::Git::Repository::NoRepository, exception
        when GRPC::Core::StatusCodes::INVALID_ARGUMENT
          raise ArgumentError, exception
        when GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
          raise Gitlab::Git::CommandTimedOut, exception
        when GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED
          raise ResourceExhaustedError, _("Upstream Gitaly has been exhausted. Try again later")
        else
          raise Gitlab::Git::CommandError, exception
        end
      end

可能的超时原因:

  1. Gitaly 服务器负载过高 - 正在处理大量请求
  2. 仓库过大 - 从磁盘读取 commit 数据耗时过长
  3. 存储性能问题 - 慢速磁盘或网络存储延迟
  4. Server Hooks 耗时 - 服务器端 Git hooks 执行时间过长
  5. 网络延迟 - GitLab 和 Gitaly 之间的网络问题
  6. 超时配置过短 - 默认 medium timeout 为 30 秒可能不够

解决方案:

  1. 调整 Gitaly 超时设置

    • 在 Admin Area → Settings → Preferences → Gitaly timeouts
    • 增加 gitaly_timeout_medium 值(默认 30 秒)
  2. 禁用耗时的 server hooks

  3. 检查 Gitaly 服务器性能

    • CPU、内存、磁盘 I/O
    • Gitaly 日志中的慢查询
  4. 优化仓库

    • 运行 git gc 清理
    • 检查是否有超大文件

参考文档:doc/user/project/repository/branches/_index.md 第 500 行提到了相同的错误和解决方法。