Language⚡ パフォーマンス・速度🛠️ 開発体験(DX)

Mirrativ

Mirrativ、レガシーなPerlサーバーサイドをGoへ段階的に刷新

PerlGo

既存Perlアプリを一気に書き換えず、Unix Domain Socket経由でPerlからGoの機能を呼び出す方式を確立し、新機能はGoで実装する方針へ移行した。

課題

配信・ギフトなど基幹のPerl実装からGoの機能を呼び出す際、fork環境でgoroutineがハングした

アプローチ

Perl XS+CGO共有ライブラリ方式を断念しUnix Domain Socket経由の親子プロセス通信へ転換

効果

fork後でも安定動作するUDS方式を確立、netpollのイベントループで並列性能も維持

背景

Mirrativは2020年から段階的にサーバーサイドの技術をPerlからGoへ移行しており、現在は新機能をGoで実装し、Perlでは積極的に新規実装を行わない方針を取っている。しかし配信やギフトといったビジネスの根幹を支えるレガシーなPerl実装の修正時に、PerlからGoの機能を呼び出したいというニーズが出てきた。

移行のプロセス

当初はGoを`-buildmode=c-shared`で共有ライブラリ化し、Perl XSでバインディングする方式を試みた。しかしParallel::Preforkによるfork型のPerlアプリケーションから呼び出すと、子プロセスでgoroutineの処理がハングアップする問題が発生した。GDBで追跡した結果、fork後にgoroutineの初期化が正常に完了しないことが判明した。この問題を解決するため、親プロセスでUnix Domain Socket(UDS)をリスンし、子プロセスがそのソケット経由で通信する方式に転換した。これによりfork後でも動作するようになり、並列実行時にはcloudwego/netpollを使ったイベントループ実装によって並列数が増えてもパフォーマンスが劣化しにくい構成を実現した。

結果

パフォーマンス比較では、単純な呼び出しコストはXS直接方式よりUDS方式の方が増加した。

  • one()関数: XS直接呼び出し124ns → UDS方式86μs
  • md5処理: XS直接呼び出し494ns → UDS方式88μs
  • ack(3,4): UDS方式で約1ms

それでもfork環境での安定動作を優先し、UDS方式は既に本番環境への導入が始まっており、少しずつ機能の移行が進められている。

学び

forkを伴うPerl環境ではXSの直接呼び出しではなくUDS経由の呼び出しの方が、パフォーマンスと可用性を両立できるとの知見を得たとしている。

2021-01-01
他の事例も見る