grafana / gel-app

Experimental Grafana Backend Expressions/Transform Plugin (GEL)
Apache License 2.0
2 stars 1 forks source link

TestService Doesn't Always Pass (Non-deterministic) #54

Closed kylebrandt closed 4 years ago

kylebrandt commented 5 years ago

In the result, the order of the frames changes so sometimes that tests pass, and sometimes they don't. This is the output of pass vs fail (with some spew):

Pass:

$ /usr/local/go/bin/go test -count=1 -timeout 30s github.com/grafana/gel-app/pkg/gelpoc -run '^(TestService)$' -v
=== RUN   TestService
([]*dataframe.Frame) (len=2 cap=2) {
 (*dataframe.Frame)(0xc000198100)({
  Name: (string) (len=4) "test",
  Labels: (dataframe.Labels) ,
  Fields: ([]*dataframe.Field) (len=2 cap=2) {
   (*dataframe.Field)(0xc00000efe0)({
    Name: (string) (len=4) "time",
    Vector: (*dataframe.nullableTimeVector)(0xc00000efc0)((len=1 cap=1) {
     (*time.Time)(0xc00000ef60)(1969-12-31 19:00:01 -0500 EST)
    })
   }),
   (*dataframe.Field)(0xc00000f060)({
    Name: (string) (len=5) "value",
    Vector: (*dataframe.nullableFloatVector)(0xc00000f040)((len=1 cap=1) {
     (*float64)(0xc00014ca80)(2)
    })
   })
  },
  RefID: (string) (len=1) "A"
 }),
 (*dataframe.Frame)(0xc000198280)({
  Name: (string) "",
  Labels: (dataframe.Labels) ,
  Fields: ([]*dataframe.Field) (len=2 cap=2) {
   (*dataframe.Field)(0xc00000f660)({
    Name: (string) (len=4) "Time",
    Vector: (*dataframe.nullableTimeVector)(0xc00000f640)((len=1 cap=1) {
     (*time.Time)(0xc00000f5e0)(1969-12-31 19:00:01 -0500 EST)
    })
   }),
   (*dataframe.Field)(0xc00000f6e0)({
    Name: (string) "",
    Vector: (*dataframe.nullableFloatVector)(0xc00000f6c0)((len=1 cap=1) {
     (*float64)(0xc00014cbd0)(4)
    })
   })
  },
  RefID: (string) (len=1) "B"
 })
}
([]*dataframe.Frame) (len=2 cap=2) {
 (*dataframe.Frame)(0xc000198100)({
  Name: (string) (len=4) "test",
  Labels: (dataframe.Labels) ,
  Fields: ([]*dataframe.Field) (len=2 cap=2) {
   (*dataframe.Field)(0xc00000efe0)({
    Name: (string) (len=4) "time",
    Vector: (*dataframe.nullableTimeVector)(0xc00000efc0)((len=1 cap=1) {
     (*time.Time)(0xc00000ef60)(1969-12-31 19:00:01 -0500 EST)
    })
   }),
   (*dataframe.Field)(0xc00000f060)({
    Name: (string) (len=5) "value",
    Vector: (*dataframe.nullableFloatVector)(0xc00000f040)((len=1 cap=1) {
     (*float64)(0xc00014ca80)(2)
    })
   })
  },
  RefID: (string) (len=1) "A"
 }),
 (*dataframe.Frame)(0xc000198240)({
  Name: (string) "",
  Labels: (dataframe.Labels) ,
  Fields: ([]*dataframe.Field) (len=2 cap=2) {
   (*dataframe.Field)(0xc00000f540)({
    Name: (string) (len=4) "Time",
    Vector: (*dataframe.nullableTimeVector)(0xc00000f520)((len=1 cap=1) {
     (*time.Time)(0xc00000ef60)(1969-12-31 19:00:01 -0500 EST)
    })
   }),
   (*dataframe.Field)(0xc00000f5c0)({
    Name: (string) "",
    Vector: (*dataframe.nullableFloatVector)(0xc00000f5a0)((len=1 cap=1) {
     (*float64)(0xc00014cbb8)(4)
    })
   })
  },
  RefID: (string) (len=1) "B"
 })
}
--- PASS: TestService (0.00s)
PASS
ok      github.com/grafana/gel-app/pkg/gelpoc   0.011s

Fail:

$  /usr/local/go/bin/go test -count=1 -timeout 30s github.com/grafana/gel-app/pkg/gelpoc -run '^(TestService)$' -v
=== RUN   TestService
([]*dataframe.Frame) (len=2 cap=2) {
 (*dataframe.Frame)(0xc0001a00c0)({
  Name: (string) (len=4) "test",
  Labels: (dataframe.Labels) ,
  Fields: ([]*dataframe.Field) (len=2 cap=2) {
   (*dataframe.Field)(0xc0000b8fe0)({
    Name: (string) (len=4) "time",
    Vector: (*dataframe.nullableTimeVector)(0xc0000b8fc0)((len=1 cap=1) {
     (*time.Time)(0xc0000b8f60)(1969-12-31 19:00:01 -0500 EST)
    })
   }),
   (*dataframe.Field)(0xc0000b9060)({
    Name: (string) (len=5) "value",
    Vector: (*dataframe.nullableFloatVector)(0xc0000b9040)((len=1 cap=1) {
     (*float64)(0xc0001529b0)(2)
    })
   })
  },
  RefID: (string) (len=1) "A"
 }),
 (*dataframe.Frame)(0xc0001a0240)({
  Name: (string) "",
  Labels: (dataframe.Labels) ,
  Fields: ([]*dataframe.Field) (len=2 cap=2) {
   (*dataframe.Field)(0xc0000b9660)({
    Name: (string) (len=4) "Time",
    Vector: (*dataframe.nullableTimeVector)(0xc0000b9640)((len=1 cap=1) {
     (*time.Time)(0xc0000b95e0)(1969-12-31 19:00:01 -0500 EST)
    })
   }),
   (*dataframe.Field)(0xc0000b96e0)({
    Name: (string) "",
    Vector: (*dataframe.nullableFloatVector)(0xc0000b96c0)((len=1 cap=1) {
     (*float64)(0xc000152b00)(4)
    })
   })
  },
  RefID: (string) (len=1) "B"
 })
}
([]*dataframe.Frame) (len=2 cap=2) {
 (*dataframe.Frame)(0xc0001a0200)({
  Name: (string) "",
  Labels: (dataframe.Labels) ,
  Fields: ([]*dataframe.Field) (len=2 cap=2) {
   (*dataframe.Field)(0xc0000b9540)({
    Name: (string) (len=4) "Time",
    Vector: (*dataframe.nullableTimeVector)(0xc0000b9520)((len=1 cap=1) {
     (*time.Time)(0xc0000b8f60)(1969-12-31 19:00:01 -0500 EST)
    })
   }),
   (*dataframe.Field)(0xc0000b95c0)({
    Name: (string) "",
    Vector: (*dataframe.nullableFloatVector)(0xc0000b95a0)((len=1 cap=1) {
     (*float64)(0xc000152ae8)(4)
    })
   })
  },
  RefID: (string) (len=1) "B"
 }),
 (*dataframe.Frame)(0xc0001a00c0)({
  Name: (string) (len=4) "test",
  Labels: (dataframe.Labels) ,
  Fields: ([]*dataframe.Field) (len=2 cap=2) {
   (*dataframe.Field)(0xc0000b8fe0)({
    Name: (string) (len=4) "time",
    Vector: (*dataframe.nullableTimeVector)(0xc0000b8fc0)((len=1 cap=1) {
     (*time.Time)(0xc0000b8f60)(1969-12-31 19:00:01 -0500 EST)
    })
   }),
   (*dataframe.Field)(0xc0000b9060)({
    Name: (string) (len=5) "value",
    Vector: (*dataframe.nullableFloatVector)(0xc0000b9040)((len=1 cap=1) {
     (*float64)(0xc0001529b0)(2)
    })
   })
  },
  RefID: (string) (len=1) "A"
 })
}
--- FAIL: TestService (0.00s)
    service_test.go:56: Result mismatch (-want +got):
          []*dataframe.Frame{
        -       &{
        -               Name: "test",
        -               Fields: []*dataframe.Field{
        -                       &{
        -                               Name:   "time",
        -                               Vector: &dataframe.nullableTimeVector{s"1969-12-31 19:00:01 -0500 EST"},
        -                       },
        -                       &{Name: "value", Vector: &dataframe.nullableFloatVector{&2}},
        -               },
        -               RefID: "A",
        -       },
                &{Fields: []*dataframe.Field{&{Name: "Time", Vector: &dataframe.nullableTimeVector{&s"1969-12-31 19:00:01 -0500 EST"}}, &{Vector: &dataframe.nullableFloatVector{&4}}}, RefID: "B"},
        +       &{
        +               Name: "test",
        +               Fields: []*dataframe.Field{
        +                       &{
        +                               Name:   "time",
        +                               Vector: &dataframe.nullableTimeVector{s"1969-12-31 19:00:01 -0500 EST"},
        +                       },
        +                       &{Name: "value", Vector: &dataframe.nullableFloatVector{&2}},
        +               },
        +               RefID: "A",
        +       },
          }
FAIL
FAIL    github.com/grafana/gel-app/pkg/gelpoc   0.014s
FAIL

In the failed tests, the order of frames is B then A instead of A then B (by refid).

kylebrandt commented 5 years ago

Looking for issues notes:

Ah, when the data pipeline is executed it is turned into mathexp.Vars which is a map and will not have a predictable order:

// execute runs all the command/datasource requests in the pipeline return a
// map of the refId of the of each command
func (dp *DataPipeline) execute(c context.Context) (mathexp.Vars, error) {
    vars := make(mathexp.Vars)
    for _, node := range *dp {
        res, err := node.Execute(c, vars)
        if err != nil {
            return nil, err
        }

        vars[node.RefID()] = res
    }
    return vars, nil
}

And then it gets converted back to slice of dataframes in the service.