Eval() divison operator always returns zero

kapacitor

#1

I’m stumped. I’m trying to get a TICKscript to determine the rate of errors, but the result of the division operator (/) is always zero.

I’m following the example elsewhere in this community, and have this code (full script at the bottom of this post):

|eval(lambda: "totalErrors.count" / "totals.count")
        .as('rate')
...
 |alert()
            .message('Count: {{ index .Fields "totals.count" }}; Errors: {{ index .Fields "totalErrors.count" }} Rate: {{ index .Fields "rate" }} Rate2: {{ index .Fields "rate2"}}')

If I feed it a bunch of data, including good and bad values, I get a message in the logs like this:

2017/09/13 08:10:28 D! CRITICAL alert triggered id: msg:Count: 32; Errors: 24 Rate: 0 Rate2: 0 data:&{api_detail map[] [time rate rate2 totalErrors.count totals.count] [[2017-09-13 13:10:11.781652695 +0000 UTC 0 0 24 32]]}

Notice: Count: 32; Errors: 24 Rate: 0 Rate2: 0

How can that be? What am I missing?

I’ve substituted in the actual values and gotten zero:

eval(lambda: 32 / 24).as('rate')

I’ve multiplied by 100 (thinking maybe it’s integer rounding to zero) but got no happiness with either of these:

eval(lambda: ("totalErrors.count" / "totals.count") * 100 )

eval(lambda: ("totalErrors.count" * 100) / ("totals.count" * 100) )

However, I’ve plugged in addition, subtraction and multiplication and all of those work, producing the expected result.

Any ideas what’s up? I’m puzzled.


var data = stream
        |from()
                .measurement('api_detail')
        |window()
                .period(20s)
                .every(5s)
        |log().prefix('initial stream: ').level('DEBUG')

var totalRecords = data
        |count('duration')
        |log().prefix('totalRecords: ').level('DEBUG')

var totalErrors = data
        |where(lambda: "tr-error" == 'true')
        |log().prefix('totalErrors (pre-count): ').level('DEBUG')
        |count('duration')
        |log().prefix('totalErrors (post-count): ').level('DEBUG')

totalErrors
        |log().prefix('pre-join: ').level('DEBUG')
        |join(totalRecords)
                .fill(0)
                .as('totalErrors','totals')
        |eval(lambda: ("totalErrors.count" * 100) / ("totals.count" * 100) )
                .as('rate')
                .keep()
        |eval(lambda: 12 / 16 )
                .as('rate2')
                .keep()
        |log().prefix('pre-alert: ').level('DEBUG')
        |alert()
                .id('{{ index .Tags "api"}}')
                .message('Count: {{ index .Fields "totals.count" }}; Errors: {{ index .Fields "totalErrors.count" }} Rate: {{ index .Fields "rate" }} Rate2: {{ index .Fields "rate2"}}')
                .crit(lambda: TRUE)
                // Whenever we get an alert write it to a file.
                .log('/tmp/alerts.log')

#2

I found the cause and a solution. The cause is that the numbers in the values are integers, which round to zero.

A solution that floats my boat is to specify the data types with functions:

eval(lambda: float(“totalErrors.count”) / float(“totals.count”) )

The rate comes out as: "rate":0.75