Go Opaque API移行

Opaque APIへの自動移行について説明します。

Opaque APIは、Goプログラミング言語向けProtocol Buffers実装の最新バージョンです。古いバージョンは現在、Open Struct APIと呼ばれています。導入については、Go Protobuf: Releasing the Opaque APIブログ投稿をご覧ください。

Opaque APIへの移行は、プロトメッセージごと、または.protoファイルごとに、api_level機能を可能な値の1つに設定することで段階的に行われます。

  • API_OPENはOpen Struct APIを選択します。これはエディション2023にバックポートされたため、Goプラグインの古いバージョンでは認識されない場合があります。
  • API_HYBRIDはOpenとOpaqueの間のステップです。ハイブリッドAPIにはアクセサーメソッドも含まれています(そのため、コードを更新できます)が、以前と同様に構造体フィールドをエクスポートします。パフォーマンスの違いはありません。このAPIレベルは移行に役立つだけです。
  • API_OPAQUEはOpaque APIを選択します。これはエディション2024以降のデフォルトです。

特定の.protoファイルのデフォルトをオーバーライドするには、api_level機能を設定します。

edition = "2024";

package log;

import "google/protobuf/go_features.proto";
option features.(pb.go).api_level = API_OPEN;

message LogEntry {  }

既存のファイルに対してapi_levelAPI_OPAQUEに変更する前に、生成されたプロトコードの既存のすべての使用箇所を更新する必要があります。open2opaqueツールがこれを支援します。

便宜上、protocコマンドラインフラグでデフォルトのAPIレベルをオーバーライドすることもできます。

protoc […] --go_opt=default_api_level=API_OPEN

特定のファイル(すべてのファイルではなく)のデフォルトAPIレベルをオーバーライドするには、apilevelMマッピングフラグを使用します(インポートパスのMフラグと同様)。

protoc […] --go_opt=apilevelMhello.proto=API_OPEN

自動移行

既存のプロジェクトをOpaque APIに可能な限り簡単に移行できるように努めています。当社のopen2opaqueツールがほとんどの作業を行います。

移行ツールをインストールするには、次を使用します。

go install google.golang.org/open2opaque@latest

プロジェクトの準備

ビルド環境とプロジェクトが十分新しいバージョンのProtocol BuffersとGo Protobufを使用していることを確認してください。

  1. protobufリリースページからprotobufコンパイラ(protoc)をバージョン29.0以降に更新します。

  2. protobufコンパイラのGoプラグイン(protoc-gen-go)をバージョン1.36.0以降に更新します。

    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
    
  3. 各プロジェクトで、go.modファイルを更新して、protobufモジュールをバージョン1.36.0以降で使用します。

    go get google.golang.org/protobuf@latest
    

ステップ1. ハイブリッドAPIに切り替える

open2opaqueツールを使用して、.protoファイルをハイブリッドAPIに切り替えます。

open2opaque setapi -api HYBRID $(find . -name "*.proto")

既存のコードは引き続きビルドされます。ハイブリッドAPIは、新しいアクセサーメソッドを追加しつつ、構造体フィールドを可視のままにする、Open APIとOpaque APIの間のステップです。

ステップ2. open2opaque rewrite

GoコードをOpaque APIを使用するように書き換えるには、open2opaque rewriteコマンドを実行します。

open2opaque rewrite -levels=red github.com/robustirc/robustirc/...

1つ以上のパッケージまたはパターンを指定できます。

例として、次のようなコードがあった場合

logEntry := &logpb.LogEntry{}
if req.IPAddress != nil {
    logEntry.IPAddress = redactIP(req.IPAddress)
}
logEntry.BackendServer = proto.String(host)

ツールはそれをアクセサーを使用するように書き換えます。

logEntry := &logpb.LogEntry{}
if req.HasIPAddress() {
    logEntry.SetIPAddress(redactIP(req.GetIPAddress()))
}
logEntry.SetBackendServer(host)

もう1つの一般的な例は、構造体リテラルでprotobufメッセージを初期化することです。

return &logpb.LogEntry{
    BackendServer: proto.String(host),
}

Opaque APIでは、同等のものはビルダーを使用することです。

return logpb.LogEntry_builder{
    BackendServer: proto.String(host),
}.Build()

ツールは利用可能な書き換えを異なるレベルに分類します。-levels=red引数は、人間のレビューが必要なものを含むすべての書き換えを有効にします。以下のレベルが利用可能です。

  • 緑: 安全な書き換え(高い信頼性)。ツールが行う変更のほとんどが含まれます。これらの変更は綿密な検討を必要とせず、人間の監視なしに自動化によって提出することも可能です。
  • 黄: (妥当な信頼性)これらの書き換えには人間のレビューが必要です。これらは正しいはずですが、レビューしてください。
  • 赤: 潜在的に危険な書き換えで、稀で複雑なパターンを変更します。これらには慎重な人間のレビューが必要です。たとえば、既存の関数が*stringパラメータを受け取る場合、proto.String(msg.GetFoo())を使用する一般的な修正は、関数がポインタに書き込むことによってフィールド値を変更することを意図していた場合(*foo = "value")には機能しません。

多くのプログラムは、緑の変更のみで完全に移行できます。プロトメッセージまたはファイルをOpaque APIに移行する前に、すべてのレベルのすべての書き換えを完了する必要があります。その時点で、コードには直接的な構造体アクセスは残りません。

ステップ3. 移行と検証

移行を完了するには、open2opaqueツールを使用して、.protoファイルをOpaque APIに切り替えます。

open2opaque setapi -api OPAQUE $(find . -name "*.proto")

これで、Opaque APIにまだ書き換えられていない残りのコードはコンパイルされなくなります。

ユニットテスト、統合テスト、その他の検証手順があれば実行してください。

質問、問題点?

まず、Opaque API FAQをご覧ください。それでも質問に答えられない、または問題を解決できない場合は、質問したり問題を報告したりするにはどこですか?を参照してください。