バージョン 23.x のニュース発表

Protocol Buffers バージョン 23.x の変更点が発表されました。

以下の発表は、2023 年 5 月 8 日にリリースされたバージョン 23.x に特化したものです。時系列順の情報については、「ニュース」を参照してください。

Rubyジェネレーターの変更点

この GitHub PR(23.x リリースに登場予定)は、Ruby コードジェネレーターを、DSL の代わりにシリアライズされたプロトを生成するように変更します。

これは、DSL を別のパッケージに分割する予定があるため、コードジェネレーターから DSL を削除します。

.proto ファイルが次のような場合

syntax = "proto3";

package pkg;

message TestMessage {
  optional int32 i32 = 1;
  optional TestMessage msg = 2;
}

以前に生成されたコード

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: protoc_explorer/main.proto

require 'google/protobuf'

Google::Protobuf::DescriptorPool.generated_pool.build do
  add_file("test.proto", :syntax => :proto3) do
    add_message "pkg.TestMessage" do
      proto3_optional :i32, :int32, 1
      proto3_optional :msg, :message, 2, "pkg.TestMessage"
    end
  end
end

module Pkg
  TestMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("pkg.TestMessage").msgclass
end

変更後に生成されたコード

# frozen_string_literal: true
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: test.proto

require 'google/protobuf'

descriptor_data = "\n\ntest.proto\x12\x03pkg\"S\n\x0bTestMessage\x12\x10\n\x03i32\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\"\n\x03msg\x18\x02 \x01(\x0b\x32\x10.pkg.TestMessageH\x01\x88\x01\x01\x42\x06\n\x04_i32B\x06\n\x04_msgb\x06proto3"
begin
  Google::Protobuf::DescriptorPool.generated_pool.add_serialized_file(descriptor_data)
rescue TypeError => e
  # <compatibility code, see below>
end

module Pkg
  TestMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("pkg.TestMessage").msgclass
end

この変更により、以前存在していたほぼすべての適合性の問題が修正されます。これは、DSL (情報が失われる) からシリアライズされたディスクリプタ (すべての情報が保持される) に移行したことによる副次的な効果です。

下位互換性

この変更は、2021 年 9 月にリリースされた Ruby Protobuf >= 3.18.0 と 100% 互換性があるはずです。さらに、既存のすべてのユーザーとデプロイメントとも互換性があるはずです。

このレベルの後方互換性を実現するために挿入された、注意すべき特殊な互換性コードが**あります**。互換性コードがなければ、後方互換性を破る可能性のあるエッジケースが存在します。以前のコードは緩やかでしたが、新しいコードはより厳格になります。

完全なシリアライズされたディスクリプタを使用する場合、そのファイルによってインポートされたすべての ` .proto` ファイルのリストが含まれます (DSL は依存関係を適切に追加しませんでした)。`descriptor.proto` のコードを参照してください。

`add_serialized_file` は、ディスクリプタにリストされているすべての依存関係が以前に `add_serialized_file` で追加されたことを確認します。通常、これは問題ありません。生成されたコードにはすべての依存関係の Ruby `require` ステートメントが含まれるため、依存する型が `DescriptorPool` で以前に定義されていない場合、ディスクリプタはとにかく読み込みに失敗します。

しかし、ファイルパスに関する曖昧さがある場合、問題が発生する可能性があります。たとえば、以下のシナリオを考えてみましょう。

// foo/bar.proto

syntax = "proto2";

message Bar {}
// foo/baz.proto

syntax = "proto2";

import "bar.proto";

message Baz {
  optional Bar bar = 1;
}

このように `protoc` を呼び出すと、正しく動作します

$ protoc --ruby_out=. -Ifoo foo/bar.proto foo/baz.proto
$ RUBYLIB=. ruby baz_pb.rb

しかし、もしこのように `protoc` を呼び出し、互換性コードがなかった場合、ロードに失敗するでしょう。

$ protoc --ruby_out=. -I. -Ifoo foo/baz.proto
$ protoc --ruby_out=. -I. -Ifoo foo/bar.proto
$ RUBYLIB=foo ruby foo/baz_pb.rb
foo/baz_pb.rb:10:in `add_serialized_file': Unable to build file to DescriptorPool: Depends on file 'bar.proto', but it has not been loaded (Google::Protobuf::TypeError)
    from foo/baz_pb.rb:10:in `<main>'

問題は、`bar.proto` が `bar.proto` と `foo/bar.proto` という2つの異なる正規名で参照されていることです。これはユーザーエラーです。各インポートは常に一貫した完全パスで参照されるべきです。このようなユーザーエラーは稀であることを願いますが、試してみないとわかりません。

この変更のコードは、このエッジケースが発生したことを検出した場合、`warn` を使用して警告を出力します。

$ RUBYLIB=foo ruby foo/baz_pb.rb
Warning: Protobuf detected an import path issue while loading generated file foo/baz_pb.rb
- foo/baz.proto imports bar.proto, but that import was loaded as foo/bar.proto
Each proto file must use a consistent fully-qualified name.
This will become an error in the next major version.

この場合、考えられる修正は2つあります。1つは、インポートに対して一貫して `bar.proto` の名前を使用すること ( `-I.` を削除する)。もう1つは、インポートに対して一貫して `foo/bar.proto` の名前を使用すること (インポート行を `import "foo/bar.proto"` に変更し、 `-Ifoo` を削除する) です。

この互換性コードは次のメジャーバージョンで削除する予定です。

Bazel <5.3 のサポート終了

v23 では Bazel 4 のサポートを終了します。Protobuf は引き続き Bazel 5 LTS をサポートし、Bazel 5.3 が最低要件バージョンとなります。これは、Foundational C++ Support Policy に記載されているビルドサポートポリシーに従い、Foundational C++ Support のバージョンに反映されています。

構文リフレクションの非推奨化

v23 では、リフレクションを使用した構文バージョンのチェック機能が非推奨になります。非推奨はビルド時に警告として表示されます。この機能は将来のリリースで削除される予定です。