-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Potentially long select calls on user spaces tend to be dangerous. A critical log entry containing the current stack traceback is created upon potentially long select calls — a user can explicitly request a full scan though by passing fullscan = true to select's options table argument in which a case a log entry will not be created. Tarantool has a similar implementation[1][2]. 1. tarantool/tarantool#7064 2. tarantool/tarantool#7131 Part of #276
- Loading branch information
1 parent
c0200c9
commit 717dc21
Showing
14 changed files
with
460 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
-- Mostly it's a copy-paste from tarantool/tarantool log.lua: | ||
-- https://github.com/tarantool/tarantool/blob/29654ffe3638e5a218dd32f1788830ff05c1c05c/src/lua/log.lua | ||
-- | ||
-- We have three reasons for the copy-paste: | ||
-- 1. Tarantool has not log.crit() (a function for logging with CRIT level). | ||
-- 2. Only new versions of Tarantool have Ratelimit type. | ||
-- 3. We want own copy of Ratelimit in case the implementation in Tarantool | ||
-- changes. Less pain between Tarantool versions. | ||
local ffi = require('ffi') | ||
|
||
local S_CRIT = ffi.C.S_CRIT | ||
local S_WARN = ffi.C.S_WARN | ||
|
||
local function say(level, fmt, ...) | ||
if ffi.C.log_level < level then | ||
-- don't waste cycles on debug.getinfo() | ||
return | ||
end | ||
local type_fmt = type(fmt) | ||
local format = "%s" | ||
if select('#', ...) ~= 0 then | ||
local stat | ||
stat, fmt = pcall(string.format, fmt, ...) | ||
if not stat then | ||
error(fmt, 3) | ||
end | ||
elseif type_fmt == 'table' then | ||
-- An implementation in tarantool/tarantool supports encoding a table in | ||
-- JSON, but it requires more dependencies from FFI. So we just deleted | ||
-- it because we don't need such encoding in the module. | ||
error("table not supported", 3) | ||
elseif type_fmt ~= 'string' then | ||
fmt = tostring(fmt) | ||
end | ||
|
||
local debug = require('debug') | ||
local frame = debug.getinfo(3, "Sl") | ||
local line, file = 0, 'eval' | ||
if type(frame) == 'table' then | ||
line = frame.currentline or 0 | ||
file = frame.short_src or frame.src or 'eval' | ||
end | ||
|
||
ffi.C._say(level, file, line, nil, format, fmt) | ||
end | ||
|
||
local ratelimit_enabled = true | ||
|
||
local function ratelimit_enable() | ||
ratelimit_enabled = true | ||
end | ||
|
||
local function ratelimit_disable() | ||
ratelimit_enabled = false | ||
end | ||
|
||
local Ratelimit = { | ||
interval = 60, | ||
burst = 10, | ||
emitted = 0, | ||
suppressed = 0, | ||
start = 0, | ||
} | ||
|
||
local function ratelimit_new(object) | ||
return Ratelimit:new(object) | ||
end | ||
|
||
function Ratelimit:new(object) | ||
object = object or {} | ||
setmetatable(object, self) | ||
self.__index = self | ||
return object | ||
end | ||
|
||
function Ratelimit:check() | ||
if not ratelimit_enabled then | ||
return 0, true | ||
end | ||
|
||
local clock = require('clock') | ||
local now = clock.monotonic() | ||
local saved_suppressed = 0 | ||
if now > self.start + self.interval then | ||
saved_suppressed = self.suppressed | ||
self.suppressed = 0 | ||
self.emitted = 0 | ||
self.start = now | ||
end | ||
|
||
if self.emitted < self.burst then | ||
self.emitted = self.emitted + 1 | ||
return saved_suppressed, true | ||
end | ||
self.suppressed = self.suppressed + 1 | ||
return saved_suppressed, false | ||
end | ||
|
||
function Ratelimit:log_check(lvl) | ||
local suppressed, ok = self:check() | ||
if lvl >= S_WARN and suppressed > 0 then | ||
say(S_WARN, '%d messages suppressed due to rate limiting', suppressed) | ||
end | ||
return ok | ||
end | ||
|
||
function Ratelimit:log(lvl, fmt, ...) | ||
if self:log_check(lvl) then | ||
say(lvl, fmt, ...) | ||
end | ||
end | ||
|
||
local function log_ratelimited_closure(lvl) | ||
return function(self, fmt, ...) | ||
self:log(lvl, fmt, ...) | ||
end | ||
end | ||
|
||
Ratelimit.log_crit = log_ratelimited_closure(S_CRIT) | ||
|
||
return { | ||
new = ratelimit_new, | ||
enable = ratelimit_enable, | ||
disable = ratelimit_disable, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,28 @@ | ||
local ratelimit = require('crud.ratelimit') | ||
local check_select_safety_rl = ratelimit.new() | ||
|
||
local common = {} | ||
|
||
common.SELECT_FUNC_NAME = '_crud.select_on_storage' | ||
common.DEFAULT_BATCH_SIZE = 100 | ||
|
||
common.check_select_safety = function(space_name, plan, opts) | ||
if opts.fullscan == true then | ||
return | ||
end | ||
|
||
if opts.first ~= nil and math.abs(opts.first) <= 1000 then | ||
return | ||
end | ||
|
||
local iter = plan.tarantool_iter | ||
if iter == box.index.EQ or iter == box.index.REQ then | ||
return | ||
end | ||
|
||
local rl = check_select_safety_rl | ||
local traceback = debug.traceback() | ||
rl:log_crit("Potentially long select from space '%s'\n %s", space_name, traceback) | ||
end | ||
|
||
return common |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.