From 2ca2e2dbc84a105bd1e25326795e8e5ca8009302 Mon Sep 17 00:00:00 2001 From: "yihao.dai" Date: Mon, 10 Mar 2025 17:38:04 +0800 Subject: [PATCH] fix: Fix parsing import endTs (#40332) Parsing import beginTs, endTs as a hybrid timestamp. issue: https://github.com/milvus-io/milvus/issues/40326 --------- Signed-off-by: bigsheeper --- internal/util/importutilv2/option.go | 7 +++-- internal/util/importutilv2/option_test.go | 37 +++++++++++++++++++++++ pkg/util/tsoutil/tso.go | 13 ++++++++ pkg/util/tsoutil/tso_test.go | 10 ++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/internal/util/importutilv2/option.go b/internal/util/importutilv2/option.go index a9848ab2d2..ffab4fc924 100644 --- a/internal/util/importutilv2/option.go +++ b/internal/util/importutilv2/option.go @@ -83,11 +83,14 @@ func ParseTimeRange(options Options) (uint64, uint64, error) { for _, targetKey := range targetKeys { for key, value := range importOptions { if strings.EqualFold(key, targetKey) { - pTs, err := strconv.ParseInt(value, 10, 64) + ts, err := strconv.ParseUint(value, 10, 64) if err != nil { return 0, merr.WrapErrImportFailed(fmt.Sprintf("parse %s failed, value=%s, err=%s", targetKey, value, err)) } - return tsoutil.ComposeTS(pTs, 0), nil + if !tsoutil.IsValidHybridTs(ts) { + return 0, merr.WrapErrImportFailed(fmt.Sprintf("%s is not a valid hybrid timestamp, value=%s", targetKey, value)) + } + return ts, nil } } } diff --git a/internal/util/importutilv2/option_test.go b/internal/util/importutilv2/option_test.go index 27f3438d50..c9ac084600 100644 --- a/internal/util/importutilv2/option_test.go +++ b/internal/util/importutilv2/option_test.go @@ -17,12 +17,15 @@ package importutilv2 import ( + "fmt" + "math" "testing" "time" "github.com/stretchr/testify/assert" "github.com/milvus-io/milvus-proto/go-api/v2/commonpb" + "github.com/milvus-io/milvus/pkg/v2/util/merr" "github.com/milvus-io/milvus/pkg/v2/util/tsoutil" ) @@ -51,3 +54,37 @@ func TestOption_GetTimeout(t *testing.T) { _, err = GetTimeoutTs(options) assert.Error(t, err) } + +func TestOption_ParseTimeRange(t *testing.T) { + s, e, err := ParseTimeRange(nil) + assert.NoError(t, err) + assert.Equal(t, uint64(0), s) + assert.Equal(t, uint64(math.MaxUint64), e) + + startTs := tsoutil.GetCurrentTime() + options := []*commonpb.KeyValuePair{{Key: StartTs, Value: fmt.Sprintf("%d", startTs)}} + s, e, err = ParseTimeRange(options) + assert.NoError(t, err) + assert.Equal(t, startTs, s) + assert.Equal(t, uint64(math.MaxUint64), e) + + endTs := tsoutil.GetCurrentTime() + options = []*commonpb.KeyValuePair{{Key: EndTs, Value: fmt.Sprintf("%d", endTs)}} + s, e, err = ParseTimeRange(options) + assert.NoError(t, err) + assert.Equal(t, uint64(0), s) + assert.Equal(t, endTs, e) + + options = []*commonpb.KeyValuePair{{Key: EndTs, Value: "&%#$%^&%^&$%^&&"}} + _, _, err = ParseTimeRange(options) + assert.ErrorIs(t, err, merr.ErrImportFailed) + + physicalTs := time.Now().UnixMilli() + options = []*commonpb.KeyValuePair{{Key: EndTs, Value: fmt.Sprintf("%d", physicalTs)}} + _, _, err = ParseTimeRange(options) + assert.ErrorIs(t, err, merr.ErrImportFailed) + + options = []*commonpb.KeyValuePair{{Key: StartTs, Value: "0"}} + _, _, err = ParseTimeRange(options) + assert.ErrorIs(t, err, merr.ErrImportFailed) +} diff --git a/pkg/util/tsoutil/tso.go b/pkg/util/tsoutil/tso.go index 9785c1f695..bdb1904007 100644 --- a/pkg/util/tsoutil/tso.go +++ b/pkg/util/tsoutil/tso.go @@ -99,3 +99,16 @@ func SubByNow(ts uint64) int64 { func PhysicalTimeFormat(ts uint64) string { return PhysicalTime(ts).Format(time.DateTime) } + +const ( + minUnixMillis = 1546300800000 // 2019-01-01 00:00:00 UTC + maxUnixMillis = 253402300799000 // 9999-12-31 23:59:59 UTC +) + +func IsValidPhysicalTs(t uint64) bool { + return t >= minUnixMillis && t <= maxUnixMillis +} + +func IsValidHybridTs(t uint64) bool { + return IsValidPhysicalTs(t >> logicalBits) +} diff --git a/pkg/util/tsoutil/tso_test.go b/pkg/util/tsoutil/tso_test.go index 5e0c1f57ba..0772f00089 100644 --- a/pkg/util/tsoutil/tso_test.go +++ b/pkg/util/tsoutil/tso_test.go @@ -70,3 +70,13 @@ func TestAddPhysicalDurationOnTs(t *testing.T) { // diff := CalculateDuration(ts2, ts1) assert.Equal(t, ts3, ts2) } + +func TestIsValidUnixMilli(t *testing.T) { + physicalTs := uint64(time.Now().UnixMilli()) + assert.True(t, IsValidPhysicalTs(physicalTs)) + assert.False(t, IsValidHybridTs(physicalTs)) + + hybridTs := GetCurrentTime() + assert.False(t, IsValidPhysicalTs(hybridTs)) + assert.True(t, IsValidHybridTs(hybridTs)) +}