Go Opaque API への移行

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

Opaque API は、Go プログラミング言語向けの Protocol Buffers 実装の最新バージョンです。古いバージョンは現在 Open Struct API と呼ばれています。概要については、Go Protobuf: Opaque API のリリースのブログ記事を参照してください。

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

  • API_OPEN は Open Struct API を選択します。これは Edition 2023 にバックポートされたため、古いバージョンの Go プラグインでは認識されない場合があります。
  • API_HYBRID は Open と Opaque の中間のステップです。Hybrid API にはアクセサメソッドも含まれています(そのためコードを更新できます)が、以前と同様に構造体フィールドはエクスポートされます。パフォーマンスの違いはありません。この API レベルは移行に役立つだけです。
  • API_OPAQUE は Opaque API を選択します。これは Edition 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
go install golang.org/x/tools/cmd/goimports@latest

プロジェクトの準備

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

  1. protobuf コンパイラ (protoc) を protobuf リリースページからバージョン 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 ファイルを更新して、バージョン 1.36.0 以降の protobuf モジュールを使用するようにします。

    go get google.golang.org/protobuf@latest
    

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

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

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

次に、プロトコルバッファを再コンパイルします。

既存のコードは引き続きビルドされます。Hybrid 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つの一般的な例は、構造体リテラルでプロトコルバッファメッセージを初期化することです。

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

Opaque API では、同等の機能としてビルダーを使用します。

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

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

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

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

ステップ3. 移行と検証

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

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

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

単体テスト、統合テスト、およびその他の検証ステップがあれば実行します。

質問ですか?問題ですか?

まず、Opaque API FAQを確認してください。それでも質問が解決しない場合や問題が解決しない場合は、どこで質問したり問題を報告できますか?を参照してください。