|
|
|
|
@ -1,7 +1,6 @@ |
|
|
|
|
package promql |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"reflect" |
|
|
|
|
"sync" |
|
|
|
|
"testing" |
|
|
|
|
"time" |
|
|
|
|
@ -11,6 +10,10 @@ import ( |
|
|
|
|
"github.com/prometheus/prometheus/storage/local" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
var noop = testStmt(func(context.Context) error { |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
func TestQueryTimeout(t *testing.T) { |
|
|
|
|
*defaultQueryTimeout = 5 * time.Millisecond |
|
|
|
|
defer func() { |
|
|
|
|
@ -24,24 +27,15 @@ func TestQueryTimeout(t *testing.T) { |
|
|
|
|
engine := NewEngine(storage) |
|
|
|
|
defer engine.Stop() |
|
|
|
|
|
|
|
|
|
query, err := engine.NewQuery("foo = bar") |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("error parsing query: %s", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Timeouts are not exact but checked in designated places. For example between
|
|
|
|
|
// invoking handlers. Thus, we reigster two handlers that take some time to ensure we check
|
|
|
|
|
// after exceeding the timeout.
|
|
|
|
|
// Should the implementation of this area change, the test might have to be adjusted.
|
|
|
|
|
engine.RegisterRecordHandler("test", func(context.Context, *RecordStmt) error { |
|
|
|
|
time.Sleep(10 * time.Millisecond) |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
engine.RegisterRecordHandler("test2", func(context.Context, *RecordStmt) error { |
|
|
|
|
f1 := testStmt(func(context.Context) error { |
|
|
|
|
time.Sleep(10 * time.Millisecond) |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// Timeouts are not exact but checked in designated places. For example between
|
|
|
|
|
// invoking test statements.
|
|
|
|
|
query := engine.newTestQuery(f1, f1) |
|
|
|
|
|
|
|
|
|
res := query.Exec() |
|
|
|
|
if res.Err == nil { |
|
|
|
|
t.Fatalf("expected timeout error but got none") |
|
|
|
|
@ -58,26 +52,16 @@ func TestQueryCancel(t *testing.T) { |
|
|
|
|
engine := NewEngine(storage) |
|
|
|
|
defer engine.Stop() |
|
|
|
|
|
|
|
|
|
query1, err := engine.NewQuery("foo = bar") |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("error parsing query: %s", err) |
|
|
|
|
} |
|
|
|
|
query2, err := engine.NewQuery("foo = baz") |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("error parsing query: %s", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// As for timeouts, cancellation is only checked at designated points. We ensure
|
|
|
|
|
// that we reach one of those points using the same method.
|
|
|
|
|
engine.RegisterRecordHandler("test1", func(context.Context, *RecordStmt) error { |
|
|
|
|
<-time.After(2 * time.Millisecond) |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
engine.RegisterRecordHandler("test2", func(context.Context, *RecordStmt) error { |
|
|
|
|
<-time.After(2 * time.Millisecond) |
|
|
|
|
f1 := testStmt(func(context.Context) error { |
|
|
|
|
time.Sleep(2 * time.Millisecond) |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
query1 := engine.newTestQuery(f1, f1) |
|
|
|
|
query2 := engine.newTestQuery(f1, f1) |
|
|
|
|
|
|
|
|
|
// Cancel query after starting it.
|
|
|
|
|
var wg sync.WaitGroup |
|
|
|
|
var res *Result |
|
|
|
|
@ -87,7 +71,7 @@ func TestQueryCancel(t *testing.T) { |
|
|
|
|
res = query1.Exec() |
|
|
|
|
wg.Done() |
|
|
|
|
}() |
|
|
|
|
<-time.After(1 * time.Millisecond) |
|
|
|
|
time.Sleep(1 * time.Millisecond) |
|
|
|
|
query1.Cancel() |
|
|
|
|
wg.Wait() |
|
|
|
|
|
|
|
|
|
@ -112,34 +96,20 @@ func TestEngineShutdown(t *testing.T) { |
|
|
|
|
|
|
|
|
|
engine := NewEngine(storage) |
|
|
|
|
|
|
|
|
|
query1, err := engine.NewQuery("foo = bar") |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("error parsing query: %s", err) |
|
|
|
|
} |
|
|
|
|
query2, err := engine.NewQuery("foo = baz") |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("error parsing query: %s", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
handlerExecutions := 0 |
|
|
|
|
|
|
|
|
|
// Shutdown engine on first handler execution. Should handler execution ever become
|
|
|
|
|
// concurrent this test has to be adjusted accordingly.
|
|
|
|
|
engine.RegisterRecordHandler("test", func(context.Context, *RecordStmt) error { |
|
|
|
|
handlerExecutions++ |
|
|
|
|
engine.Stop() |
|
|
|
|
time.Sleep(10 * time.Millisecond) |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
engine.RegisterRecordHandler("test2", func(context.Context, *RecordStmt) error { |
|
|
|
|
f1 := testStmt(func(context.Context) error { |
|
|
|
|
handlerExecutions++ |
|
|
|
|
engine.Stop() |
|
|
|
|
time.Sleep(10 * time.Millisecond) |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
query1 := engine.newTestQuery(f1, f1) |
|
|
|
|
query2 := engine.newTestQuery(f1, f1) |
|
|
|
|
|
|
|
|
|
// Stopping the engine should cancel the base context. While setting up queries is
|
|
|
|
|
// still possible their context is canceled from the beginning and execution should
|
|
|
|
|
// Stopping the engine must cancel the base context. While executing queries is
|
|
|
|
|
// still possible, their context is canceled from the beginning and execution should
|
|
|
|
|
// terminate immediately.
|
|
|
|
|
|
|
|
|
|
res := query1.Exec() |
|
|
|
|
@ -147,7 +117,7 @@ func TestEngineShutdown(t *testing.T) { |
|
|
|
|
t.Fatalf("expected error on shutdown during query but got none") |
|
|
|
|
} |
|
|
|
|
if handlerExecutions != 1 { |
|
|
|
|
t.Fatalf("expected only one handler to be executed before query cancellation but got %d executons", handlerExecutions) |
|
|
|
|
t.Fatalf("expected only one handler to be executed before query cancellation but got %d executions", handlerExecutions) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res2 := query2.Exec() |
|
|
|
|
@ -159,114 +129,3 @@ func TestEngineShutdown(t *testing.T) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestAlertHandler(t *testing.T) { |
|
|
|
|
storage, closer := local.NewTestStorage(t, 1) |
|
|
|
|
defer closer.Close() |
|
|
|
|
|
|
|
|
|
engine := NewEngine(storage) |
|
|
|
|
defer engine.Stop() |
|
|
|
|
|
|
|
|
|
qs := `ALERT Foo IF bar FOR 5m WITH {a="b"} SUMMARY "sum" DESCRIPTION "desc"` |
|
|
|
|
|
|
|
|
|
doQuery := func(expectFailure bool) *AlertStmt { |
|
|
|
|
query, err := engine.NewQuery(qs) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("error parsing query: %s", err) |
|
|
|
|
} |
|
|
|
|
res := query.Exec() |
|
|
|
|
if expectFailure && res.Err == nil { |
|
|
|
|
t.Fatalf("expected error but got none.") |
|
|
|
|
} |
|
|
|
|
if res.Err != nil && !expectFailure { |
|
|
|
|
t.Fatalf("error on executing alert query: %s", res.Err) |
|
|
|
|
} |
|
|
|
|
// That this alert statement is correct is tested elsewhere.
|
|
|
|
|
return query.Statements()[0].(*AlertStmt) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// We expect an error if nothing is registered to handle the query.
|
|
|
|
|
alertStmt := doQuery(true) |
|
|
|
|
|
|
|
|
|
receivedCalls := 0 |
|
|
|
|
|
|
|
|
|
// Ensure that we receive the correct statement.
|
|
|
|
|
engine.RegisterAlertHandler("test", func(ctx context.Context, as *AlertStmt) error { |
|
|
|
|
if !reflect.DeepEqual(alertStmt, as) { |
|
|
|
|
t.Errorf("received alert statement did not match input: %q", qs) |
|
|
|
|
t.Fatalf("no match\n\nexpected:\n%s\ngot: \n%s\n", Tree(alertStmt), Tree(as)) |
|
|
|
|
} |
|
|
|
|
receivedCalls++ |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ { |
|
|
|
|
doQuery(false) |
|
|
|
|
if receivedCalls != i+1 { |
|
|
|
|
t.Fatalf("alert handler was not called on query execution") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
engine.UnregisterAlertHandler("test") |
|
|
|
|
|
|
|
|
|
// We must receive no further calls after unregistering.
|
|
|
|
|
doQuery(true) |
|
|
|
|
if receivedCalls != 10 { |
|
|
|
|
t.Fatalf("received calls after unregistering alert handler") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestRecordHandler(t *testing.T) { |
|
|
|
|
storage, closer := local.NewTestStorage(t, 1) |
|
|
|
|
defer closer.Close() |
|
|
|
|
|
|
|
|
|
engine := NewEngine(storage) |
|
|
|
|
defer engine.Stop() |
|
|
|
|
|
|
|
|
|
qs := `foo = bar` |
|
|
|
|
|
|
|
|
|
doQuery := func(expectFailure bool) *RecordStmt { |
|
|
|
|
query, err := engine.NewQuery(qs) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("error parsing query: %s", err) |
|
|
|
|
} |
|
|
|
|
res := query.Exec() |
|
|
|
|
if expectFailure && res.Err == nil { |
|
|
|
|
t.Fatalf("expected error but got none.") |
|
|
|
|
} |
|
|
|
|
if res.Err != nil && !expectFailure { |
|
|
|
|
t.Fatalf("error on executing record query: %s", res.Err) |
|
|
|
|
} |
|
|
|
|
return query.Statements()[0].(*RecordStmt) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// We expect an error if nothing is registered to handle the query.
|
|
|
|
|
recordStmt := doQuery(true) |
|
|
|
|
|
|
|
|
|
receivedCalls := 0 |
|
|
|
|
|
|
|
|
|
// Ensure that we receive the correct statement.
|
|
|
|
|
engine.RegisterRecordHandler("test", func(ctx context.Context, rs *RecordStmt) error { |
|
|
|
|
if !reflect.DeepEqual(recordStmt, rs) { |
|
|
|
|
t.Errorf("received record statement did not match input: %q", qs) |
|
|
|
|
t.Fatalf("no match\n\nexpected:\n%s\ngot: \n%s\n", Tree(recordStmt), Tree(rs)) |
|
|
|
|
} |
|
|
|
|
receivedCalls++ |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ { |
|
|
|
|
doQuery(false) |
|
|
|
|
if receivedCalls != i+1 { |
|
|
|
|
t.Fatalf("record handler was not called on query execution") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
engine.UnregisterRecordHandler("test") |
|
|
|
|
|
|
|
|
|
// We must receive no further calls after unregistering.
|
|
|
|
|
doQuery(true) |
|
|
|
|
if receivedCalls != 10 { |
|
|
|
|
t.Fatalf("received calls after unregistering record handler") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|