influxdb/task/backend/meta_test.go

282 lines
7.8 KiB
Go

package backend_test
import (
"errors"
"strings"
"testing"
"time"
platform "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/snowflake"
"github.com/influxdata/influxdb/task/backend"
)
var idGen = snowflake.NewIDGenerator()
func makeID() (platform.ID, error) {
return idGen.ID(), nil
}
func TestMeta_CreateNextRun(t *testing.T) {
good := backend.StoreTaskMeta{
MaxConcurrency: 2,
Status: "enabled",
EffectiveCron: "* * * * *", // Every minute.
LatestCompleted: 60, // It has run once for the first minute.
}
_, err := good.CreateNextRun(59, makeID)
if e, ok := err.(backend.RunNotYetDueError); !ok {
t.Fatalf("expected RunNotYetDueError, got %v (%T)", err, err)
} else if e.DueAt != 120 {
t.Fatalf("expected run due at 120, got %d", e.DueAt)
}
bad := new(backend.StoreTaskMeta)
*bad = good
bad.MaxConcurrency = 0
if _, err := bad.CreateNextRun(120, makeID); err == nil || !strings.Contains(err.Error(), "max concurrency") {
t.Fatalf("expected error about max concurrency, got %v", err)
}
*bad = good
bad.EffectiveCron = "not a cron"
if _, err := bad.CreateNextRun(120, makeID); err == nil {
t.Fatal("expected error with bad cron")
}
idErr := errors.New("error making ID")
*bad = good
if _, err := bad.CreateNextRun(120, func() (platform.ID, error) {
return platform.InvalidID(), idErr
}); err != idErr {
t.Fatalf("expected id creation error, got %v", err)
}
rc, err := good.CreateNextRun(300, makeID)
if err != nil {
t.Fatal(err)
}
if rc.Created.TaskID.Valid() {
t.Fatalf("CreateNextRun should not have set task ID; got %v", rc.Created.TaskID)
}
if !rc.Created.RunID.Valid() {
t.Fatal("CreateNextRun should have set run ID but didn't")
}
if rc.Created.Now != 120 {
t.Fatalf("expected created run to have time 120, got %d", rc.Created.Now)
}
if rc.NextDue != 180 {
t.Fatalf("unexpected next run time: %d", rc.NextDue)
}
rc, err = good.CreateNextRun(300, makeID)
if err != nil {
t.Fatal(err)
}
if rc.Created.TaskID.Valid() {
t.Fatalf("CreateNextRun should not have set task ID; got %v", rc.Created.TaskID)
}
if !rc.Created.RunID.Valid() {
t.Fatal("CreateNextRun should have set run ID but didn't")
}
if rc.Created.Now != 180 {
t.Fatalf("expected created run to have time 180, got %d", rc.Created.Now)
}
if rc.NextDue != 240 {
t.Fatalf("unexpected next run time: %d", rc.NextDue)
}
if _, err := good.CreateNextRun(300, makeID); err == nil || !strings.Contains(err.Error(), "max concurrency") {
t.Fatalf("expected error about max concurrency, got %v", err)
}
}
func TestMeta_CreateNextRun_Queue(t *testing.T) {
stm := backend.StoreTaskMeta{
MaxConcurrency: 9,
Status: "enabled",
EffectiveCron: "* * * * *", // Every minute.
LatestCompleted: 3000, // It has run once for the first minute.
}
// Should run on 0, 60, and 120.
if err := stm.ManuallyRunTimeRange(0, 120, 3005, nil); err != nil {
t.Fatal(err)
}
// Should run once: 240.
if err := stm.ManuallyRunTimeRange(240, 240, 3005, nil); err != nil {
t.Fatal(err)
}
// Run once on the next natural schedule.
rc, err := stm.CreateNextRun(3060, makeID)
if err != nil {
t.Fatal(err)
}
if rc.Created.Now != 3060 {
t.Fatalf("expected created now of 3060, got %d", rc.Created.Now)
}
if rc.NextDue != 3120 {
t.Fatalf("expected NextDue = 3120, got %d", rc.NextDue)
}
if !rc.HasQueue {
t.Fatal("expected to have queue but didn't")
}
// 0 from queue.
rc, err = stm.CreateNextRun(3060, makeID)
if err != nil {
t.Fatal(err)
}
if rc.Created.Now != 0 {
t.Fatalf("expected created now of 0, got %d", rc.Created.Now)
}
if rc.NextDue != 3120 {
t.Fatalf("expected NextDue = 3120, got %d", rc.NextDue)
}
if !rc.HasQueue {
t.Fatal("expected to have queue but didn't")
}
// 60 from queue.
rc, err = stm.CreateNextRun(3060, makeID)
if err != nil {
t.Fatal(err)
}
if rc.Created.Now != 60 {
t.Fatalf("expected created now of 60, got %d", rc.Created.Now)
}
if rc.NextDue != 3120 {
t.Fatalf("expected NextDue = 3120, got %d", rc.NextDue)
}
if !rc.HasQueue {
t.Fatal("expected to have queue but didn't")
}
// 120 from queue.
rc, err = stm.CreateNextRun(3060, makeID)
if err != nil {
t.Fatal(err)
}
if rc.Created.Now != 120 {
t.Fatalf("expected created now of 120, got %d", rc.Created.Now)
}
if rc.NextDue != 3120 {
t.Fatalf("expected NextDue = 3120, got %d", rc.NextDue)
}
if !rc.HasQueue {
t.Fatal("expected to have queue but didn't")
}
// 240 from queue.
rc, err = stm.CreateNextRun(3060, makeID)
if err != nil {
t.Fatal(err)
}
if rc.Created.Now != 240 {
t.Fatalf("expected created now of 240, got %d", rc.Created.Now)
}
if rc.NextDue != 3120 {
t.Fatalf("expected NextDue = 3120, got %d", rc.NextDue)
}
if rc.HasQueue {
t.Fatal("expected to have empty queue but didn't")
}
}
func TestMeta_CreateNextRun_Delay(t *testing.T) {
stm := backend.StoreTaskMeta{
MaxConcurrency: 2,
Status: "enabled",
EffectiveCron: "* * * * *", // Every minute.
Offset: "5s",
LatestCompleted: 30, // Arbitrary non-overlap starting point.
}
_, err := stm.CreateNextRun(61, makeID)
if e, ok := err.(backend.RunNotYetDueError); !ok {
t.Fatalf("expected RunNotYetDueError, got %v (%T)", err, err)
} else if e.DueAt != 65 {
t.Fatalf("expected run due at 65, got %d", e.DueAt)
}
rc, err := stm.CreateNextRun(300, makeID)
if err != nil {
t.Fatal(err)
}
if rc.Created.Now != 60 {
t.Fatalf("expected created run to have time 60, got %d", rc.Created.Now)
}
if rc.NextDue != 125 {
t.Fatalf("unexpected next run time: %d", rc.NextDue)
}
}
func TestMeta_ManuallyRunTimeRange(t *testing.T) {
now := time.Now().Unix()
stm := backend.StoreTaskMeta{
MaxConcurrency: 2,
Status: "enabled",
EffectiveCron: "* * * * *", // Every minute.
Offset: "5s",
LatestCompleted: 30, // Arbitrary non-overlap starting point.
}
// Constant defined in (*StoreTaskMeta).ManuallyRunTimeRange.
const maxQueueSize = 32
for i := int64(0); i < maxQueueSize; i++ {
j := i * 10
if err := stm.ManuallyRunTimeRange(j, j+5, j+now, nil); err != nil {
t.Fatal(err)
}
if int64(len(stm.ManualRuns)) != i+1 {
t.Fatalf("expected %d runs queued, got %d", i+1, len(stm.ManualRuns))
}
run := stm.ManualRuns[len(stm.ManualRuns)-1]
if run.Start != j {
t.Fatalf("expected start %d, got %d", j, run.Start)
}
if run.End != j+5 {
t.Fatalf("expected end %d, got %d", j+5, run.End)
}
if run.LatestCompleted != j-1 {
t.Fatalf("expected LatestCompleted %d, got %d", j-1, run.LatestCompleted)
}
if run.RequestedAt != j+now {
t.Fatalf("expected RequestedAt %d, got %d", j+now, run.RequestedAt)
}
}
// One more should cause ErrManualQueueFull.
if err := stm.ManuallyRunTimeRange(maxQueueSize*100, maxQueueSize*200, maxQueueSize+now, nil); err != backend.ErrManualQueueFull {
t.Fatalf("expected ErrManualQueueFull, got %v", err)
}
if len(stm.ManualRuns) != maxQueueSize {
t.Fatalf("expected to be unable to exceed queue size of %d; got %d", maxQueueSize, len(stm.ManualRuns))
}
// Reset manual runs.
stm.ManualRuns = stm.ManualRuns[:0]
// Duplicate manual run with single timestamp should be rejected.
if err := stm.ManuallyRunTimeRange(1, 1, 2, nil); err != nil {
t.Fatal(err)
}
if exp, err := (backend.RequestStillQueuedError{Start: 1, End: 1}), stm.ManuallyRunTimeRange(1, 1, 3, func() (platform.ID, error) { return platform.ID(1099), nil }); err != exp {
t.Fatalf("expected %v, got %v", exp, err)
}
// Duplicate manual run with time range should be rejected.
if err := stm.ManuallyRunTimeRange(100, 200, 201, nil); err != nil {
t.Fatal(err)
}
if exp, err := (backend.RequestStillQueuedError{Start: 100, End: 200}), stm.ManuallyRunTimeRange(100, 200, 202, nil); err != exp {
t.Fatalf("expected %v, got %v", exp, err)
}
// Not currently enforcing one way or another when a newly requested time range overlaps with an existing one.
}