diff --git a/content/editions/features.md b/content/editions/features.md index 424da25c7..cec962d81 100644 --- a/content/editions/features.md +++ b/content/editions/features.md @@ -472,7 +472,7 @@ message Msg { This feature determines how generated code should treat string fields. This replaces the `ctype` option from proto2 and proto3, and offers a new -`string_view` feature. In Edition 2023, either `ctype` or `string_view` may be +`string_view` feature. In Edition 2023, either `ctype` or `string_type` may be specified on a field, but not both. **Values available:** diff --git a/content/includes/version-tables.css b/content/includes/version-tables.css index 1e84fe95c..c0fb516d3 100644 --- a/content/includes/version-tables.css +++ b/content/includes/version-tables.css @@ -95,36 +95,36 @@ table.version-chart td.active { /* latest release column */ /* * How to advance the selection of the latest release: - * Replace class 'y24q3' in the following selectors with 'y24q4' (the class + * Replace class 'y24q4' in the following selectors with 'y25q1' (the class * referring to the quarter of the next release). Please also update this * instruction as a courtesy to the next maintainer. */ /* visually replace 'yyQq' heading with string 'Latest' */ -table.version-chart th.y24q3 span { +table.version-chart th.y24q4 span { display: none; } -table.version-chart th.y24q3::after { +table.version-chart th.y24q4::after { content: "Latest" } /* draw a focus rectangle around the latest release column */ -table.version-chart th.y24q3 { +table.version-chart th.y24q4 { border-top: 2px solid #e06666 !important; border-left: 2px solid #e06666 !important; border-right: 2px solid #e06666 !important; } -table.version-chart td.y24q3 { +table.version-chart td.y24q4 { font-weight: bold; border-left: 2px solid #e06666 !important; border-right: 2px solid #e06666 !important; } -table.version-chart tr:last-child td.y24q3 { +table.version-chart tr:last-child td.y24q4 { border-bottom: 2px solid #e06666 !important; } /* future release columns */ -table.version-chart td:not(:has(~ .y24q3)):not(.y24q3) { +table.version-chart td:not(:has(~ .y24q4)):not(.y24q4) { font-style: italic; } diff --git a/content/news/2024-12-04.md b/content/news/2024-12-04.md new file mode 100644 index 000000000..8679ee36c --- /dev/null +++ b/content/news/2024-12-04.md @@ -0,0 +1,77 @@ ++++ +title = "Changes announced December 4, 2024" +linkTitle = "December 4, 2024" +toc_hide = "true" +description = "Changes announced for Protocol Buffers on December 4, 2024." +type = "docs" ++++ + +We are planning to modify the Protobuf debug APIs (including Protobuf +AbslStringify, `proto2::ShortFormat`, `proto2::Utf8Format`, +`Message::DebugString`, `Message::ShortDebugString`, `Message::Utf8DebugString`) +in v30 to redact sensitive fields annotated by `debug_redact`; the outputs of +these APIs will contain a per-process randomized prefix, and so will no longer +be parseable by Protobuf TextFormat Parsers. + +## Motivation + +Currently Protobuf debug APIs print every field in a proto into human-readable +formats. This may lead to privacy incidents where developers accidentally log +Protobuf debug outputs containing sensitive fields. + +## How to Annotate Sensitive Fields + +There are two ways to mark fields sensitive: + +* Mark a field with the field option `debug_redact = true`, directly. + + ```proto + message Foo { + optional string secret = 1 [debug_redact = true]; + } + ``` + +* If you have already defined a field annotation of type Enum by extending + `proto2.FieldOptions`, and certain values of this annotation are used to + annotate fields you would like to redact, then you can annotate these values + with `debug_redact = true`. All the fields that have been annotated with + such values will be redacted. + + ```proto + package my.package; + + extend proto2.FieldOptions { + # The existing field annotation + optional ContentType content_type = 1234567; + }; + + enum ContentType { + PUBLIC = 0; + SECRET = 1 [debug_redact = true]; + }; + + message Foo { + # will not be redacted + optional string public_info = 1 [ + (my.package.content_type) = PUBLIC + ]; + # will be redacted + optional string secret = 1 [ + (my.package.content_type) = SECRET + ]; + } + ``` + +## New Debug Format + +Compared to the existing debug format, the new debug format has two major +differences: + +* The sensitive fields annotated with `debug_redact` are redacted + automatically in the output formats +* The output formats will contain a per-process randomized prefix, which will + make them no longer be parsable by TextFormat parsers. + +Note that the second change is true regardless of whether the proto contains +sensitive fields or not, which ensures that any debug output always cannot be +deserialized regardless of the proto content. diff --git a/content/news/_index.md b/content/news/_index.md index 489aa446a..f60a38c98 100644 --- a/content/news/_index.md +++ b/content/news/_index.md @@ -20,6 +20,8 @@ New news topics will also be published to the The following news topics provide information in the reverse order in which it was released. +* [December 4, 2024](/news/2024-12-04) - `DebugString` + replaced * [November 7, 2024](/news/2024-11-07) - More breaking changes in the upcoming 30.x release * [October 2, 2024](/news/2024-10-02) - Breaking diff --git a/content/news/v30.md b/content/news/v30.md index 723b3d704..da9d2e2c4 100644 --- a/content/news/v30.md +++ b/content/news/v30.md @@ -43,6 +43,18 @@ dependencies. If you use installed packages, you won't be affected. It could break some CMake workflows. +### Modify Debug APIs to Redact Sensitive Fields {#debug-redaction} + +We are planning to modify the Protobuf debug APIs (including Protobuf +AbslStringify, `proto2::ShortFormat`, `proto2::Utf8Format`, +`Message::DebugString`, `Message::ShortDebugString`, `Message::Utf8DebugString`) +in v30 to redact sensitive fields annotated by `debug_redact`; the outputs of +these APIs will contain a per-process randomized prefix, and so will no longer +be parseable by Protobuf TextFormat Parsers. + +Read more about this in the +[news article released November 21, 2024](/news/2024-11-21). + ### Remove Deprecated APIs {#remove-deprecated} v30 will remove the following public runtime APIs, which have been marked diff --git a/content/programming-guides/deserialize-debug.md b/content/programming-guides/deserialize-debug.md new file mode 100644 index 000000000..26fe363cd --- /dev/null +++ b/content/programming-guides/deserialize-debug.md @@ -0,0 +1,120 @@ ++++ +title = "Deserializing Debug Proto Representations" +weight = 89 +description = "How to log debugging information in Protocol Buffers." +type = "docs" ++++ + +From version 29.x, `DebugString` APIs (`proto2::DebugString`, +`proto2::ShortDebugString`, `proto2::Utf8DebugString`) are deprecated. +DebugString users should migrate to some Abseil string functions (such as +`absl::StrCat`, `absl::StrFormat`, `absl::StrAppend`, AND `absl::Substitute`), +Abseil logging API, and some Protobuf APIs (`proto2::ShortFormat`, +`proto2::Utf8Format`) to automatically convert proto arguments into a new +debugging format . + +Unlike the Protobuf DebugString output format, the new debugging format +automatically redacts sensitive fields by replacing their values with the string +"[REDACTED]" (without the quotation marks). In +addition, to ensure that this new output format cannot be deserialized by +Protobuf TextFormat parsers, regardless of whether the underlying proto contains +SPII fields, we add a set of randomized links pointing to this article +and a randomized-length whitespace sequence. The new debugging format looks as +follows: + +```none +go/nodeserialize +spii_field: [REDACTED] +normal_field: "value" +``` + +Note that the new debugging format is only different from the output format of +DebugString format in two ways: + +* The URL prefix +* The values of SPII fields are replaced by + "[REDACTED]" (without the quotes) + +The new debugging format never removes any field names; it only replaces the +value with +"[REDACTED]" if the field is considered sensitive. +**If you don't see certain fields in the output, it is because those fields are +not set in the proto.** + +**Tip:** If you see only the URL and nothing else, your proto is empty! + +## Why is this URL here? + +We want to make sure nobody deserializes human-readable representations of a +protobuf message intended for humans debugging a system. Historically, +`.DebugString()` and `TextFormat` were interchangeable, and existing systems use +DebugString to transport and store data. + +We want to make sure sensitive data does not accidentally end up in logs. +Therefore, we are transparently redacting some field values from protobuf +messages before turning them into a string +("[REDACTED]"). This reduces the security & privacy +risk of accidental logging, but risks data loss if other systems deserialize +your message. To address this risk, we are intentionally splitting the +machine-readable TextFormat from the human-readable debug format to be used in +log messages. + +### Why are there links in my web page? Why is my code producing this new "debug representation"? + +This is intentional, to make the "debug representation" of your protos +(produced, for example, by logging) incompatible with TextFormat. We want to +prevent anyone from depending on debugging mechanisms to transport data between +programs. Historically, the debug format (generated by the DebugString APIs) and +TextFormat have been incorrectly used in a interchangeable fashion. We hope this +intentional effort will prevent that going forward. + +We intentionally picked a link over less visible format changes to get an +opportunity to provide context. This might stand out in UIs, such as if you +display status information on a table in a webpage. You may use +`TextFormat::PrintToString` instead, which will not redact any information and +preserves formatting. However, use this API cautiously -- there are no built in +protections. As a rule of thumb, if you are writing data to debug logs, or +producing status messages, you should continue to use the Debug Format with the +link. Even if you are currently not handling sensitive data, keep in mind that +systems can change and code gets re-used. + +### I tried converting this message into TextFormat, but I noticed the format changes every time my process restarts. + +This is intentional. Don't attempt to parse the output of this debug format. We +reserve the right to change the syntax without notice. The debug format syntax +randomly changes per process to prevent inadvertent dependencies. If a syntactic +change in the debug format would break your system, chances are you shouldn't +use the debug representation of a proto. + +## FAQ + +### Can I Just Use TextFormat Everywhere? + +Don't use TextFormat for producing log messages. This will bypass all built-in +protections, and you risk accidentally logging sensitive information. Even if +your systems are currently not handling any sensitive data, this can change in +the future. + +Distinguish logs from information that's meant for further processing by other +systems by using either the debug representation or TextFormat as appropriate. + +### I Want to Write Configuration Files That Need to Be Both Human-Readable And Machine-Readable + +For this use case, you can use TextFormat explicitly. You are responsible for +making sure your configuration files don't contain any PII. + +### I Am Writing a Unit Test, and Want to Compare Debugstring in a Test Assertion + +If you want to compare protobuf values, use `MessageDifferencer` like in the +following: + +```cpp +using google::protobuf::util::MessageDifferencer; +... +MessageDifferencer diff; +... +diff.Compare(foo, bar); +``` + +Besides ignoring formatting and field order differences, you will also get +better error messages. diff --git a/content/programming-guides/dos-donts.md b/content/programming-guides/dos-donts.md index eebb91f4c..e1bc96541 100644 --- a/content/programming-guides/dos-donts.md +++ b/content/programming-guides/dos-donts.md @@ -183,7 +183,7 @@ from repeated to scalar will result in the last deserialized value "winning." Going from scalar to repeated is OK in proto2 and in proto3 with `[packed=false]` because for binary serialization the scalar value becomes a -one-element list . +one-element list. diff --git a/content/reference/cpp/cpp-generated.md b/content/reference/cpp/cpp-generated.md index 443a9297e..6b3cd9194 100644 --- a/content/reference/cpp/cpp-generated.md +++ b/content/reference/cpp/cpp-generated.md @@ -409,21 +409,21 @@ enum Bar { For either of these field definitions: ```proto -optional Bar foo = 1; -required Bar foo = 1; +optional Bar bar = 1; +required Bar bar = 1; ``` The compiler will generate the following accessor methods: -- `bool has_foo() const`: Returns `true` if the field is set. -- `Bar foo() const`: Returns the current value of the field. If the field is +- `bool has_bar() const`: Returns `true` if the field is set. +- `Bar bar() const`: Returns the current value of the field. If the field is not set, returns the default value. -- `void set_foo(Bar value)`: Sets the value of the field. After calling this, - `has_foo()` will return `true` and `foo()` will return `value`. In debug +- `void set_bar(Bar value)`: Sets the value of the field. After calling this, + `has_bar()` will return `true` and `bar()` will return `value`. In debug mode (i.e. NDEBUG is not defined), if `value` does not match any of the values defined for `Bar`, this method will abort the process. -- `void clear_foo()`: Clears the value of the field. After calling this, - `has_foo()` will return `false` and `foo()` will return the default value. +- `void clear_bar()`: Clears the value of the field. After calling this, + `has_bar()` will return `false` and `bar()` will return the default value. ### Implicit Presence Enum Fields (proto3) {#implicit-enum} @@ -440,17 +440,17 @@ enum Bar { For this field definition: ```proto -Bar foo = 1; // no field label specified, defaults to implicit presence. +Bar bar = 1; // no field label specified, defaults to implicit presence. ``` The compiler will generate the following accessor methods: -- `Bar foo() const`: Returns the current value of the field. If the field is +- `Bar bar() const`: Returns the current value of the field. If the field is not set, returns the default value (0). -- `void set_foo(Bar value)`: Sets the value of the field. After calling this, - `foo()` will return `value`. -- `void clear_foo()`: Clears the value of the field. After calling this, - `foo()` will return the default value. +- `void set_bar(Bar value)`: Sets the value of the field. After calling this, + `bar()` will return `value`. +- `void clear_bar()`: Clears the value of the field. After calling this, + `bar()` will return the default value. ### Optional Embedded Message Fields (proto2 and proto3) {#embeddedmessage} @@ -464,35 +464,35 @@ For any of these field definitions: ```proto //proto2 -optional Bar foo = 1; -required Bar foo = 1; +optional Bar bar = 1; +required Bar bar = 1; //proto3 -Bar foo = 1; +Bar bar = 1; ``` The compiler will generate the following accessor methods: -- `bool has_foo() const`: Returns `true` if the field is set. -- `const Bar& foo() const`: Returns the current value of the field. If the +- `bool has_bar() const`: Returns `true` if the field is set. +- `const Bar& bar() const`: Returns the current value of the field. If the field is not set, returns a `Bar` with none of its fields set (possibly `Bar::default_instance()`). -- `Bar* mutable_foo()`: Returns a pointer to the mutable `Bar` object that +- `Bar* mutable_bar()`: Returns a pointer to the mutable `Bar` object that stores the field's value. If the field was not set prior to the call, then the returned `Bar` will have none of its fields set (i.e. it will be - identical to a newly-allocated `Bar`). After calling this, `has_foo()` will - return `true` and `foo()` will return a reference to the same instance of + identical to a newly-allocated `Bar`). After calling this, `has_bar()` will + return `true` and `bar()` will return a reference to the same instance of `Bar`. -- `void clear_foo()`: Clears the value of the field. After calling this, - `has_foo()` will return `false` and `foo()` will return the default value. -- `void set_allocated_foo(Bar* bar)`: Sets the `Bar` object to the field and +- `void clear_bar()`: Clears the value of the field. After calling this, + `has_bar()` will return `false` and `bar()` will return the default value. +- `void set_allocated_bar(Bar* bar)`: Sets the `Bar` object to the field and frees the previous field value if it exists. If the `Bar` pointer is not `NULL`, the message takes ownership of the allocated `Bar` object and - `has_foo()` will return `true`. Otherwise, if the `Bar` is `NULL`, the - behavior is the same as calling `clear_foo()`. -- `Bar* release_foo()`: Releases the ownership of the field and returns the + `has_bar()` will return `true`. Otherwise, if the `Bar` is `NULL`, the + behavior is the same as calling `clear_bar()`. +- `Bar* release_bar()`: Releases the ownership of the field and returns the pointer of the `Bar` object. After calling this, caller takes the ownership - of the allocated `Bar` object, `has_foo()` will return `false`, and `foo()` + of the allocated `Bar` object, `has_bar()` will return `false`, and `bar()` will return the default value. ### Repeated Numeric Fields {#repeatednumeric} @@ -593,33 +593,33 @@ enum Bar { For this field definition: ```proto -repeated Bar foo = 1; +repeated Bar bar = 1; ``` The compiler will generate the following accessor methods: -- `int foo_size() const`: Returns the number of elements currently in the +- `int bar_size() const`: Returns the number of elements currently in the field. To check for an empty set, consider using the [`empty()`](/reference/cpp/api-docs/google.protobuf.repeated_field#RepeatedPtrField) method in the underlying `RepeatedField` instead of this method. -- `Bar foo(int index) const`: Returns the element at the given zero-based - index. Calling this method with index outside of [0, foo_size()) yields +- `Bar bar(int index) const`: Returns the element at the given zero-based + index. Calling this method with index outside of [0, bar_size()) yields undefined behavior. -- `void set_foo(int index, Bar value)`: Sets the value of the element at the +- `void set_bar(int index, Bar value)`: Sets the value of the element at the given zero-based index. In debug mode (i.e. NDEBUG is not defined), if `value` does not match any of the values defined for `Bar`, this method will abort the process. -- `void add_foo(Bar value)`: Appends a new element to the end of the field +- `void add_bar(Bar value)`: Appends a new element to the end of the field with the given value. In debug mode (i.e. NDEBUG is not defined), if `value` does not match any of the values defined for `Bar`, this method will abort the process. -- `void clear_foo()`: Removes all elements from the field. After calling this, - `foo_size()` will return zero. -- `const RepeatedField& foo() const`: Returns the underlying +- `void clear_bar()`: Removes all elements from the field. After calling this, + `bar_size()` will return zero. +- `const RepeatedField& bar() const`: Returns the underlying [`RepeatedField`](/reference/cpp/api-docs/google.protobuf.repeated_field#RepeatedField) that stores the field's elements. This container class provides STL-like iterators and other methods. -- `RepeatedField* mutable_foo()`: Returns a pointer to the underlying +- `RepeatedField* mutable_bar()`: Returns a pointer to the underlying mutable `RepeatedField` that stores the field's elements. This container class provides STL-like iterators and other methods. @@ -634,27 +634,27 @@ message Bar {} For this field definitions: ```proto -repeated Bar foo = 1; +repeated Bar bar = 1; ``` The compiler will generate the following accessor methods: -- `int foo_size() const`: Returns the number of elements currently in the +- `int bar_size() const`: Returns the number of elements currently in the field. To check for an empty set, consider using the [`empty()`](/reference/cpp/api-docs/google.protobuf.repeated_field#RepeatedPtrField) method in the underlying `RepeatedField` instead of this method. -- `const Bar& foo(int index) const`: Returns the element at the given - zero-based index. Calling this method with index outside of [0, foo_size()) +- `const Bar& bar(int index) const`: Returns the element at the given + zero-based index. Calling this method with index outside of [0, bar_size()) yields undefined behavior. -- `Bar* mutable_foo(int index)`: Returns a pointer to the mutable `Bar` object +- `Bar* mutable_bar(int index)`: Returns a pointer to the mutable `Bar` object that stores the value of the element at the given zero-based index. Calling - this method with index outside of [0, foo_size()) yields undefined behavior. -- `Bar* add_foo()`: Adds a new element to the end of the field and returns a + this method with index outside of [0, bar_size()) yields undefined behavior. +- `Bar* add_bar()`: Adds a new element to the end of the field and returns a pointer to it. The returned `Bar` is mutable and will have none of its fields set (i.e. it will be identical to a newly-allocated `Bar`). -- `void clear_foo()`: Removes all elements from the field. After calling this, - `foo_size()` will return zero. -- `const RepeatedPtrField& foo() const`: Returns the underlying +- `void clear_bar()`: Removes all elements from the field. After calling this, + `bar_size()` will return zero. +- `const RepeatedPtrField& bar() const`: Returns the underlying [`RepeatedPtrField`](/reference/cpp/api-docs/google.protobuf.repeated_field#RepeatedPtrField) that stores the field's elements. This container class provides STL-like iterators and other methods. @@ -777,28 +777,28 @@ For the [oneof](#oneof) field definition: ```proto oneof example_name { - Bar foo = 1; + Bar bar = 1; ... } ``` The compiler will generate the following accessor methods: -- `bool has_foo() const`: Returns `true` if oneof case is `kFoo`. -- `Bar foo() const`: Returns the current value of the field if oneof case is - `kFoo`. Otherwise, returns the default value. -- `void set_foo(Bar value)`: +- `bool has_bar() const`: Returns `true` if oneof case is `kBar`. +- `Bar bar() const`: Returns the current value of the field if oneof case is + `kBar`. Otherwise, returns the default value. +- `void set_bar(Bar value)`: - If any other oneof field in the same oneof is set, calls `clear_example_name()`. - - Sets the value of this field and sets the oneof case to `kFoo`. - - `has_foo()` will return `true`, `foo()` will return `value` and - `example_name_case()` will return `kFoo`. + - Sets the value of this field and sets the oneof case to `kBar`. + - `has_bar()` will return `true`, `bar()` will return `value` and + `example_name_case()` will return `kBar`. - In debug mode (i.e. NDEBUG is not defined), if `value` does not match any of the values defined for `Bar`, this method will abort the process. -- `void clear_foo()`: - - Nothing will be changed if the oneof case is not `kFoo`. - - If the oneof case is `kFoo`, clears the value of the field and the oneof - case. `has_foo()` will return `false`, `foo()` will return the default +- `void clear_bar()`: + - Nothing will be changed if the oneof case is not `kBar`. + - If the oneof case is `kBar`, clears the value of the field and the oneof + case. `has_bar()` will return `false`, `bar()` will return the default value and `example_name_case()` will return `EXAMPLE_NAME_NOT_SET`. ### Oneof Embedded Message Fields {#oneof-embedded} @@ -813,47 +813,47 @@ For the [oneof](#oneof) field definition: ```proto oneof example_name { - Bar foo = 1; + Bar bar = 1; ... } ``` The compiler will generate the following accessor methods: -- `bool has_foo() const`: Returns true if oneof case is `kFoo`. -- `const Bar& foo() const`: Returns the current value of the field if oneof - case is `kFoo`. Otherwise, returns a Bar with none of its fields set +- `bool has_bar() const`: Returns true if oneof case is `kBar`. +- `const Bar& bar() const`: Returns the current value of the field if oneof + case is `kBar`. Otherwise, returns a Bar with none of its fields set (possibly `Bar::default_instance()`). -- `Bar* mutable_foo()`: +- `Bar* mutable_bar()`: - If any other oneof field in the same oneof is set, calls `clear_example_name()`. - - Sets the oneof case to `kFoo` and returns a pointer to the mutable Bar - object that stores the field's value. If the oneof case was not `kFoo` + - Sets the oneof case to `kBar` and returns a pointer to the mutable Bar + object that stores the field's value. If the oneof case was not `kBar` prior to the call, then the returned Bar will have none of its fields set (i.e. it will be identical to a newly-allocated Bar). - - After calling this, `has_foo()` will return `true`, `foo()` will return + - After calling this, `has_bar()` will return `true`, `bar()` will return a reference to the same instance of `Bar` and `example_name_case()` will - return `kFoo`. -- `void clear_foo()`: - - Nothing will be changed if the oneof case is not `kFoo`. - - If the oneof case equals `kFoo`, frees the field and clears the oneof - case. `has_foo()` will return `false`, `foo()` will return the default + return `kBar`. +- `void clear_bar()`: + - Nothing will be changed if the oneof case is not `kBar`. + - If the oneof case equals `kBar`, frees the field and clears the oneof + case. `has_bar()` will return `false`, `bar()` will return the default value and `example_name_case()` will return `EXAMPLE_NAME_NOT_SET`. -- `void set_allocated_foo(Bar* bar)`: +- `void set_allocated_bar(Bar* bar)`: - Calls `clear_example_name()`. - If the `Bar` pointer is not `NULL`: Sets the `Bar` object to the field - and sets the oneof case to `kFoo`. The message takes ownership of the - allocated `Bar` object, has_foo() will return true and - `example_name_case()` will return `kFoo`. - - If the pointer is `NULL`, `has_foo()` will return `false` and + and sets the oneof case to `kBar`. The message takes ownership of the + allocated `Bar` object, has_bar() will return true and + `example_name_case()` will return `kBar`. + - If the pointer is `NULL`, `has_bar()` will return `false` and `example_name_case()` will return `EXAMPLE_NAME_NOT_SET`. (The behavior is like calling `clear_example_name()`) -- `Bar* release_foo()`: - - Returns `NULL` if oneof case is not `kFoo`. - - If the oneof case is `kFoo`, clears the oneof case, releases the +- `Bar* release_bar()`: + - Returns `NULL` if oneof case is not `kBar`. + - If the oneof case is `kBar`, clears the oneof case, releases the ownership of the field and returns the pointer of the `Bar` object. After calling this, caller takes the ownership of the allocated `Bar` - object, `has_foo()` will return `false`, `foo()` will return the default + object, `has_bar()` will return `false`, `bar()` will return the default value and `example_name_case()` will return `EXAMPLE_NAME_NOT_SET`. ### Map Fields {#map} diff --git a/content/reference/go/size.md b/content/reference/go/size.md index d04ae4959..a4b0868de 100644 --- a/content/reference/go/size.md +++ b/content/reference/go/size.md @@ -44,8 +44,8 @@ quick fix to your pipeline: ```go {highlight="context:1,proto.Size,1"} func (*beamFn) ProcessElement(key string, value []byte, emit func(proto.Message)) { task := produceWorkTask(value) - if proto.Size(task) > 100 * 1024 * 1024 { - // Skip every work task over 100 MB to not overwhelm + if proto.Size(task) > 500 * 1024 * 1024 { + // Skip every work task over 500 MB to not overwhelm // the brittle downstream system. return } diff --git a/content/reference/protobuf/proto2-spec.md b/content/reference/protobuf/proto2-spec.md index ea5264bb5..6d85415b6 100644 --- a/content/reference/protobuf/proto2-spec.md +++ b/content/reference/protobuf/proto2-spec.md @@ -272,6 +272,9 @@ extensions 100 to 199; extensions 4, 20 to max; ``` +For more on this topic, see +[Extension Declarations](/programming-guides/extension_declarations). + ### Reserved Reserved declares a range of field numbers or field names in a message that can