VolkovLabs / business-charts

The Business Charts panel allows you to integrate charts and graphs created by the Apache ECharts library into your Grafana dashboard.
https://docs.volkovlabs.io
Apache License 2.0
139 stars 17 forks source link

EChart bar is rendered to weird state after dashboard refresh #207

Closed andrejshapal closed 1 year ago

andrejshapal commented 1 year ago

Hello, Issue is reproduced as on grafana v9, as on latest v10. We use latest plugin v5.0.0. We did not notice this issue previously.

My panel setting:

const percentile = replaceVariables('$le');
const gameType = replaceVariables('$gameType');
const cluster = replaceVariables('$cluster');
const browserName = replaceVariables('$browserName');
const operatingSystemName = replaceVariables('$operatingSystemName');
const directLaunch = replaceVariables('$directLaunch');
const cacheBucket = replaceVariables('$cacheBucket');
const mobile = []
const desktop = []
for (var i = 0, leni = (data.series).length; i < leni; i++) {
  const array = {}
  const trueArray = {}
  const deductArray = {}
  const keys = (data.series[i].fields).map(value => value.name)
  const le = data.series[i].fields[_.invert(keys).le].values.buffer
  const device = data.series[i].fields[_.invert(keys).deviceType].values.buffer
  const values = data.series[i].fields[keys.length - 1].values.buffer
  let inf = 0
  for (var m = 0, lenm = le.length; m < lenm; m++) {

    if (le[m] == 'Infinity') {

      inf = inf + values[m]
    }
    else {
      array[le[m]] && array[le[m]][device[m]] ? prev_value = array[le[m]][device[m]] : prev_value = 0
      array[le[m]] ? Object.assign(array[le[m]], { [device[m]]: values[m] + prev_value }) : array[le[m]] = { [device[m]]: values[m] + prev_value }
    }
  }

  trueArray[0] = array[Object.keys(array)[0]]
  let count = 0
  for (var r = 1, lenr = Object.keys(array).length; r < lenr; r++) {
    le_r = Object.keys(array)[r]
    val_mob = array[Object.keys(array)[r]].mobile
    if (!val_mob) { val_mob = 0 }
    val_desk = array[Object.keys(array)[r]].desktop
    if (!val_desk) { val_desk = 0 }
    prev_mob = array[Object.keys(array)[r - 1]].mobile
    if (!prev_mob) { prev_mob = 0 }
    prev_desk = array[Object.keys(array)[r - 1]].desktop
    if (!prev_desk) { prev_desk = 0 }
    count = count + (val_mob - prev_mob) + (val_desk - prev_desk)
    trueArray[le_r] = { mobile: val_mob - prev_mob, desktop: val_desk - prev_desk }
  }
  let to_deduct = count - (count * percentile)
  for (var n = Object.keys(trueArray).length - 1; n >= 0; n--) {
    let mob = trueArray[Object.keys(trueArray)[n]].mobile
    let desk = trueArray[Object.keys(trueArray)[n]].desktop

    if (mob > desk && to_deduct > 0) {
      diff = mob - desk
      if (to_deduct <= diff) {
        mob = mob - to_deduct

      }
      else {
        mob = mob - diff
        to_deduct = to_deduct - diff
        mob = mob - to_deduct / 2
        desk = desk - to_deduct / 2
      }
    }
    else if (desk > mob && to_deduct > 0) {
      diff = desk - mob
      if (to_deduct <= diff) {
        desk = desk - to_deduct
      }
      else {
        desk = desk - diff
        to_deduct = to_deduct - diff
        mob = mob - to_deduct / 2
        desk = desk - to_deduct / 2
      }
    }

    else if (to_deduct > 0) {
      mob = mob - to_deduct / 2
      desk = desk - to_deduct / 2
    }
    if (mob <= 0 && desk <= 0) {
      to_deduct = Math.abs(desk) + Math.abs(mob)
      desk = 0
      mob = 0
    }
    else if (mob <= 0 && desk > 0) {
      to_deduct = Math.abs(mob)
      mob = 0
      deductArray[Object.keys(trueArray)[n]] = { mobile: mob, desktop: desk }

    }
    else if (mob > 0 && desk <= 0) {
      to_deduct = Math.abs(desk)
      desk = 0
      deductArray[Object.keys(trueArray)[n]] = { mobile: mob, desktop: desk }
    }
    else if (mob > 0 && desk > 0) {
      to_deduct = 0
      deductArray[Object.keys(trueArray)[n]] = { mobile: mob, desktop: desk }

    }
  }

  let sum_mob = 0
  let sum_desk = 0
  for (var v = 0, lenv = (Object.keys(deductArray)).length; v < lenv; v++) {
    sum_mob = sum_mob + deductArray[Object.keys(deductArray)[v]].mobile * Object.keys(deductArray)[v]
    sum_desk = sum_desk + deductArray[Object.keys(deductArray)[v]].desktop * Object.keys(deductArray)[v]
  }
  let count_mob = 0
  let count_desk = 0
  for (var v = 0, lenv = (Object.keys(deductArray)).length; v < lenv; v++) {
    count_mob = count_mob + deductArray[Object.keys(deductArray)[v]].mobile
    count_desk = count_desk + deductArray[Object.keys(deductArray)[v]].desktop

  }
  mobile.push([(sum_mob / count_mob).toFixed(0), data.series[i].refId, (sum_mob / count_mob).toFixed(0)])
  desktop.push([(sum_desk / count_desk).toFixed(0), data.series[i].refId, (sum_desk / count_desk).toFixed(0)])
}
return option = {
  backgroundColor: (theme.colors.background.primary),
  grid: { containLabel: true, left: '5%' },
  xAxis: {
    axisLabel: {
      formatter: '{value} ms',
      show: false
    },
    show: false
  },
  yAxis: {
    type: 'category',
    inverse: true,
    axisLabel: {
      fontSize: 16,
      fontWeight: 'bold',
      align: 'right',
    },
    axisLine: {
      show: false
    },
    axisTick: {
      show: false
    }
  },
  visualMap: {
    show: false,
    orient: 'horizontal',
    left: 'center',
    formatter: '{value} ms',
    min: 0,
    max: 5000,
    text: ['High Score', 'Low Score'],
    // Map the score column to color
    dimension: 0,
    inRange: {
      color: ['#005b96', '#ecb939', '#FD665F']
    }
  },
  series: [
    {
      type: 'bar',
      data: desktop,
      name: 'Desktop',
      encode: {
        // Map the "amount" column to X axis.
        x: 'value',
        // Map the "product" column to Y axis
        y: 'type'
      },
      label: {
        show: true,
        position: 'right',
        valueAnimation: true,
        formatter: '{@value} ms',
        rich: {
          name: {}
        },
        fontSize: 16,
        fontWeight: 'bold',
        textBorderWidth: 0,
        textBorderColor: (theme.colors.background.primary)
      }
    }
  ]
};

https://github.com/VolkovLabs/volkovlabs-echarts-panel/assets/96792836/a18ff432-3c4c-4d6d-ab54-731335cf5b90

What is hapenning: After refresh, the echart panel shows some data which is not present anywehere (I was checking every step of calculation in every loop). After some actions to rerender panel, the data back to correct state.

Steps to reproduce:

  1. Load dashboard.
  2. Change time range.
  3. After data loaded, see an issue.
  4. Change size of panel.
  5. Data is back to normal.

Expected result: Panel is rendered after data is loaded and calculated.

andrejshapal commented 1 year ago

As ususally, found how to resolve when already asked for help. This option fixes issue: return option = { animation: false, Sure, chart not animated.

Maybe you still can take a look and problem can be in plugin.

mikhail-vl commented 1 year ago

@andrejshapal, could you try Apache ECharts 4.5.0?

We removed clearing the chart, which was an issue with Streaming, and I am thinking that it may lead to this issue with animation enabled.

mikhail-vl commented 1 year ago

@andrejshapal, have you had a chance to test on 4.5.0?

I plan to release a new version today, and I want to ensure it's not an issue with v5.

mikhail-vl commented 1 year ago

@andrejshapal Also, if you use Canvas render in the panel options, please test with SVG renderer.

Another community member had a similar ghosting issue resolved using an SVG renderer.

andrejshapal commented 1 year ago

@mikhail-vl SVG did not fix issue. 4.5.0 did.

mikhail-vl commented 1 year ago

@andrejshapal I found this old issue in the Apache EChart: https://github.com/apache/echarts/issues/6202.

Thank you for confirming the problem with v5. One of the changes we made is to replace clear() and the default merge option to not Merge with removed clear() for streaming.

I could not reproduce the issue using your code so far to confirm the fix. Could you try the possible fix in the signed CI artifact on your QA? https://github.com/VolkovLabs/volkovlabs-echarts-panel/actions/runs/5836485857

@asimonok, please take a look if I am missing anything.

andrejshapal commented 1 year ago

@mikhail-vl The 5.1.0 version did not resolve an issue. Neither from pipeline, neither from grafana library.

image

Also, I doublechecked and on the same environment and in 4.5.0 issue did not persist. The bug you refered to looks a bit different. In their case the data is getting drawn over the old data. In my case I found the following correlation:

Initial value: 2075 Rendered value: -42075 Expected value: 2071 (2075-2071)2075 => -42075

Initial value: 900 Rendered value: 145900 Expected value: 755 (900-755)2075 => 145900

So, basically, it shows Why it does so, I have no idea.

mikhail-vl commented 1 year ago

@andrejshapal Thank you for testing.

I found the same correlation that the difference is displayed in front of the initial value. It was an idea that the previous elements were not cleaned properly before the animation started.

We will continue the investigation next week.

asimonok commented 1 year ago

Hi @andrejshapal

I was able to reproduce the issue. The problem is passing string values. Method Number.toFixed returns string - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed So passing numbers instead of strings fixes the issue. Please look at the comment to see more details - https://github.com/VolkovLabs/volkovlabs-echarts-panel/pull/209#issuecomment-1676835718

Please adjust the code by passing data as numbers instead of string

mobile.push([parseInt((sum_mob / count_mob).toFixed(0)), data.series[i].refId, parseInt((sum_mob / count_mob).toFixed(0))])
desktop.push([parseInt((sum_desk / count_desk).toFixed(0)), data.series[i].refId, parseInt((sum_desk / count_desk).toFixed(0))])

Please let us know if the issue still exists for you after changes.

andrejshapal commented 1 year ago

@asimonok Hello. I can confirm issue is fixed by converting values to int.