scenarios.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. package testdata
  2. import (
  3. "encoding/json"
  4. "math"
  5. "math/rand"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "github.com/grafana/grafana/pkg/components/null"
  10. "github.com/grafana/grafana/pkg/log"
  11. "github.com/grafana/grafana/pkg/tsdb"
  12. )
  13. type ScenarioHandler func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult
  14. type Scenario struct {
  15. Id string `json:"id"`
  16. Name string `json:"name"`
  17. StringInput string `json:"stringOption"`
  18. Description string `json:"description"`
  19. Handler ScenarioHandler `json:"-"`
  20. }
  21. var ScenarioRegistry map[string]*Scenario
  22. func init() {
  23. ScenarioRegistry = make(map[string]*Scenario)
  24. logger := log.New("tsdb.testdata")
  25. logger.Debug("Initializing TestData Scenario")
  26. registerScenario(&Scenario{
  27. Id: "exponential_heatmap_bucket_data",
  28. Name: "Exponential heatmap bucket data",
  29. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  30. to := context.TimeRange.GetToAsMsEpoch()
  31. var series []*tsdb.TimeSeries
  32. start := 1
  33. factor := 2
  34. for i := 0; i < 10; i++ {
  35. timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
  36. serie := &tsdb.TimeSeries{Name: strconv.Itoa(start)}
  37. start *= factor
  38. points := make(tsdb.TimeSeriesPoints, 0)
  39. for j := int64(0); j < 100 && timeWalkerMs < to; j++ {
  40. v := float64(rand.Int63n(100))
  41. points = append(points, tsdb.NewTimePoint(null.FloatFrom(v), float64(timeWalkerMs)))
  42. timeWalkerMs += query.IntervalMs * 50
  43. }
  44. serie.Points = points
  45. series = append(series, serie)
  46. }
  47. queryRes := tsdb.NewQueryResult()
  48. queryRes.Series = append(queryRes.Series, series...)
  49. return queryRes
  50. },
  51. })
  52. registerScenario(&Scenario{
  53. Id: "linear_heatmap_bucket_data",
  54. Name: "Linear heatmap bucket data",
  55. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  56. to := context.TimeRange.GetToAsMsEpoch()
  57. var series []*tsdb.TimeSeries
  58. for i := 0; i < 10; i++ {
  59. timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
  60. serie := &tsdb.TimeSeries{Name: strconv.Itoa(i * 10)}
  61. points := make(tsdb.TimeSeriesPoints, 0)
  62. for j := int64(0); j < 100 && timeWalkerMs < to; j++ {
  63. v := float64(rand.Int63n(100))
  64. points = append(points, tsdb.NewTimePoint(null.FloatFrom(v), float64(timeWalkerMs)))
  65. timeWalkerMs += query.IntervalMs * 50
  66. }
  67. serie.Points = points
  68. series = append(series, serie)
  69. }
  70. queryRes := tsdb.NewQueryResult()
  71. queryRes.Series = append(queryRes.Series, series...)
  72. return queryRes
  73. },
  74. })
  75. registerScenario(&Scenario{
  76. Id: "random_walk",
  77. Name: "Random Walk",
  78. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  79. return getRandomWalk(query, context)
  80. },
  81. })
  82. registerScenario(&Scenario{
  83. Id: "random_walk_table",
  84. Name: "Random Walk Table",
  85. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  86. return getRandomWalkTable(query, context)
  87. },
  88. })
  89. registerScenario(&Scenario{
  90. Id: "slow_query",
  91. Name: "Slow Query",
  92. StringInput: "5s",
  93. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  94. stringInput := query.Model.Get("stringInput").MustString()
  95. parsedInterval, _ := time.ParseDuration(stringInput)
  96. time.Sleep(parsedInterval)
  97. return getRandomWalk(query, context)
  98. },
  99. })
  100. registerScenario(&Scenario{
  101. Id: "no_data_points",
  102. Name: "No Data Points",
  103. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  104. return tsdb.NewQueryResult()
  105. },
  106. })
  107. registerScenario(&Scenario{
  108. Id: "datapoints_outside_range",
  109. Name: "Datapoints Outside Range",
  110. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  111. queryRes := tsdb.NewQueryResult()
  112. series := newSeriesForQuery(query)
  113. outsideTime := context.TimeRange.MustGetFrom().Add(-1*time.Hour).Unix() * 1000
  114. series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(10), float64(outsideTime)))
  115. queryRes.Series = append(queryRes.Series, series)
  116. return queryRes
  117. },
  118. })
  119. registerScenario(&Scenario{
  120. Id: "manual_entry",
  121. Name: "Manual Entry",
  122. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  123. queryRes := tsdb.NewQueryResult()
  124. points := query.Model.Get("points").MustArray()
  125. series := newSeriesForQuery(query)
  126. startTime := context.TimeRange.GetFromAsMsEpoch()
  127. endTime := context.TimeRange.GetToAsMsEpoch()
  128. for _, val := range points {
  129. pointValues := val.([]interface{})
  130. var value null.Float
  131. var time int64
  132. if valueFloat, err := strconv.ParseFloat(string(pointValues[0].(json.Number)), 64); err == nil {
  133. value = null.FloatFrom(valueFloat)
  134. }
  135. if timeInt, err := strconv.ParseInt(string(pointValues[1].(json.Number)), 10, 64); err != nil {
  136. continue
  137. } else {
  138. time = timeInt
  139. }
  140. if time >= startTime && time <= endTime {
  141. series.Points = append(series.Points, tsdb.NewTimePoint(value, float64(time)))
  142. }
  143. }
  144. queryRes.Series = append(queryRes.Series, series)
  145. return queryRes
  146. },
  147. })
  148. registerScenario(&Scenario{
  149. Id: "csv_metric_values",
  150. Name: "CSV Metric Values",
  151. StringInput: "1,20,90,30,5,0",
  152. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  153. queryRes := tsdb.NewQueryResult()
  154. stringInput := query.Model.Get("stringInput").MustString()
  155. stringInput = strings.Replace(stringInput, " ", "", -1)
  156. values := []null.Float{}
  157. for _, strVal := range strings.Split(stringInput, ",") {
  158. if strVal == "null" {
  159. values = append(values, null.FloatFromPtr(nil))
  160. }
  161. if val, err := strconv.ParseFloat(strVal, 64); err == nil {
  162. values = append(values, null.FloatFrom(val))
  163. }
  164. }
  165. if len(values) == 0 {
  166. return queryRes
  167. }
  168. series := newSeriesForQuery(query)
  169. startTime := context.TimeRange.GetFromAsMsEpoch()
  170. endTime := context.TimeRange.GetToAsMsEpoch()
  171. step := (endTime - startTime) / int64(len(values)-1)
  172. for _, val := range values {
  173. series.Points = append(series.Points, tsdb.TimePoint{val, null.FloatFrom(float64(startTime))})
  174. startTime += step
  175. }
  176. queryRes.Series = append(queryRes.Series, series)
  177. return queryRes
  178. },
  179. })
  180. registerScenario(&Scenario{
  181. Id: "table_static",
  182. Name: "Table Static",
  183. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  184. timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
  185. to := context.TimeRange.GetToAsMsEpoch()
  186. table := tsdb.Table{
  187. Columns: []tsdb.TableColumn{
  188. {Text: "Time"},
  189. {Text: "Message"},
  190. {Text: "Description"},
  191. {Text: "Value"},
  192. },
  193. Rows: []tsdb.RowValues{},
  194. }
  195. for i := int64(0); i < 10 && timeWalkerMs < to; i++ {
  196. table.Rows = append(table.Rows, tsdb.RowValues{float64(timeWalkerMs), "This is a message", "Description", 23.1})
  197. timeWalkerMs += query.IntervalMs
  198. }
  199. queryRes := tsdb.NewQueryResult()
  200. queryRes.Tables = append(queryRes.Tables, &table)
  201. return queryRes
  202. },
  203. })
  204. }
  205. func getRandomWalk(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResult {
  206. timeWalkerMs := tsdbQuery.TimeRange.GetFromAsMsEpoch()
  207. to := tsdbQuery.TimeRange.GetToAsMsEpoch()
  208. series := newSeriesForQuery(query)
  209. points := make(tsdb.TimeSeriesPoints, 0)
  210. walker := query.Model.Get("startValue").MustFloat64(rand.Float64() * 100)
  211. for i := int64(0); i < 10000 && timeWalkerMs < to; i++ {
  212. points = append(points, tsdb.NewTimePoint(null.FloatFrom(walker), float64(timeWalkerMs)))
  213. walker += rand.Float64() - 0.5
  214. timeWalkerMs += query.IntervalMs
  215. }
  216. series.Points = points
  217. queryRes := tsdb.NewQueryResult()
  218. queryRes.Series = append(queryRes.Series, series)
  219. return queryRes
  220. }
  221. func getRandomWalkTable(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResult {
  222. timeWalkerMs := tsdbQuery.TimeRange.GetFromAsMsEpoch()
  223. to := tsdbQuery.TimeRange.GetToAsMsEpoch()
  224. table := tsdb.Table{
  225. Columns: []tsdb.TableColumn{
  226. {Text: "Time"},
  227. {Text: "Value"},
  228. {Text: "Min"},
  229. {Text: "Max"},
  230. {Text: "Info"},
  231. },
  232. Rows: []tsdb.RowValues{},
  233. }
  234. withNil := query.Model.Get("withNil").MustBool(false)
  235. walker := query.Model.Get("startValue").MustFloat64(rand.Float64() * 100)
  236. spread := 2.5
  237. var info strings.Builder
  238. for i := int64(0); i < query.MaxDataPoints && timeWalkerMs < to; i++ {
  239. delta := rand.Float64() - 0.5
  240. walker += delta
  241. info.Reset()
  242. if delta > 0 {
  243. info.WriteString("up")
  244. } else {
  245. info.WriteString("down")
  246. }
  247. if math.Abs(delta) > .4 {
  248. info.WriteString(" fast")
  249. }
  250. row := tsdb.RowValues{
  251. float64(timeWalkerMs),
  252. walker,
  253. walker - ((rand.Float64() * spread) + 0.01), // Min
  254. walker + ((rand.Float64() * spread) + 0.01), // Max
  255. info.String(),
  256. }
  257. // Add some random null values
  258. if withNil && rand.Float64() > 0.8 {
  259. for i := 1; i < 4; i++ {
  260. if rand.Float64() > .2 {
  261. row[i] = nil
  262. }
  263. }
  264. }
  265. table.Rows = append(table.Rows, row)
  266. timeWalkerMs += query.IntervalMs
  267. }
  268. queryRes := tsdb.NewQueryResult()
  269. queryRes.Tables = append(queryRes.Tables, &table)
  270. return queryRes
  271. }
  272. func registerScenario(scenario *Scenario) {
  273. ScenarioRegistry[scenario.Id] = scenario
  274. }
  275. func newSeriesForQuery(query *tsdb.Query) *tsdb.TimeSeries {
  276. alias := query.Model.Get("alias").MustString("")
  277. if alias == "" {
  278. alias = query.RefId + "-series"
  279. }
  280. return &tsdb.TimeSeries{Name: alias}
  281. }