Skip to content

Optimize label handling with predefined label keys in counter/gauge #508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

p0rtale
Copy link

@p0rtale p0rtale commented Jun 25, 2025

This change adds support for predefined label keys in counter and gauge metric constructors, optimizing label processing.

Changes:

  • Add optional label_keys table to counter() and gauge() initialization
  • Replace dynamic label sorting with direct key generation when label_keys are provided, eliminating unnecessary processing overhead

Dynamic labels still supported when label_keys is omitted.

Closes TNTP-3453

@p0rtale
Copy link
Author

p0rtale commented Jun 25, 2025

variable labels: 83.25 s
fixed labels: 43.94 s

Bench script
local metrics = require('metrics')
local clock = require('clock')
local log = require('log')

local GAUGES_COUNT = 75
local KEYS_COUNT = 10000
local OPS_COUNT = 50000000

local label_keys = {
    "service", "version", "env", "region", "status", "deployment"
}

local label_values = {
    service = { "auth", "cart", "payment", "storage", "analytics", "notify", "gateway" },
    version = { "v1.0", "v1.1", "v1.2", "v2.0", "v2.1", "v2.2", "v3.0-beta" },
    env = { "prod", "stage", "dev", "test", "canary" },
    region = { "eu-west", "us-east", "us-west", "asia-south", "asia-east" },
    status = { "active", "inactive", "draining", "booting" },
    deployment = { "blue", "green", "red", "canary" }
}

local function generate_random_labels()
    local labels = {}
    for _, key in ipairs(label_keys) do
        local values = label_values[key]
        labels[key] = values[math.random(1, #values)]
    end
    return labels
end

local label_combinations = {}
for _ = 1, KEYS_COUNT do
    table.insert(label_combinations, generate_random_labels())
end

local var_gauges = {}
local fixed_gauges = {}
for i = 1, GAUGES_COUNT do
    var_gauges[i] = metrics.gauge('var_gauge_' .. i, 'Description for gauge ' .. i)
    fixed_gauges[i] = metrics.gauge('fixed_gauge_' .. i, 'Description for gauge ' .. i, {}, label_keys)
end

local function run_bench(gauges)
    local start_time = clock.monotonic()

    for _ = 1, OPS_COUNT do
        local gauge_idx = math.random(1, #gauges)
        local label_idx = math.random(1, #label_combinations)
        local value = math.random(1, 1000)

        gauges[gauge_idx]:set(value, label_combinations[label_idx])
    end

    return clock.monotonic() - start_time
end

local t1 = run_bench(var_gauges)
local t2 = run_bench(fixed_gauges)
local t3 = run_bench(var_gauges)
local t4 = run_bench(fixed_gauges)

log.info("variable labels: %.10f s", (t1 + t3) / 2)
log.info("fixed labels: %.10f s", (t2 + t4) / 2)

Copy link
Member

@DifferentialOrange DifferentialOrange left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the motivation? Is it the optimization or more assertive API for metrics users? If it's the optimization, I think the solution can be expanded to remove redundant table allocation (parts = {}) or even table.concat. As far as I remember, they also take a lot of work.

We shouldn't forget to

  1. add a CHANGELOG entry,
  2. update the API documentation (like here).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants