mirror of
https://github.com/SigNoz/signoz.git
synced 2026-11-13 11:50:42 +00:00
Compare commits
4 Commits
main
...
tvats-offs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0a908e15c | ||
|
|
f3bd3efe85 | ||
|
|
58e7bcf29b | ||
|
|
447c9c0865 |
@@ -31,3 +31,12 @@ packages:
|
||||
filename: "tokenizer.go"
|
||||
structname: 'Mock{{.InterfaceName}}'
|
||||
pkgname: '{{.SrcPackageName}}test'
|
||||
github.com/ClickHouse/clickhouse-go/v2/lib/driver:
|
||||
interfaces:
|
||||
Rows:
|
||||
ColumnType:
|
||||
config:
|
||||
dir: 'pkg/telemetrystore/drivertest'
|
||||
filename: "driver.go"
|
||||
structname: 'Mock{{.InterfaceName}}'
|
||||
pkgname: '{{.SrcPackageName}}test'
|
||||
|
||||
@@ -457,6 +457,13 @@ func emptyResultFor(kind qbtypes.RequestType, queryName string) *qbtypes.Result
|
||||
|
||||
// executeWithContext executes the query with query window and step context for partial value detection.
|
||||
func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string, args []any) (*qbtypes.Result, error) {
|
||||
result, _, err := q.executeSkippingRaw(ctx, query, args, 0)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// executeSkippingRaw is executeWithContext for a raw query that drops its first
|
||||
// skip rows undecoded. It returns how many rows were skipped.
|
||||
func (q *builderQuery[T]) executeSkippingRaw(ctx context.Context, query string, args []any, skip int) (*qbtypes.Result, int, error) {
|
||||
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
|
||||
instrumentationtypes.TelemetrySignal: q.spec.Signal.StringValue(),
|
||||
instrumentationtypes.QueryDuration: instrumentationtypes.DurationBucket(q.fromMS, q.toMS),
|
||||
@@ -475,19 +482,19 @@ func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string,
|
||||
rows, err := q.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return nil, errors.Newf(errors.TypeTimeout, errors.CodeTimeout, "Query timed out").
|
||||
return nil, 0, errors.Newf(errors.TypeTimeout, errors.CodeTimeout, "Query timed out").
|
||||
WithAdditional("Try refining your search by adding relevant resource attributes filtering")
|
||||
}
|
||||
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
return nil, errors.Newf(
|
||||
return nil, 0, errors.Newf(
|
||||
errors.TypeInternal,
|
||||
errors.CodeInternal,
|
||||
"Something went wrong on our end. It's not you, it's us. Our team is notified about it. Reach out to support if issue persists.",
|
||||
)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
@@ -501,9 +508,15 @@ func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string,
|
||||
kind = qbtypes.RequestTypeTimeSeries
|
||||
}
|
||||
|
||||
payload, err := consume(rows, kind, queryWindow, q.spec.StepInterval, q.spec.Name)
|
||||
var payload any
|
||||
skipped := 0
|
||||
if skip > 0 {
|
||||
payload, skipped, err = readAsRawSkipping(rows, q.spec.Name, skip)
|
||||
} else {
|
||||
payload, err = consume(rows, kind, queryWindow, q.spec.StepInterval, q.spec.Name)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// TODO: This should move to readAsRaw function in consume.go but for now we are keeping it here since it's only relevant for traces
|
||||
@@ -523,7 +536,7 @@ func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string,
|
||||
BytesScanned: totalBytes,
|
||||
DurationMS: uint64(elapsed.Milliseconds()),
|
||||
},
|
||||
}, nil
|
||||
}, skipped, nil
|
||||
}
|
||||
|
||||
func (q *builderQuery[T]) executeWindowList(ctx context.Context) (*qbtypes.Result, error) {
|
||||
@@ -623,7 +636,7 @@ func (q *builderQuery[T]) executeWindowList(ctx context.Context) (*qbtypes.Resul
|
||||
}
|
||||
}
|
||||
// Execute with proper context for partial value detection
|
||||
res, err := q.executeWithContext(ctx, stmt.Query, stmt.Args)
|
||||
res, skipped, err := q.executeSkippingRaw(ctx, stmt.Query, stmt.Args, offsetLeft)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -631,13 +644,10 @@ func (q *builderQuery[T]) executeWindowList(ctx context.Context) (*qbtypes.Resul
|
||||
totalBytes += res.Stats.BytesScanned
|
||||
|
||||
rawRows := res.Value.(*qbtypes.RawData).Rows
|
||||
need -= len(rawRows)
|
||||
need -= skipped + len(rawRows)
|
||||
offsetLeft -= skipped
|
||||
|
||||
for _, rr := range rawRows {
|
||||
if offsetLeft > 0 { // client-requested initial offset
|
||||
offsetLeft--
|
||||
continue
|
||||
}
|
||||
rows = append(rows, rr)
|
||||
if len(rows) >= reqLimit { // page filled
|
||||
break
|
||||
|
||||
@@ -521,13 +521,26 @@ func derefValue(v any) any {
|
||||
}
|
||||
|
||||
func readAsRaw(rows driver.Rows, queryName string) (*qbtypes.RawData, error) {
|
||||
data, _, err := readAsRawSkipping(rows, queryName, 0)
|
||||
return data, err
|
||||
}
|
||||
|
||||
// readAsRawSkipping advances past the first skip rows without scanning them, so
|
||||
// rows dropped for an offset are never decoded. It returns how many were skipped.
|
||||
func readAsRawSkipping(rows driver.Rows, queryName string, skip int) (*qbtypes.RawData, int, error) {
|
||||
colNames := rows.Columns()
|
||||
colTypes := rows.ColumnTypes()
|
||||
colCnt := len(colNames)
|
||||
|
||||
var outRows []*qbtypes.RawRow
|
||||
skipped := 0
|
||||
|
||||
for rows.Next() {
|
||||
if skipped < skip {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
// fresh copy of the scan slice (otherwise the driver reuses pointers)
|
||||
scan := make([]any, colCnt)
|
||||
for i := range colTypes {
|
||||
@@ -535,7 +548,7 @@ func readAsRaw(rows driver.Rows, queryName string) (*qbtypes.RawData, error) {
|
||||
}
|
||||
|
||||
if err := rows.Scan(scan...); err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
rr := qbtypes.RawRow{
|
||||
@@ -567,13 +580,13 @@ func readAsRaw(rows driver.Rows, queryName string) (*qbtypes.RawData, error) {
|
||||
outRows = append(outRows, &rr)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &qbtypes.RawData{
|
||||
QueryName: queryName,
|
||||
Rows: outRows,
|
||||
}, nil
|
||||
}, skipped, nil
|
||||
}
|
||||
|
||||
// mergeSpanAttributeColumns merges (attributes_string, attributes_number, attributes_bool, resources_string) into
|
||||
|
||||
@@ -5,9 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2/lib/chcol"
|
||||
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore/drivertest"
|
||||
"github.com/SigNoz/signoz/pkg/types/spantypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrystoretypes"
|
||||
)
|
||||
@@ -254,3 +257,52 @@ func TestMergeSpanAttributeColumns_JSONColumn(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadAsRawSkipping(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
values []string
|
||||
skip int
|
||||
wantSkipped int
|
||||
wantIDs []string
|
||||
}{
|
||||
{name: "NoSkip", values: []string{"a", "b", "c"}, skip: 0, wantSkipped: 0, wantIDs: []string{"a", "b", "c"}},
|
||||
{name: "SkipInsideResult", values: []string{"d", "e", "f", "g"}, skip: 2, wantSkipped: 2, wantIDs: []string{"f", "g"}},
|
||||
{name: "SkipWholeResult", values: []string{"h", "i"}, skip: 2, wantSkipped: 2, wantIDs: nil},
|
||||
{name: "SkipPastResult", values: []string{"j"}, skip: 5, wantSkipped: 1, wantIDs: nil},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
columnType := drivertest.NewMockColumnType(t)
|
||||
columnType.EXPECT().ScanType().Return(reflect.TypeOf("")).Maybe()
|
||||
|
||||
// Like clickhouse-go, Next advances on its own; Scan is expected only for returned rows.
|
||||
rows := drivertest.NewMockRows(t)
|
||||
pos := 0
|
||||
rows.EXPECT().Columns().Return([]string{"id"})
|
||||
rows.EXPECT().ColumnTypes().Return([]driver.ColumnType{columnType})
|
||||
rows.EXPECT().Next().RunAndReturn(func() bool {
|
||||
pos++
|
||||
return pos <= len(testCase.values)
|
||||
})
|
||||
if len(testCase.wantIDs) > 0 {
|
||||
rows.EXPECT().Scan(mock.Anything).RunAndReturn(func(dest ...any) error {
|
||||
*dest[0].(*string) = testCase.values[pos-1]
|
||||
return nil
|
||||
}).Times(len(testCase.wantIDs))
|
||||
}
|
||||
rows.EXPECT().Err().Return(nil)
|
||||
|
||||
data, skipped, err := readAsRawSkipping(rows, "A", testCase.skip)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ids []string
|
||||
for _, row := range data.Rows {
|
||||
ids = append(ids, row.Data["id"].(string))
|
||||
}
|
||||
assert.Equal(t, testCase.wantSkipped, skipped)
|
||||
assert.Equal(t, testCase.wantIDs, ids)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
701
pkg/telemetrystore/drivertest/driver.go
Normal file
701
pkg/telemetrystore/drivertest/driver.go
Normal file
@@ -0,0 +1,701 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package drivertest
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewMockRows creates a new instance of MockRows. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockRows(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockRows {
|
||||
if helper, ok := t.(interface{ Helper() }); ok {
|
||||
helper.Helper()
|
||||
}
|
||||
|
||||
mock := &MockRows{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() {
|
||||
if helper, ok := t.(interface{ Helper() }); ok {
|
||||
helper.Helper()
|
||||
}
|
||||
mock.AssertExpectations(t)
|
||||
})
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// MockRows is an autogenerated mock type for the Rows type
|
||||
type MockRows struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockRows_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockRows) EXPECT() *MockRows_Expecter {
|
||||
return &MockRows_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Close provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) Close() error {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Close")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
|
||||
type MockRows_Close_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Close is a helper method to define mock.On call
|
||||
func (_e *MockRows_Expecter) Close() *MockRows_Close_Call {
|
||||
return &MockRows_Close_Call{Call: _e.mock.On("Close")}
|
||||
}
|
||||
|
||||
func (_c *MockRows_Close_Call) Run(run func()) *MockRows_Close_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Close_Call) Return(err error) *MockRows_Close_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Close_Call) RunAndReturn(run func() error) *MockRows_Close_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ColumnTypes provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) ColumnTypes() []driver.ColumnType {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ColumnTypes")
|
||||
}
|
||||
|
||||
var r0 []driver.ColumnType
|
||||
if returnFunc, ok := ret.Get(0).(func() []driver.ColumnType); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]driver.ColumnType)
|
||||
}
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_ColumnTypes_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ColumnTypes'
|
||||
type MockRows_ColumnTypes_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ColumnTypes is a helper method to define mock.On call
|
||||
func (_e *MockRows_Expecter) ColumnTypes() *MockRows_ColumnTypes_Call {
|
||||
return &MockRows_ColumnTypes_Call{Call: _e.mock.On("ColumnTypes")}
|
||||
}
|
||||
|
||||
func (_c *MockRows_ColumnTypes_Call) Run(run func()) *MockRows_ColumnTypes_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_ColumnTypes_Call) Return(columnTypes []driver.ColumnType) *MockRows_ColumnTypes_Call {
|
||||
_c.Call.Return(columnTypes)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_ColumnTypes_Call) RunAndReturn(run func() []driver.ColumnType) *MockRows_ColumnTypes_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Columns provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) Columns() []string {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Columns")
|
||||
}
|
||||
|
||||
var r0 []string
|
||||
if returnFunc, ok := ret.Get(0).(func() []string); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]string)
|
||||
}
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_Columns_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Columns'
|
||||
type MockRows_Columns_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Columns is a helper method to define mock.On call
|
||||
func (_e *MockRows_Expecter) Columns() *MockRows_Columns_Call {
|
||||
return &MockRows_Columns_Call{Call: _e.mock.On("Columns")}
|
||||
}
|
||||
|
||||
func (_c *MockRows_Columns_Call) Run(run func()) *MockRows_Columns_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Columns_Call) Return(strings []string) *MockRows_Columns_Call {
|
||||
_c.Call.Return(strings)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Columns_Call) RunAndReturn(run func() []string) *MockRows_Columns_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Err provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) Err() error {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Err")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_Err_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Err'
|
||||
type MockRows_Err_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Err is a helper method to define mock.On call
|
||||
func (_e *MockRows_Expecter) Err() *MockRows_Err_Call {
|
||||
return &MockRows_Err_Call{Call: _e.mock.On("Err")}
|
||||
}
|
||||
|
||||
func (_c *MockRows_Err_Call) Run(run func()) *MockRows_Err_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Err_Call) Return(err error) *MockRows_Err_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Err_Call) RunAndReturn(run func() error) *MockRows_Err_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// HasData provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) HasData() bool {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for HasData")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
if returnFunc, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_HasData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HasData'
|
||||
type MockRows_HasData_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// HasData is a helper method to define mock.On call
|
||||
func (_e *MockRows_Expecter) HasData() *MockRows_HasData_Call {
|
||||
return &MockRows_HasData_Call{Call: _e.mock.On("HasData")}
|
||||
}
|
||||
|
||||
func (_c *MockRows_HasData_Call) Run(run func()) *MockRows_HasData_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_HasData_Call) Return(b bool) *MockRows_HasData_Call {
|
||||
_c.Call.Return(b)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_HasData_Call) RunAndReturn(run func() bool) *MockRows_HasData_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Next provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) Next() bool {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Next")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
if returnFunc, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_Next_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Next'
|
||||
type MockRows_Next_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Next is a helper method to define mock.On call
|
||||
func (_e *MockRows_Expecter) Next() *MockRows_Next_Call {
|
||||
return &MockRows_Next_Call{Call: _e.mock.On("Next")}
|
||||
}
|
||||
|
||||
func (_c *MockRows_Next_Call) Run(run func()) *MockRows_Next_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Next_Call) Return(b bool) *MockRows_Next_Call {
|
||||
_c.Call.Return(b)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Next_Call) RunAndReturn(run func() bool) *MockRows_Next_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Scan provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) Scan(dest ...any) error {
|
||||
var tmpRet mock.Arguments
|
||||
if len(dest) > 0 {
|
||||
tmpRet = _mock.Called(dest)
|
||||
} else {
|
||||
tmpRet = _mock.Called()
|
||||
}
|
||||
ret := tmpRet
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Scan")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(...any) error); ok {
|
||||
r0 = returnFunc(dest...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_Scan_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Scan'
|
||||
type MockRows_Scan_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Scan is a helper method to define mock.On call
|
||||
// - dest ...any
|
||||
func (_e *MockRows_Expecter) Scan(dest ...any) *MockRows_Scan_Call {
|
||||
return &MockRows_Scan_Call{Call: _e.mock.On("Scan",
|
||||
append([]any{}, dest...)...)}
|
||||
}
|
||||
|
||||
func (_c *MockRows_Scan_Call) Run(run func(dest ...any)) *MockRows_Scan_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 []any
|
||||
var variadicArgs []any
|
||||
if len(args) > 0 {
|
||||
variadicArgs = args[0].([]any)
|
||||
}
|
||||
arg0 = variadicArgs
|
||||
run(
|
||||
arg0...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Scan_Call) Return(err error) *MockRows_Scan_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Scan_Call) RunAndReturn(run func(dest ...any) error) *MockRows_Scan_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ScanStruct provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) ScanStruct(dest any) error {
|
||||
ret := _mock.Called(dest)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ScanStruct")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(any) error); ok {
|
||||
r0 = returnFunc(dest)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_ScanStruct_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ScanStruct'
|
||||
type MockRows_ScanStruct_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ScanStruct is a helper method to define mock.On call
|
||||
// - dest any
|
||||
func (_e *MockRows_Expecter) ScanStruct(dest any) *MockRows_ScanStruct_Call {
|
||||
return &MockRows_ScanStruct_Call{Call: _e.mock.On("ScanStruct", dest)}
|
||||
}
|
||||
|
||||
func (_c *MockRows_ScanStruct_Call) Run(run func(dest any)) *MockRows_ScanStruct_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 any
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(any)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_ScanStruct_Call) Return(err error) *MockRows_ScanStruct_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_ScanStruct_Call) RunAndReturn(run func(dest any) error) *MockRows_ScanStruct_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Totals provides a mock function for the type MockRows
|
||||
func (_mock *MockRows) Totals(dest ...any) error {
|
||||
var tmpRet mock.Arguments
|
||||
if len(dest) > 0 {
|
||||
tmpRet = _mock.Called(dest)
|
||||
} else {
|
||||
tmpRet = _mock.Called()
|
||||
}
|
||||
ret := tmpRet
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Totals")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(...any) error); ok {
|
||||
r0 = returnFunc(dest...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockRows_Totals_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Totals'
|
||||
type MockRows_Totals_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Totals is a helper method to define mock.On call
|
||||
// - dest ...any
|
||||
func (_e *MockRows_Expecter) Totals(dest ...any) *MockRows_Totals_Call {
|
||||
return &MockRows_Totals_Call{Call: _e.mock.On("Totals",
|
||||
append([]any{}, dest...)...)}
|
||||
}
|
||||
|
||||
func (_c *MockRows_Totals_Call) Run(run func(dest ...any)) *MockRows_Totals_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 []any
|
||||
var variadicArgs []any
|
||||
if len(args) > 0 {
|
||||
variadicArgs = args[0].([]any)
|
||||
}
|
||||
arg0 = variadicArgs
|
||||
run(
|
||||
arg0...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Totals_Call) Return(err error) *MockRows_Totals_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRows_Totals_Call) RunAndReturn(run func(dest ...any) error) *MockRows_Totals_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockColumnType creates a new instance of MockColumnType. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockColumnType(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockColumnType {
|
||||
if helper, ok := t.(interface{ Helper() }); ok {
|
||||
helper.Helper()
|
||||
}
|
||||
|
||||
mock := &MockColumnType{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() {
|
||||
if helper, ok := t.(interface{ Helper() }); ok {
|
||||
helper.Helper()
|
||||
}
|
||||
mock.AssertExpectations(t)
|
||||
})
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// MockColumnType is an autogenerated mock type for the ColumnType type
|
||||
type MockColumnType struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockColumnType_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockColumnType) EXPECT() *MockColumnType_Expecter {
|
||||
return &MockColumnType_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// DatabaseTypeName provides a mock function for the type MockColumnType
|
||||
func (_mock *MockColumnType) DatabaseTypeName() string {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DatabaseTypeName")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
if returnFunc, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockColumnType_DatabaseTypeName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DatabaseTypeName'
|
||||
type MockColumnType_DatabaseTypeName_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// DatabaseTypeName is a helper method to define mock.On call
|
||||
func (_e *MockColumnType_Expecter) DatabaseTypeName() *MockColumnType_DatabaseTypeName_Call {
|
||||
return &MockColumnType_DatabaseTypeName_Call{Call: _e.mock.On("DatabaseTypeName")}
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_DatabaseTypeName_Call) Run(run func()) *MockColumnType_DatabaseTypeName_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_DatabaseTypeName_Call) Return(s string) *MockColumnType_DatabaseTypeName_Call {
|
||||
_c.Call.Return(s)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_DatabaseTypeName_Call) RunAndReturn(run func() string) *MockColumnType_DatabaseTypeName_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Name provides a mock function for the type MockColumnType
|
||||
func (_mock *MockColumnType) Name() string {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Name")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
if returnFunc, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockColumnType_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name'
|
||||
type MockColumnType_Name_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Name is a helper method to define mock.On call
|
||||
func (_e *MockColumnType_Expecter) Name() *MockColumnType_Name_Call {
|
||||
return &MockColumnType_Name_Call{Call: _e.mock.On("Name")}
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_Name_Call) Run(run func()) *MockColumnType_Name_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_Name_Call) Return(s string) *MockColumnType_Name_Call {
|
||||
_c.Call.Return(s)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_Name_Call) RunAndReturn(run func() string) *MockColumnType_Name_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Nullable provides a mock function for the type MockColumnType
|
||||
func (_mock *MockColumnType) Nullable() bool {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Nullable")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
if returnFunc, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockColumnType_Nullable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Nullable'
|
||||
type MockColumnType_Nullable_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Nullable is a helper method to define mock.On call
|
||||
func (_e *MockColumnType_Expecter) Nullable() *MockColumnType_Nullable_Call {
|
||||
return &MockColumnType_Nullable_Call{Call: _e.mock.On("Nullable")}
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_Nullable_Call) Run(run func()) *MockColumnType_Nullable_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_Nullable_Call) Return(b bool) *MockColumnType_Nullable_Call {
|
||||
_c.Call.Return(b)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_Nullable_Call) RunAndReturn(run func() bool) *MockColumnType_Nullable_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ScanType provides a mock function for the type MockColumnType
|
||||
func (_mock *MockColumnType) ScanType() reflect.Type {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ScanType")
|
||||
}
|
||||
|
||||
var r0 reflect.Type
|
||||
if returnFunc, ok := ret.Get(0).(func() reflect.Type); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(reflect.Type)
|
||||
}
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockColumnType_ScanType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ScanType'
|
||||
type MockColumnType_ScanType_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ScanType is a helper method to define mock.On call
|
||||
func (_e *MockColumnType_Expecter) ScanType() *MockColumnType_ScanType_Call {
|
||||
return &MockColumnType_ScanType_Call{Call: _e.mock.On("ScanType")}
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_ScanType_Call) Run(run func()) *MockColumnType_ScanType_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_ScanType_Call) Return(typeParam reflect.Type) *MockColumnType_ScanType_Call {
|
||||
_c.Call.Return(typeParam)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockColumnType_ScanType_Call) RunAndReturn(run func() reflect.Type) *MockColumnType_ScanType_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -9,7 +9,12 @@ from fixtures import types
|
||||
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD
|
||||
from fixtures.logs import Logs
|
||||
from fixtures.querier import (
|
||||
BuilderQuery,
|
||||
OrderBy,
|
||||
RequestType,
|
||||
TelemetryFieldKey,
|
||||
assert_identical_query_response,
|
||||
get_rows,
|
||||
make_query_request,
|
||||
)
|
||||
|
||||
@@ -498,3 +503,64 @@ def test_logs_list_with_order_by(
|
||||
ids = [row["data"]["attributes_string"].get("id", "") for row in rows]
|
||||
|
||||
assert ids == expected_order
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"limit,offset",
|
||||
[
|
||||
pytest.param(2, 0, id="first_chunk"),
|
||||
pytest.param(2, 1, id="first_into_second_chunk"),
|
||||
pytest.param(2, 2, id="second_chunk"),
|
||||
pytest.param(2, 3, id="second_into_third_chunk"),
|
||||
pytest.param(2, 4, id="third_chunk"),
|
||||
pytest.param(4, 5, id="last_partial_page"),
|
||||
pytest.param(2, 6, id="offset_past_end"),
|
||||
],
|
||||
)
|
||||
def test_logs_list_offset_across_chunks(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_logs: Callable[[list[Logs]], None],
|
||||
limit: int,
|
||||
offset: int,
|
||||
) -> None:
|
||||
"""
|
||||
A 4h list runs as 1h, 2h and 1h chunks, newest first. Two logs sit in each
|
||||
chunk, so the offset lands in, or crosses into, every chunk.
|
||||
"""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
minutes_ago = [10, 20, 90, 150, 200, 220]
|
||||
insert_logs(
|
||||
[
|
||||
Logs(
|
||||
timestamp=now - timedelta(minutes=minutes),
|
||||
resources={"service.name": "chunked-offset-service"},
|
||||
body=f"chunked-log-{minutes}",
|
||||
)
|
||||
for minutes in minutes_ago
|
||||
]
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((now - timedelta(hours=4)).timestamp() * 1000),
|
||||
end_ms=int((now + timedelta(seconds=1)).timestamp() * 1000),
|
||||
request_type=RequestType.RAW,
|
||||
queries=[
|
||||
BuilderQuery(
|
||||
signal="logs",
|
||||
name="A",
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
filter_expression="service.name = 'chunked-offset-service'",
|
||||
order=[OrderBy(TelemetryFieldKey("timestamp"), "desc"), OrderBy(TelemetryFieldKey("id"), "desc")],
|
||||
).to_dict()
|
||||
],
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
expected_bodies = [f"chunked-log-{minutes}" for minutes in minutes_ago][offset : offset + limit]
|
||||
assert [row["data"]["body"] for row in get_rows(response)] == expected_bodies
|
||||
|
||||
@@ -917,6 +917,69 @@ def test_traces_list_pagination(
|
||||
assert [row["data"]["span_id"] for row in rows] == expected_ids
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"limit,offset",
|
||||
[
|
||||
pytest.param(2, 0, id="first_chunk"),
|
||||
pytest.param(2, 1, id="first_into_second_chunk"),
|
||||
pytest.param(2, 2, id="second_chunk"),
|
||||
pytest.param(2, 3, id="second_into_third_chunk"),
|
||||
pytest.param(2, 4, id="third_chunk"),
|
||||
pytest.param(4, 5, id="last_partial_page"),
|
||||
pytest.param(2, 6, id="offset_past_end"),
|
||||
],
|
||||
)
|
||||
def test_traces_list_offset_across_chunks(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_traces: Callable[[list[Traces]], None],
|
||||
limit: int,
|
||||
offset: int,
|
||||
) -> None:
|
||||
"""
|
||||
A 4h list runs as 1h, 2h and 1h chunks, newest first. Two spans sit in each
|
||||
chunk, so the offset lands in, or crosses into, every chunk.
|
||||
"""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
minutes_ago = [10, 20, 90, 150, 200, 220]
|
||||
insert_traces(
|
||||
[
|
||||
Traces(
|
||||
timestamp=now - timedelta(minutes=minutes),
|
||||
trace_id=TraceIdGenerator.trace_id(),
|
||||
span_id=TraceIdGenerator.span_id(),
|
||||
name=f"chunked-span-{minutes}",
|
||||
resources={"service.name": "chunked-offset-service"},
|
||||
)
|
||||
for minutes in minutes_ago
|
||||
]
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((now - timedelta(hours=4)).timestamp() * 1000),
|
||||
end_ms=int((now + timedelta(seconds=1)).timestamp() * 1000),
|
||||
request_type=RequestType.RAW,
|
||||
queries=[
|
||||
BuilderQuery(
|
||||
signal="traces",
|
||||
name="A",
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
filter_expression="service.name = 'chunked-offset-service'",
|
||||
order=[OrderBy(TelemetryFieldKey("timestamp"), "desc")],
|
||||
).to_dict()
|
||||
],
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
expected_names = [f"chunked-span-{minutes}" for minutes in minutes_ago][offset : offset + limit]
|
||||
assert [row["data"]["name"] for row in get_rows(response)] == expected_names
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Time window
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user