Git receive/upload-pack 推送时占用所有 RAM

Git receive/upload-pack takes all RAM on push

我有一个带有 Git 的 Redmine 服务器。它有 2GB 的内存。

当有人尝试推送数据大于 2GB 的存储库时,我收到以下错误:

Counting objects: 957, done.
Delta compression using up to 12 threads.
Compressing objects: 100% (577/577), done.
Writing objects: 100% (957/957), 2.18 GiB | 29.66 MiB/s, done.
Total 957 (delta 255), reused 862 (delta 229)
fatal: The remote end hung up unexpectedly
fatal: The remote end hung up unexpectedly
Everything up-to-date

在第一个 fatal: ... 之前,ruby 开始占用服务器上的所有 RAM。

当内存使用量达到 ~2GB 时,会出现 fatal: ... 错误。服务器错误:

App 9339 stderr: Errno::ENOMEM: Cannot allocate memory - git

所以我的猜测是 Git 或 Redmine 试图将那些 2.18 GB 放入 RAM,这显然是行不通的。

错误后:

我怎样才能让它发挥作用?是 Redmine 还是 Git 占用了所有 RAM?

推送数据 "part by part" (like here) 可以解决吗?

我已经尝试 (git config http.postBuffer 524288000) 但没有成功。


编辑 (2017-06-01):

一些规格:

Redmine 部署在带有 passenger 模块的 Apache 服务器 (v2.2.22) 后面。它使用 Grack (last revision) for its Git integration with the Redmine SCM 插件来创建存储库。

重新查看日志后,罪魁祸首似乎是 Grack。

App 9339 stderr: Errno::ENOMEM: Cannot allocate memory - git
App 29461 stderr:       /opt/grack/lib/git_adapter.rb:24:in ``'
App 29461 stderr:       /opt/grack/lib/git_adapter.rb:24:in `command'
App 29461 stderr:       /opt/grack/lib/git_adapter.rb:34:in `upload_pack'
App 29461 stderr:       /opt/grack/lib/grack.rb:84:in `get_info_refs'
App 29461 stderr:       /opt/grack/lib/grack.rb:55:in `call'
App 29461 stderr:       /opt/grack/lib/grack.rb:55:in `call'

换句话说,错误发生在 Grack 启动 git upload-pack 命令时。


编辑 (2017-06-01) (2):

本地存储库确实有 2+GB 的包。我用以下内容重新包装它:

git repack -a -d --window-memory=512m --max-pack-size=512m

将那个大包分成多个更小的包。我还配置了以下客户端和服务器端:

git config pack.windowMemory 512m
git config pack.packSizeLimit 512m

但问题依旧

App 30005 stderr: [ 2017-06-01 09:15:04.4393 30122/0x000000019264a8(Worker 1) utils.rb:72 ]: *** Exception Errno::ENOMEM in Rack body object #each method (Cannot allocate memory - git) (process 30122, thread 0x000000019264a8(Worker 1)):
App 30005 stderr:       from /opt/grack/lib/git_adapter.rb:19:in `popen'
App 30005 stderr:       from /opt/grack/lib/git_adapter.rb:19:in `command'
App 30005 stderr:       from /opt/grack/lib/git_adapter.rb:43:in `receive_pack'
App 30005 stderr:       from /opt/grack/lib/grack.rb:70:in `block in service_rpc'

编辑 (2017-06-01) (3):

在 Grack 上添加了一些日志记录以查看它究竟启动了什么:

git receive-pack --advertise-refs --stateless-rpc /var/git/mygit.git

由于它没有从 Git 存储库目录启动命令,我尝试了以下操作:

git config --global pack.windowMemory 512m
git config --global pack.packSizeLimit 512m

遗憾的是仍然无法正常工作。

编辑 (2017-06-01) (4):

再次尝试,但使用 64m 而不是 512m,仍然没有效果。

看来Git还需要把所有对象都放到RAM上才能开始打包。在客户端,git repack 命令确实需要 2+GB 来执行,即使结果输出是多个 512 MB 的文件。

由于这个问题深入人心,这里是我用作答案的解决方法。

看起来 receive/upload-pack 需要将所有内容加载到 RAM 上。也许我错了,但我没有发现任何东西可以将 RAM 使用量减少到小数据包中。所以我所做的是创建一个临时交换文件。

# create a file of 4GB
sudo dd if=/dev/zero of=/swap bs=1024 count=4194303

# make it a swap file
sudo mkswap /swap

# enable the swap on this swap file
sudo swapon /swap

2GB 不够:推送大约需要 3.5GB,因此需要 4GB 的交换文件。

推送花了很长时间(没有 Sh*t Sherlock)但至少它成功了。

我留下了新的交换文件以备将来出现类似问题,但如果你想在推送后删除它,你可以执行以下操作:

# disable the swap (will automatically move used memory into RAM or another swap)
sudo swapoff /swap

# delete the file
sudo rm /swap

Reference on UNIX guide