Skip to content

Commit

Permalink
perf(plugin/prometheus): generate metrics output data with string.buf…
Browse files Browse the repository at this point in the history
…fer (#11065)

This PR follows #11040, continue to optimize the string usage by string.buffer.

KAG-1852

Full changelog

- use table.new for seen_metrics
- buffered_print() outputs a string, not a table
- change printable_metric_data of stream to fit the new buffered_print()
- do not use ipairs to iterate array
- other small optimizations
  • Loading branch information
chronolaw committed Jun 25, 2023
1 parent 05c1837 commit 027d646
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -54,6 +54,7 @@
- The Prometheus plugin has been optimized to reduce proxy latency impacts during scraping.
[#10949](https://github.com/Kong/kong/pull/10949)
[#11040](https://github.com/Kong/kong/pull/11040)
[#11065](https://github.com/Kong/kong/pull/11065)

### Fixes

Expand Down
13 changes: 8 additions & 5 deletions kong/plugins/prometheus/api.lua
@@ -1,17 +1,20 @@
local buffer = require("string.buffer")
local exporter = require "kong.plugins.prometheus.exporter"
local tbl_insert = table.insert
local tbl_concat = table.concat


local printable_metric_data = function(_)
local buffer = {}
local buf = buffer.new(4096)
-- override write_fn, since stream_api expect response to returned
-- instead of ngx.print'ed
exporter.metric_data(function(new_metric_data)
tbl_insert(buffer, tbl_concat(new_metric_data, ""))
buf:put(new_metric_data)
end)

return tbl_concat(buffer, "")
local str = buf:get()

buf:free()

return str
end


Expand Down
2 changes: 1 addition & 1 deletion kong/plugins/prometheus/exporter.lua
Expand Up @@ -90,7 +90,7 @@ end

local function init()
local shm = "prometheus_metrics"
if not ngx.shared.prometheus_metrics then
if not ngx.shared[shm] then
kong.log.err("prometheus: ngx shared dict 'prometheus_metrics' not found")
return
end
Expand Down
46 changes: 27 additions & 19 deletions kong/plugins/prometheus/prometheus.lua
Expand Up @@ -68,9 +68,8 @@ local ipairs = ipairs
local pairs = pairs
local tostring = tostring
local tonumber = tonumber
local st_format = string.format
local table_sort = table.sort
local tb_clear = require("table.clear")
local tb_new = require("table.new")
local yield = require("kong.tools.utils").yield


Expand All @@ -90,6 +89,9 @@ local TYPE_LITERAL = {
-- Default metric name size for string.buffer.new()
local NAME_BUFFER_SIZE_HINT = 256

-- Default metric data size for string.buffer.new()
local DATA_BUFFER_SIZE_HINT = 4096

-- Default name for error metric incremented by this library.
local DEFAULT_ERROR_METRIC_NAME = "nginx_metric_errors_total"

Expand Down Expand Up @@ -399,7 +401,7 @@ local function lookup_or_create(self, label_values)
local bucket_pref
if self.label_count > 0 then
-- strip last }
bucket_pref = self.name .. "_bucket" .. string.sub(labels, 1, #labels-1) .. ","
bucket_pref = self.name .. "_bucket" .. string.sub(labels, 1, -2) .. ","
else
bucket_pref = self.name .. "_bucket{"
end
Expand Down Expand Up @@ -701,8 +703,9 @@ end
-- Returns:
-- an object that should be used to register metrics.
function Prometheus.init(dict_name, options_or_prefix)
if ngx.get_phase() ~= 'init' and ngx.get_phase() ~= 'init_worker' and
ngx.get_phase() ~= 'timer' then
local phase = ngx.get_phase()
if phase ~= 'init' and phase ~= 'init_worker' and
phase ~= 'timer' then
error('Prometheus.init can only be called from ' ..
'init_by_lua_block, init_worker_by_lua_block or timer' , 2)
end
Expand Down Expand Up @@ -912,26 +915,29 @@ function Prometheus:metric_data(write_fn, local_only)
-- numerical order of their label values.
table_sort(keys)

local seen_metrics = {}
local output = {}
local seen_metrics = tb_new(0, count)

-- the output is an integral string, not an array any more
local output = buffer.new(DATA_BUFFER_SIZE_HINT)
local output_count = 0

local function buffered_print(data)
if data then
local function buffered_print(fmt, ...)
if fmt then
output_count = output_count + 1
output[output_count] = data
output:putf(fmt, ...)
end

if output_count >= 100 or not data then
write_fn(output)
if output_count >= 100 or not fmt then
write_fn(output:get()) -- consume the whole buffer
output_count = 0
tb_clear(output)
end
end

for _, key in ipairs(keys) do
for i = 1, count do
yield()

local key = keys[i]

local value, err
local is_local_metrics = true
value = self.local_metrics[key]
Expand All @@ -950,26 +956,28 @@ function Prometheus:metric_data(write_fn, local_only)
local m = self.registry[short_name]
if m then
if m.help then
buffered_print(st_format("# HELP %s%s %s\n",
self.prefix, short_name, m.help))
buffered_print("# HELP %s%s %s\n",
self.prefix, short_name, m.help)
end
if m.typ then
buffered_print(st_format("# TYPE %s%s %s\n",
self.prefix, short_name, TYPE_LITERAL[m.typ]))
buffered_print("# TYPE %s%s %s\n",
self.prefix, short_name, TYPE_LITERAL[m.typ])
end
end
seen_metrics[short_name] = true
end
if not is_local_metrics then -- local metrics is always a gauge
key = fix_histogram_bucket_labels(key)
end
buffered_print(st_format("%s%s %s\n", self.prefix, key, value))
buffered_print("%s%s %s\n", self.prefix, key, value)

::continue::

end

buffered_print(nil)

output:free()
end

-- Present all metrics in a text format compatible with Prometheus.
Expand Down

1 comment on commit 027d646

@khcp-gha-bot
Copy link

Choose a reason for hiding this comment

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

Bazel Build

Docker image available kong/kong:027d64686c0c8b9e3045b8e5aea5aaaec40f6cc9
Artifacts available https://github.com/Kong/kong/actions/runs/5367948147

Please sign in to comment.