This is an automated email from the ASF dual-hosted git repository.

ButterBright pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git


The following commit(s) were added to refs/heads/main by this push:
     new 152fa41f8 Fix block metadata reset before unmarshal for stream and 
trace (#1195)
152fa41f8 is described below

commit 152fa41f89aca1a97c73e692587f37d6ee96d68e
Author: Huang Youliang <[email protected]>
AuthorDate: Wed Jul 1 20:49:58 2026 +0800

    Fix block metadata reset before unmarshal for stream and trace (#1195)
    
    * Fix block metadata reset before unmarshal
---
 CHANGES.md                             |  1 +
 banyand/measure/block_metadata_test.go | 32 +++++++++++++++++++++++++++++++
 banyand/stream/block_metadata.go       |  2 ++
 banyand/stream/block_metadata_test.go  | 35 ++++++++++++++++++++++++++++++++++
 banyand/stream/block_scanner_test.go   |  1 +
 banyand/stream/part_iter_test.go       |  2 ++
 banyand/stream/query_by_idx_test.go    |  1 +
 banyand/stream/tstable_test.go         |  1 +
 banyand/trace/block_metadata.go        |  2 ++
 banyand/trace/block_metadata_test.go   | 34 +++++++++++++++++++++++++++++++++
 10 files changed, 111 insertions(+)

diff --git a/CHANGES.md b/CHANGES.md
index 6182ca9df..fb123ea32 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -124,6 +124,7 @@ Release Notes.
 - Fix lifecycle row-replay OOM on large measure parts by streaming the dump 
reader, pooling size-classed marshal buffers, and bounding in-flight batch 
bytes (default 32 MiB); peak heap drops ~1.5 GB→~296 MB.
 - Consolidate lifecycle migration report errors into a single flat list of 
structured, stage-aware entries.
 - Fix backup container OOM from overlapping scheduled runs; serialize runs and 
upload small snapshot files concurrently.
+- Fix block metadata reset before unmarshal for stream and trace.
 
 ### Document
 
diff --git a/banyand/measure/block_metadata_test.go 
b/banyand/measure/block_metadata_test.go
index 3ea72f491..dbb514113 100644
--- a/banyand/measure/block_metadata_test.go
+++ b/banyand/measure/block_metadata_test.go
@@ -376,6 +376,38 @@ func Test_unmarshalBlockMetadata(t *testing.T) {
                require.Equal(t, original, unmarshaled)
        })
 
+       t.Run("reuse destination clears stale tag families", func(t *testing.T) 
{
+               original := []blockMetadata{
+                       {
+                               seriesID:    common.SeriesID(1),
+                               tagFamilies: make(map[string]*dataBlock),
+                               timestamps: timestampsMetadata{
+                                       dataBlock: dataBlock{
+                                               offset: 1,
+                                               size:   1,
+                                       },
+                                       min:        1,
+                                       max:        1,
+                                       encodeType: encoding.EncodeTypeConst,
+                               },
+                       },
+               }
+
+               var marshaled []byte
+               for _, bm := range original {
+                       marshaled = bm.marshal(marshaled)
+               }
+
+               unmarshaled, err := unmarshalBlockMetadata(nil, marshaled)
+               require.NoError(t, err)
+               require.Len(t, unmarshaled, 1)
+
+               unmarshaled[0].tagFamilies = map[string]*dataBlock{"stale": 
{offset: 99, size: 99}}
+               reused, reuseErr := unmarshalBlockMetadata(unmarshaled[:0], 
marshaled)
+               require.NoError(t, reuseErr)
+               require.Equal(t, original, reused)
+       })
+
        t.Run("unmarshal invalid blockMetadata", func(t *testing.T) {
                original := []blockMetadata{
                        {
diff --git a/banyand/stream/block_metadata.go b/banyand/stream/block_metadata.go
index db44348f7..a8c8c3a9a 100644
--- a/banyand/stream/block_metadata.go
+++ b/banyand/stream/block_metadata.go
@@ -310,6 +310,7 @@ func unmarshalBlockMetadata(dst []blockMetadata, src 
[]byte) ([]blockMetadata, e
                        dst = append(dst, blockMetadata{})
                }
                bm := &dst[len(dst)-1]
+               bm.reset()
                tail, err := bm.unmarshal(src)
                if err != nil {
                        return dstOrig, fmt.Errorf("cannot unmarshal 
blockMetadata entries: %w", err)
@@ -391,6 +392,7 @@ func unmarshalBlockMetadataFiltered(dst []blockMetadata, 
src []byte, wanted []co
                                dst = append(dst, blockMetadata{})
                        }
                        bm := &dst[len(dst)-1]
+                       bm.reset()
                        tail, uErr := bm.unmarshal(src)
                        if uErr != nil {
                                return dstOrig, fmt.Errorf("cannot unmarshal 
blockMetadata entries: %w", uErr)
diff --git a/banyand/stream/block_metadata_test.go 
b/banyand/stream/block_metadata_test.go
index 64831fdfb..bb22c35d5 100644
--- a/banyand/stream/block_metadata_test.go
+++ b/banyand/stream/block_metadata_test.go
@@ -246,6 +246,7 @@ func Test_unmarshalBlockMetadata(t *testing.T) {
                original := []blockMetadata{
                        {
                                seriesID: common.SeriesID(1),
+                               tagType:  make(tagType),
                                timestamps: timestampsMetadata{
                                        dataBlock: dataBlock{
                                                offset: 1,
@@ -258,6 +259,7 @@ func Test_unmarshalBlockMetadata(t *testing.T) {
                        },
                        {
                                seriesID: common.SeriesID(2),
+                               tagType:  make(tagType),
                                timestamps: timestampsMetadata{
                                        dataBlock: dataBlock{
                                                offset: 2,
@@ -280,6 +282,39 @@ func Test_unmarshalBlockMetadata(t *testing.T) {
                require.Equal(t, original, unmarshaled)
        })
 
+       t.Run("reuse destination clears stale tag families", func(t *testing.T) 
{
+               original := []blockMetadata{
+                       {
+                               seriesID:    common.SeriesID(1),
+                               tagFamilies: make(map[string]*dataBlock),
+                               tagType:     make(tagType),
+                               timestamps: timestampsMetadata{
+                                       dataBlock: dataBlock{
+                                               offset: 1,
+                                               size:   1,
+                                       },
+                                       min:        1,
+                                       max:        1,
+                                       encodeType: encoding.EncodeTypeConst,
+                               },
+                       },
+               }
+
+               var marshaled []byte
+               for _, bm := range original {
+                       marshaled = bm.marshal(marshaled)
+               }
+
+               unmarshaled, err := unmarshalBlockMetadata(nil, marshaled)
+               require.NoError(t, err)
+               require.Len(t, unmarshaled, 1)
+
+               unmarshaled[0].tagFamilies = map[string]*dataBlock{"stale": 
{offset: 99, size: 99}}
+               reused, reuseErr := unmarshalBlockMetadata(unmarshaled[:0], 
marshaled)
+               require.NoError(t, reuseErr)
+               require.Equal(t, original, reused)
+       })
+
        t.Run("unmarshal invalid blockMetadata", func(t *testing.T) {
                original := []blockMetadata{
                        {
diff --git a/banyand/stream/block_scanner_test.go 
b/banyand/stream/block_scanner_test.go
index ac592b9d7..25f9e490b 100644
--- a/banyand/stream/block_scanner_test.go
+++ b/banyand/stream/block_scanner_test.go
@@ -187,6 +187,7 @@ func TestBlockScanner_QuotaExceeded(t *testing.T) {
                                cmpopts.IgnoreFields(blockMetadata{}, 
"timestamps"),
                                cmpopts.IgnoreFields(blockMetadata{}, 
"elementIDs"),
                                cmpopts.IgnoreFields(blockMetadata{}, 
"tagFamilies"),
+                               cmpopts.IgnoreFields(blockMetadata{}, 
"tagType"),
                                cmp.AllowUnexported(blockMetadata{}),
                        ); diff != "" {
                                t.Errorf("Unexpected blockMetadata (-got 
+want):\n%s", diff)
diff --git a/banyand/stream/part_iter_test.go b/banyand/stream/part_iter_test.go
index 6eb63da01..dc8c59e25 100644
--- a/banyand/stream/part_iter_test.go
+++ b/banyand/stream/part_iter_test.go
@@ -136,6 +136,7 @@ func Test_partIter_nextBlock(t *testing.T) {
                                        cmpopts.IgnoreFields(blockMetadata{}, 
"timestamps"),
                                        cmpopts.IgnoreFields(blockMetadata{}, 
"elementIDs"),
                                        cmpopts.IgnoreFields(blockMetadata{}, 
"tagFamilies"),
+                                       cmpopts.IgnoreFields(blockMetadata{}, 
"tagType"),
                                        cmp.AllowUnexported(blockMetadata{}),
                                ); diff != "" {
                                        t.Errorf("Unexpected blockMetadata 
(-got +want):\n%s", diff)
@@ -199,6 +200,7 @@ func Test_partMergeIter_nextBlock(t *testing.T) {
                                        cmpopts.IgnoreFields(blockMetadata{}, 
"timestamps"),
                                        cmpopts.IgnoreFields(blockMetadata{}, 
"elementIDs"),
                                        cmpopts.IgnoreFields(blockMetadata{}, 
"tagFamilies"),
+                                       cmpopts.IgnoreFields(blockMetadata{}, 
"tagType"),
                                        cmp.AllowUnexported(blockMetadata{}),
                                ); diff != "" {
                                        t.Errorf("Unexpected blockMetadata 
(-got +want):\n%s", diff)
diff --git a/banyand/stream/query_by_idx_test.go 
b/banyand/stream/query_by_idx_test.go
index 4a695bd8e..05af46e7f 100644
--- a/banyand/stream/query_by_idx_test.go
+++ b/banyand/stream/query_by_idx_test.go
@@ -127,6 +127,7 @@ func TestQueryResult_QuotaExceeded(t *testing.T) {
                                cmpopts.IgnoreFields(blockMetadata{}, 
"timestamps"),
                                cmpopts.IgnoreFields(blockMetadata{}, 
"elementIDs"),
                                cmpopts.IgnoreFields(blockMetadata{}, 
"tagFamilies"),
+                               cmpopts.IgnoreFields(blockMetadata{}, 
"tagType"),
                                cmp.AllowUnexported(blockMetadata{}),
                        ); diff != "" {
                                t.Errorf("Unexpected blockMetadata (-got 
+want):\n%s", diff)
diff --git a/banyand/stream/tstable_test.go b/banyand/stream/tstable_test.go
index ffbd31e35..7ce2dffd8 100644
--- a/banyand/stream/tstable_test.go
+++ b/banyand/stream/tstable_test.go
@@ -167,6 +167,7 @@ func Test_tstIter(t *testing.T) {
                        cmpopts.IgnoreFields(blockMetadata{}, "timestamps"),
                        cmpopts.IgnoreFields(blockMetadata{}, "elementIDs"),
                        cmpopts.IgnoreFields(blockMetadata{}, "tagFamilies"),
+                       cmpopts.IgnoreFields(blockMetadata{}, "tagType"),
                        cmp.AllowUnexported(blockMetadata{}),
                ); diff != "" {
                        t.Errorf("Unexpected blockMetadata (-got +want):\n%s", 
diff)
diff --git a/banyand/trace/block_metadata.go b/banyand/trace/block_metadata.go
index 09f884d62..ad9660717 100644
--- a/banyand/trace/block_metadata.go
+++ b/banyand/trace/block_metadata.go
@@ -266,6 +266,7 @@ func unmarshalBlockMetadata(dst []blockMetadata, src 
[]byte, tagType map[string]
                        dst = append(dst, blockMetadata{})
                }
                bm := &dst[len(dst)-1]
+               bm.reset()
                tail, err := bm.unmarshal(src, tagType)
                if err != nil {
                        return dstOrig, fmt.Errorf("cannot unmarshal 
blockMetadata entries: %w", err)
@@ -334,6 +335,7 @@ func unmarshalBlockMetadataFiltered(dst []blockMetadata, 
src []byte, tagType map
                                dst = append(dst, blockMetadata{})
                        }
                        bm := &dst[len(dst)-1]
+                       bm.reset()
                        tail, uErr := bm.unmarshal(src, tagType)
                        if uErr != nil {
                                return dstOrig, fmt.Errorf("cannot unmarshal 
blockMetadata entries: %w", uErr)
diff --git a/banyand/trace/block_metadata_test.go 
b/banyand/trace/block_metadata_test.go
index a8dc829de..d100d6e47 100644
--- a/banyand/trace/block_metadata_test.go
+++ b/banyand/trace/block_metadata_test.go
@@ -207,6 +207,7 @@ func Test_unmarshalBlockMetadata(t *testing.T) {
                wanted := []blockMetadata{
                        {
                                traceID: "trace1",
+                               tags:    make(map[string]*dataBlock),
                                tagType: make(map[string]pbv1.ValueType),
                                spans: &dataBlock{
                                        offset: 10,
@@ -218,6 +219,7 @@ func Test_unmarshalBlockMetadata(t *testing.T) {
                        },
                        {
                                traceID: "trace2",
+                               tags:    make(map[string]*dataBlock),
                                tagType: make(map[string]pbv1.ValueType),
                                spans: &dataBlock{
                                        offset: 30,
@@ -240,6 +242,38 @@ func Test_unmarshalBlockMetadata(t *testing.T) {
                require.Equal(t, wanted, unmarshaled)
        })
 
+       t.Run("reuse destination clears stale tags", func(t *testing.T) {
+               original := []blockMetadata{
+                       {
+                               traceID: "trace1",
+                               tags:    make(map[string]*dataBlock),
+                               tagType: make(map[string]pbv1.ValueType),
+                               spans: &dataBlock{
+                                       offset: 10,
+                                       size:   20,
+                               },
+                               timestamps:                timestampsMetadata{},
+                               uncompressedSpanSizeBytes: 100,
+                               count:                     1,
+                       },
+               }
+
+               var marshaled []byte
+               for _, bm := range original {
+                       marshaled = bm.marshal(marshaled)
+               }
+
+               tagType := make(map[string]pbv1.ValueType)
+               unmarshaled, err := unmarshalBlockMetadata(nil, marshaled, 
tagType)
+               require.NoError(t, err)
+               require.Equal(t, original, unmarshaled)
+
+               unmarshaled[0].tags["stale"] = &dataBlock{offset: 99, size: 99}
+               reused, reuseErr := unmarshalBlockMetadata(unmarshaled[:0], 
marshaled, tagType)
+               require.NoError(t, reuseErr)
+               require.Equal(t, original, reused)
+       })
+
        t.Run("unmarshal invalid blockMetadata", func(t *testing.T) {
                original := []blockMetadata{
                        {

Reply via email to