Skip to content

Commit

Permalink
perf(*) make some heavily used code paths more cooperative by yielding (
Browse files Browse the repository at this point in the history
#8888)

### Summary

Adds yielding to some hot paths that otherwise may cause latency spikes
with huge Kong configurations. The special focus was to make CP/DP config
changes less blocking.
  • Loading branch information
bungle authored Jun 2, 2022
1 parent c54a2e9 commit 95d704e
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 19 deletions.
4 changes: 3 additions & 1 deletion kong/api/routes/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ local errors = require("kong.db.errors")

local kong = kong
local ngx = ngx
local dc = declarative.new_config(kong.configuration)
local type = type
local table = table
local tostring = tostring

Expand Down Expand Up @@ -71,6 +71,8 @@ return {
end
self.params.check_hash = nil

local dc = declarative.new_config(kong.configuration)

local entities, _, err_t, meta, new_hash
if self.params._format_version then
entities, _, err_t, meta, new_hash = dc:parse_table(self.params)
Expand Down
12 changes: 11 additions & 1 deletion kong/clustering/control_plane.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ local setmetatable = setmetatable
local type = type
local pcall = pcall
local pairs = pairs
local yield = utils.yield
local ipairs = ipairs
local tonumber = tonumber
local ngx = ngx
Expand Down Expand Up @@ -235,11 +236,20 @@ function _M:export_deflated_reconfigure_payload()

self.reconfigure_payload = payload

payload, err = deflate_gzip(cjson_encode(payload))
payload, err = cjson_encode(payload)
if not payload then
return nil, err
end

yield()

payload, err = deflate_gzip(payload)
if not payload then
return nil, err
end

yield()

self.current_hashes = hashes
self.current_config_hash = config_hash
self.deflated_reconfigure_payload = payload
Expand Down
9 changes: 7 additions & 2 deletions kong/clustering/data_plane.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ local ws_client = require("resty.websocket.client")
local cjson = require("cjson.safe")
local declarative = require("kong.db.declarative")
local constants = require("kong.constants")
local utils = require("kong.tools.utils")


local assert = assert
local setmetatable = setmetatable
local math = math
Expand All @@ -20,8 +23,8 @@ local cjson_encode = cjson.encode
local kong = kong
local exiting = ngx.worker.exiting
local ngx_time = ngx.time

local inflate_gzip = require("kong.tools.utils").inflate_gzip
local inflate_gzip = utils.inflate_gzip
local yield = utils.yield


local KONG_VERSION = kong.version
Expand Down Expand Up @@ -247,8 +250,10 @@ function _M:communicate(premature)

if typ == "binary" then
data = assert(inflate_gzip(data))
yield()

local msg = assert(cjson_decode(data))
yield()

if msg.type == "reconfigure" then
if msg.timestamp then
Expand Down
5 changes: 5 additions & 0 deletions kong/clustering/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ local assert = assert
local error = error
local concat = table.concat
local pairs = pairs
local yield = require("kong.tools.utils").yield
local sort = table.sort
local type = type


local ngx_ERR = ngx.ERR
local ngx_DEBUG = ngx.DEBUG


local DECLARATIVE_EMPTY_CONFIG_HASH = constants.DECLARATIVE_EMPTY_CONFIG_HASH
local _log_prefix = "[clustering] "

Expand All @@ -35,6 +38,8 @@ local MT = { __index = _M, }


local function to_sorted_string(value)
yield(true)

if value == ngx_null then
return "/null/"
end
Expand Down
4 changes: 3 additions & 1 deletion kong/db/declarative/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,7 @@ function declarative.load_into_cache(entities, meta, hash)
end

for tag_name, tags in pairs(tags_by_name) do
yield()
yield(true)

-- tags:admin|@list -> all tags tagged "admin", regardless of the entity type
-- each tag is encoded as a string with the format "admin|services|uuid", where uuid is the service uuid
Expand Down Expand Up @@ -942,6 +942,8 @@ function declarative.load_into_cache(entities, meta, hash)
kong.core_cache:purge()
kong.cache:purge()

yield()

return true, nil, default_workspace
end

Expand Down
5 changes: 5 additions & 0 deletions kong/db/schema/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ local insert = table.insert
local format = string.format
local unpack = unpack
local assert = assert
local yield = utils.yield
local pairs = pairs
local pcall = pcall
local floor = math.floor
Expand Down Expand Up @@ -846,6 +847,8 @@ local validate_fields
-- @return true if the field validates correctly;
-- nil and an error message on failure.
function Schema:validate_field(field, value)
yield(true)

if value == null then
if field.ne == null then
return nil, field.err or validation_errors.NE:format("null")
Expand Down Expand Up @@ -1601,6 +1604,8 @@ end
-- appropriate updated values (except for "select" context
-- it does it in place by modifying the data directly).
function Schema:process_auto_fields(data, context, nulls, opts)
yield(true)

local check_immutable_fields = false

local is_select = context == "select"
Expand Down
8 changes: 7 additions & 1 deletion kong/db/schema/others/declarative_config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ local null = ngx.null
local type = type
local next = next
local pairs = pairs
local yield = utils.yield
local ipairs = ipairs
local insert = table.insert
local concat = table.concat
local tostring = tostring
local cjson_encode = require("cjson.safe").encode
local yield = require("kong.tools.utils").yield


local DeclarativeConfig = {}

Expand Down Expand Up @@ -276,6 +277,8 @@ end

local function populate_references(input, known_entities, by_id, by_key, expected, parent_entity)
for _, entity in ipairs(known_entities) do
yield(true)

if type(input[entity]) ~= "table" then
goto continue
end
Expand All @@ -295,6 +298,7 @@ local function populate_references(input, known_entities, by_id, by_key, expecte

local entity_schema = all_schemas[entity]
for i, item in ipairs(input[entity]) do
yield(true)

populate_references(item, known_entities, by_id, by_key, expected, entity)

Expand Down Expand Up @@ -352,6 +356,8 @@ local function validate_references(self, input)
local errors = {}

for a, as in pairs(expected) do
yield(true)

for b, bs in pairs(as) do
for _, k in ipairs(bs) do
local key = k.value
Expand Down
2 changes: 0 additions & 2 deletions kong/db/strategies/off/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,6 @@ local function page_for_key(self, key, size, offset, options)
break
end

yield(true)

-- Tags are stored in the cache entries "tags||@list" and "tags:<tagname>|@list"
-- The contents of both of these entries is an array of strings
-- Each of these strings has the form "<tag>|<entity_name>|<entity_id>"
Expand Down
26 changes: 15 additions & 11 deletions kong/tools/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1418,20 +1418,24 @@ _M.topological_sort = topological_sort


do
local counter = 0
function _M.yield(in_loop, phase)
phase = phase or get_phase()
if phase == "init" or phase == "init_worker" then
return
end
if in_loop then
counter = counter + 1
if counter % YIELD_ITERATIONS ~= 0 then
if ngx.IS_CLI then
function _M.yield() end
else
local counter = 0
function _M.yield(in_loop, phase)
phase = phase or get_phase()
if phase == "init" or phase == "init_worker" then
return
end
counter = 0
if in_loop then
counter = counter + 1
if counter % YIELD_ITERATIONS ~= 0 then
return
end
counter = 0
end
ngx_sleep(0)
end
ngx_sleep(0)
end
end

Expand Down

0 comments on commit 95d704e

Please sign in to comment.