From 9f35e3855ab7882b0fe895bbf86cf1bfeb1e55f9 Mon Sep 17 00:00:00 2001 From: Dmitry Kropachev Date: Mon, 27 Jan 2025 10:24:11 -0400 Subject: [PATCH] Fix USING TIMEOUT time generation Scylla does not support fractions. We need to make sure that time is formatted the following way: XmYsZms --- qb/using.go | 2 +- qb/using_test.go | 5 ++++ qb/utils.go | 26 +++++++++++++++++++++ qb/utils_test.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 qb/utils_test.go diff --git a/qb/using.go b/qb/using.go index 7e4cbe00..32a199e9 100644 --- a/qb/using.go +++ b/qb/using.go @@ -94,7 +94,7 @@ func (u *using) writeCql(cql *bytes.Buffer) (names []string) { if u.timeout != 0 { writePreamble(cql) - fmt.Fprintf(cql, "TIMEOUT %s ", u.timeout) + fmt.Fprintf(cql, "TIMEOUT %s ", formatDuration(u.timeout)) } else if u.timeoutName != "" { writePreamble(cql) cql.WriteString("TIMEOUT ? ") diff --git a/qb/using_test.go b/qb/using_test.go index fe856b16..69c26656 100644 --- a/qb/using_test.go +++ b/qb/using_test.go @@ -57,6 +57,11 @@ func TestUsing(t *testing.T) { B: new(using).Timeout(time.Second), S: "USING TIMEOUT 1s ", }, + // Timeout faction + { + B: new(using).Timeout(time.Second + 100*time.Millisecond), + S: "USING TIMEOUT 1s100ms ", + }, // TimeoutNamed { B: new(using).TimeoutNamed("to"), diff --git a/qb/utils.go b/qb/utils.go index ffa4ab47..8ffc2a5c 100644 --- a/qb/utils.go +++ b/qb/utils.go @@ -6,6 +6,9 @@ package qb import ( "bytes" + "fmt" + "strings" + "time" ) // placeholders returns a string with count ? placeholders joined with commas. @@ -31,3 +34,26 @@ func (cols columns) writeCql(cql *bytes.Buffer) { } } } + +func formatDuration(d time.Duration) string { + // Round the duration to the nearest millisecond + // Extract hours, minutes, seconds, and milliseconds + minutes := d / time.Minute + d %= time.Minute + seconds := d / time.Second + d %= time.Second + milliseconds := d / time.Millisecond + + // Format the duration string + var res []string + if minutes > 0 { + res = append(res, fmt.Sprintf("%dm", minutes)) + } + if seconds > 0 { + res = append(res, fmt.Sprintf("%ds", seconds)) + } + if milliseconds > 0 { + res = append(res, fmt.Sprintf("%dms", milliseconds)) + } + return strings.Join(res, "") +} diff --git a/qb/utils_test.go b/qb/utils_test.go new file mode 100644 index 00000000..d11afb76 --- /dev/null +++ b/qb/utils_test.go @@ -0,0 +1,61 @@ +package qb + +import ( + "testing" + "time" +) + +func TestFormatDuration(t *testing.T) { + tests := []struct { + name string + input time.Duration + expected string + }{ + { + name: "Zero duration", + input: 0, + expected: "", + }, + { + input: 500 * time.Millisecond, + expected: "500ms", + }, + { + input: 10 * time.Second, + expected: "10s", + }, + { + input: 3 * time.Minute, + expected: "3m", + }, + { + input: (2 * time.Minute) + (30 * time.Second), + expected: "2m30s", + }, + { + input: (15 * time.Second) + (250 * time.Millisecond), + expected: "15s250ms", + }, + { + input: (1 * time.Minute) + (45 * time.Second) + (123 * time.Millisecond), + expected: "1m45s123ms", + }, + { + input: (5 * time.Minute) + (1 * time.Second) + (999 * time.Millisecond), + expected: "5m1s999ms", + }, + { + input: (2 * time.Second) + (1500 * time.Millisecond), // 3 seconds, 500ms + expected: "3s500ms", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := formatDuration(tt.input) + if actual != tt.expected { + t.Errorf("got %q, want %q", actual, tt.expected) + } + }) + } +}