This is an automated email from the ASF dual-hosted git repository.
airborne12 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new fa95a70656f [fix](be) Restore SNII proto field numbers to the shipped
layout (#67135)
fa95a70656f is described below
commit fa95a70656fefefdb63a6c0998a7536d5a77478f
Author: Jack <[email protected]>
AuthorDate: Thu Aug 27 16:06:26 2026 +0800
[fix](be) Restore SNII proto field numbers to the shipped layout (#67135)
### What problem does this PR solve?
Issue Number: None
Related PR: #66052
Problem Summary: SNII shipped before its metadata messages reached
upstream, and
the version upstreamed in #66052 had INSERTED fields in the middle of
two of
them rather than appending. protobuf identifies a field by its tag, so
the two
layouts now disagree about what a tag means, and every SNII segment
already
written decodes incorrectly under the upstream numbering:
SniiStatsPB shipped upstream (#66052)
tag 4 null_count sum_total_term_freq
tag 5 -- null_count
SniiSectionRefsPB shipped upstream (#66052)
tag 3 null_bitmap norms
tag 4 bsbf null_bitmap
tag 5 -- bsbf
Neither misread fails. SniiStatsPB tags 4 and 5 are both uint64, so a
null count
is returned as the collection token sum and feeds avgdl -- a plausible
wrong
number with no error anywhere. In SniiSectionRefsPB the null bitmap's
offset/length is taken for the norms region and bsbf is lost entirely.
This PR renumbers both messages back to the shipped layout and appends
the two
upstream additions after it: sum_total_term_freq becomes 5 and norms
becomes 5
in their respective messages. SniiCommonGramsMetadataPB needed no change
-- its
upstream fields 8-12 were appended, which is the shape every future
addition
must follow.
A new case set asserts the field numbers through protobuf reflection
rather than
through a byte digest, so the next accidental insert fails with a
message naming
the field and both tags instead of an opaque checksum mismatch.
The twelve SniiWriterGoldenBytes digests were RE-HARVESTED: field tags
and the
field-number ordering are part of the serialized Core metadata that
every SNII
segment carries, so all of them moved -- including
kGoldenKeywordDocsOnly, whose
index has neither norms nor a null bitmap. That one moving is the
evidence the
change reaches every segment shape rather than one lane.
COMPATIBILITY: this changes how existing bytes decode, deliberately.
Segments
written by an upstream build carrying #66052 are not readable afterwards
and
must be rebuilt; the format is not yet released upstream, so that
population is
development data only. Segments written by the shipped build become
readable,
which is the point.
---
.../snii/format/snii_proto_field_numbers_test.cpp | 103 +++++++++++++++++++++
.../snii/writer/snii_writer_golden_bytes_test.cpp | 17 ++--
gensrc/proto/snii.proto | 20 +++-
3 files changed, 129 insertions(+), 11 deletions(-)
diff --git
a/be/test/storage/index/snii/format/snii_proto_field_numbers_test.cpp
b/be/test/storage/index/snii/format/snii_proto_field_numbers_test.cpp
new file mode 100644
index 00000000000..5f14db77dc5
--- /dev/null
+++ b/be/test/storage/index/snii/format/snii_proto_field_numbers_test.cpp
@@ -0,0 +1,103 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// SNII core metadata field numbers are ON DISK.
+//
+// protobuf identifies a field by its tag, never by its name, so renaming is
free
+// and RENUMBERING silently changes what an existing segment decodes to. The
SNII
+// format shipped before these messages reached upstream; the shipped layout is
+// the one every existing segment was written with, and it is what these cases
+// pin. Two of the fields are especially unforgiving:
+//
+// SniiStatsPB tag 4 is null_count. Upstream had inserted sum_total_term_freq
+// there. Both are uint64, so a mismatched read yields a plausible wrong
number
+// -- a null count used as a token sum, which then feeds avgdl -- with no
error
+// anywhere.
+//
+// SniiSectionRefsPB tags 3 and 4 are null_bitmap and bsbf. Upstream had
+// inserted norms at 3, which makes a reader take the null bitmap's
+// offset/length for the norms region and lose bsbf entirely.
+//
+// APPEND new fields. Never insert, never renumber, never reuse.
+
+#include <gen_cpp/snii.pb.h>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+namespace doris::snii {
+namespace {
+
+void expect_field_numbers(const google::protobuf::Descriptor* descriptor,
+ const std::vector<std::pair<std::string, int>>&
expected) {
+ ASSERT_NE(descriptor, nullptr);
+ for (const auto& [name, number] : expected) {
+ const auto* field = descriptor->FindFieldByName(name);
+ ASSERT_NE(field, nullptr) << descriptor->full_name() << " lost field
'" << name << "'";
+ EXPECT_EQ(field->number(), number)
+ << descriptor->full_name() << "." << name << " moved from tag
" << number << " to "
+ << field->number() << "; existing segments decode this tag as "
+ << "something else";
+ }
+}
+
+} // namespace
+
+TEST(SniiProtoFieldNumbers, StatsMatchesTheShippedLayout) {
+ expect_field_numbers(SniiStatsPB::descriptor(), {{"doc_count", 1},
+ {"indexed_doc_count", 2},
+ {"term_count", 3},
+ {"null_count", 4},
+ {"sum_total_term_freq",
5}});
+}
+
+TEST(SniiProtoFieldNumbers, SectionRefsMatchTheShippedLayout) {
+ expect_field_numbers(SniiSectionRefsPB::descriptor(), {{"dict_region", 1},
+ {"posting_region",
2},
+ {"null_bitmap", 3},
+ {"bsbf", 4},
+ {"norms", 5}});
+}
+
+TEST(SniiProtoFieldNumbers, CoreMetadataMatchesTheShippedLayout) {
+ expect_field_numbers(SniiCoreMetadataPB::descriptor(), {{"index_config",
1},
+ {"stats", 2},
+ {"section_refs",
3},
+ {"common_grams",
4},
+
{"common_grams_posting_policy", 5}});
+}
+
+// The CommonGrams block was APPENDED upstream (tags 8-12 were free in the
+// shipped layout), which is the shape every future addition must copy.
+TEST(SniiProtoFieldNumbers, CommonGramsMetadataAppendsRatherThanInserts) {
+ expect_field_numbers(SniiCommonGramsMetadataPB::descriptor(),
+ {{"plain_term_key_version", 1},
+ {"common_grams_coverage", 2},
+ {"common_grams_semantics_version", 3},
+ {"common_grams_key_version", 4},
+ {"common_grams_dictionary_identity", 5},
+ {"base_analyzer_fingerprint", 6},
+ {"common_grams_fingerprint", 7},
+ {"scoring_coverage", 8},
+ {"scoring_stats_version", 9},
+ {"norm_semantics_version", 10},
+ {"scoring_doc_count", 11},
+ {"scoring_token_count", 12}});
+}
+
+} // namespace doris::snii
diff --git
a/be/test/storage/index/snii/writer/snii_writer_golden_bytes_test.cpp
b/be/test/storage/index/snii/writer/snii_writer_golden_bytes_test.cpp
index a03e823d211..e7b19ac1c41 100644
--- a/be/test/storage/index/snii/writer/snii_writer_golden_bytes_test.cpp
+++ b/be/test/storage/index/snii/writer/snii_writer_golden_bytes_test.cpp
@@ -31,6 +31,11 @@
// If a digest changes INTENTIONALLY (format or analyzer change), re-harvest by
// running the test and copying the "actual=" value from the failure message --
// and say so loudly in the commit message.
+//
+// RE-HARVESTED when SniiStatsPB and SniiSectionRefsPB were renumbered back to
the
+// field numbers the format shipped with. Protobuf tags are part of the image,
so
+// EVERY digest moved -- including kGoldenKeywordDocsOnly, which is what tells
you
+// the change reaches all SNII segments and not just one lane.
#include <gtest/gtest.h>
@@ -303,9 +308,9 @@ private:
// Whole-image digests re-harvested for the protobuf v1 metadata layout. They
// still pin posting bytes together with every framing, directory, and metadata
// byte, so future format changes remain explicit.
-constexpr uint64_t kGoldenEnglishPhrase = 0x0adb7bf49bed5dc2ULL;
-constexpr uint64_t kGoldenUnicodePhrase = 0x2ea85ae3a736665cULL;
-constexpr uint64_t kGoldenKeywordDocsOnly = 0xcdfd89278e7a7979ULL;
+constexpr uint64_t kGoldenEnglishPhrase = 0x21fa508c4b24585eULL;
+constexpr uint64_t kGoldenUnicodePhrase = 0xf3132d01603c7613ULL;
+constexpr uint64_t kGoldenKeywordDocsOnly = 0x45e45e6f7b81c65aULL;
TEST_F(SniiWriterGoldenBytes, EnglishPhrase) {
const TabletIndex meta =
@@ -385,9 +390,9 @@ TEST_F(SniiWriterGoldenBytes,
PostingShapeMatrixCompleteImageDigest) {
"df-8192", "recut-full", "recut-tail", "docs-only",
};
constexpr std::array<uint64_t, 9> expected = {
- 0x3d00d59799c7d0adULL, 0x1ae78d4f5bcfe8b9ULL,
0x2b6f0cf4ba73bfb0ULL,
- 0xc3e82019faf77965ULL, 0x8152796902268ea2ULL,
0xe9219cd6881137a9ULL,
- 0xc7da487463843f7aULL, 0xc65096369e752f3eULL,
0x70e35f7c3b9c42a1ULL,
+ 0x5c9d83ad451240d5ULL, 0xbc57a5703b9ad0b2ULL,
0x41bbd0018cad67eeULL,
+ 0x5fd78aaa9e3d2387ULL, 0x19fb79ed197b52b1ULL,
0x53d66eb1b705907cULL,
+ 0xdae9ed00bf186842ULL, 0x72e87672c20f8cafULL,
0xc86fb412bf9c7686ULL,
};
for (size_t i = 0; i < images.size(); ++i) {
const uint64_t actual = fnv1a64(images[i].bytes);
diff --git a/gensrc/proto/snii.proto b/gensrc/proto/snii.proto
index 64a0ed4b419..e28aa0fe5cd 100644
--- a/gensrc/proto/snii.proto
+++ b/gensrc/proto/snii.proto
@@ -81,12 +81,18 @@ message SniiCoreMetadataPB {
optional uint32 common_grams_posting_policy = 5;
}
+// FIELD NUMBERS ARE ON DISK. The SNII format shipped before this message
reached
+// upstream, and those segments put null_count at 4. Upstream later inserted
+// sum_total_term_freq at 4 and pushed null_count to 5, so the two layouts
+// disagree on what tag 4 means -- and both are uint64, so a mismatched read
+// returns a plausible wrong number instead of failing. Numbering here follows
+// the shipped layout; append new fields, never insert.
message SniiStatsPB {
optional uint64 doc_count = 1;
optional uint64 indexed_doc_count = 2;
optional uint64 term_count = 3;
- optional uint64 sum_total_term_freq = 4;
- optional uint64 null_count = 5;
+ optional uint64 null_count = 4;
+ optional uint64 sum_total_term_freq = 5;
}
message SniiRegionRefPB {
@@ -94,12 +100,16 @@ message SniiRegionRefPB {
optional uint64 length = 2;
}
+// FIELD NUMBERS ARE ON DISK -- see the note on SniiStatsPB. Shipped segments
put
+// null_bitmap at 3 and bsbf at 4; upstream inserted norms at 3 and shifted
both,
+// which makes a reader take the null bitmap's offset/length for the norms
region
+// and lose bsbf entirely. Numbering here follows the shipped layout.
message SniiSectionRefsPB {
optional SniiRegionRefPB dict_region = 1;
optional SniiRegionRefPB posting_region = 2;
- optional SniiRegionRefPB norms = 3;
- optional SniiRegionRefPB null_bitmap = 4;
- optional SniiRegionRefPB bsbf = 5;
+ optional SniiRegionRefPB null_bitmap = 3;
+ optional SniiRegionRefPB bsbf = 4;
+ optional SniiRegionRefPB norms = 5;
}
message SniiCommonGramsMetadataPB {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]