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

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

commit 86d4d3dd1c07a0c4a699bb2139379ef8295336b5
Author: Hongtao Gao <[email protected]>
AuthorDate: Tue Apr 7 00:55:38 2026 +0000

    feat(storage): write JSON metadata with endTime on segment creation
---
 banyand/internal/storage/segment.go      | 10 ++++-
 banyand/internal/storage/segment_test.go | 66 ++++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/banyand/internal/storage/segment.go 
b/banyand/internal/storage/segment.go
index f9ad6a122..4de7ab50b 100644
--- a/banyand/internal/storage/segment.go
+++ b/banyand/internal/storage/segment.go
@@ -19,6 +19,7 @@ package storage
 
 import (
        "context"
+       "encoding/json"
        "fmt"
        "io/fs"
        "path"
@@ -539,7 +540,14 @@ func (sc *segmentController[T, O]) create(start time.Time) 
(*segment[T, O], erro
        }
        segPath := path.Join(sc.location, fmt.Sprintf(segTemplate, 
sc.format(start)))
        sc.lfs.MkdirPanicIfExist(segPath, DirPerm)
-       data := []byte(currentVersion)
+       meta := segmentMeta{
+               Version: currentVersion,
+               EndTime: end.Format(time.RFC3339Nano),
+       }
+       data, marshalErr := json.Marshal(meta)
+       if marshalErr != nil {
+               logger.Panicf("cannot marshal segment metadata: %s", marshalErr)
+       }
        metadataPath := filepath.Join(segPath, metadataFilename)
        lf, err := sc.lfs.CreateLockFile(metadataPath, FilePerm)
        if err != nil {
diff --git a/banyand/internal/storage/segment_test.go 
b/banyand/internal/storage/segment_test.go
index 7bcc1063b..85f28c259 100644
--- a/banyand/internal/storage/segment_test.go
+++ b/banyand/internal/storage/segment_test.go
@@ -641,3 +641,69 @@ func TestDeleteExpiredSegmentsWithClosedSegments(t 
*testing.T) {
                        "Remaining segment %d should be from the expected 
date", i)
        }
 }
+
+func TestCreateSegmentWritesJSONMetadata(t *testing.T) {
+       tempDir, cleanup := setupTestEnvironment(t)
+       defer cleanup()
+
+       ctx := context.Background()
+       l := logger.GetLogger("test-segment-metadata")
+       ctx = context.WithValue(ctx, logger.ContextKey, l)
+       ctx = common.SetPosition(ctx, func(_ common.Position) common.Position {
+               return common.Position{
+                       Database: "test-db",
+                       Stage:    "test-stage",
+               }
+       })
+
+       opts := TSDBOpts[mockTSTable, mockTSTableOpener]{
+               TSTableCreator: func(_ fs.FileSystem, _ string, _ 
common.Position, _ *logger.Logger,
+                       _ timestamp.TimeRange, _ mockTSTableOpener, _ any,
+               ) (mockTSTable, error) {
+                       return mockTSTable{ID: common.ShardID(0)}, nil
+               },
+               ShardNum:                       1,
+               SegmentInterval:                IntervalRule{Unit: DAY, Num: 1},
+               TTL:                            IntervalRule{Unit: DAY, Num: 7},
+               SeriesIndexFlushTimeoutSeconds: 10,
+               SeriesIndexCacheMaxBytes:       1024 * 1024,
+       }
+
+       serviceCache := NewServiceCache().(*serviceCache)
+       sc := newSegmentController[mockTSTable, mockTSTableOpener](
+               ctx,
+               tempDir,
+               l,
+               opts,
+               nil,
+               nil,
+               5*time.Minute,
+               
fs.NewLocalFileSystemWithLoggerAndLimit(logger.GetLogger("storage"), 
opts.MemoryLimit),
+               serviceCache,
+               group,
+       )
+
+       now := time.Now().UTC()
+       startTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, 
time.UTC)
+
+       seg, createErr := sc.create(startTime)
+       require.NoError(t, createErr)
+       require.NotNil(t, seg)
+
+       // Read metadata from disk and verify it's JSON with endTime
+       suffix := startTime.Format(dayFormat)
+       metadataPath := filepath.Join(tempDir, fmt.Sprintf("seg-%s", suffix), 
metadataFilename)
+       rawMeta, readErr := os.ReadFile(metadataPath)
+       require.NoError(t, readErr)
+
+       meta, parseErr := readSegmentMeta(rawMeta)
+       require.NoError(t, parseErr)
+       assert.Equal(t, currentVersion, meta.Version)
+       assert.NotEmpty(t, meta.EndTime, "endTime should be persisted in 
metadata")
+
+       // Verify the endTime matches the segment's End
+       expectedEnd := startTime.Add(24 * time.Hour)
+       assert.Equal(t, expectedEnd.Format(time.RFC3339Nano), meta.EndTime)
+
+       seg.DecRef()
+}

Reply via email to