[AAAA] => true
}
-.. function:: cache.clear([name], [exact_name], [rr_type], [chunk_size], [callback])
+.. function:: cache.clear([name], [exact_name], [rr_type], [chunk_size], [callback], [prev_state])
Purge cache records matching specified criteria. There are two specifics:
* To reliably remove **negative** cache entries you need to clear subtree with the whole zone. E.g. to clear negative cache entries for (formerly non-existing) record `www.example.com. A` you need to flush whole subtree starting at zone apex, e.g. `example.com.` [#]_.
- * This operation is an asynchonous and might not be yet finished when call to ``cache.clear()`` function returns. Result is indicated return value. You can use custom callback to wait for operation to finish.
+ * This operation is an asynchonous and might not be yet finished when call to ``cache.clear()`` function returns. Return value indicates if clearing continues asynchronously or not.
- :rtype: table
- :return: ``count`` field is always present, other fields are optional.
-
- =========== ===========
- Key Description
- =========== ===========
- count number of items removed from cache by this call
- not_apex indicates that cleared subtree is not cached as zone apex; proofs of non-existence were not removed
- subtree hint where zone apex lies (this is guess from cache content, might not be accurate)
- chunk_limit indicates that more than ``chunk_size`` needs to be cleared, clearing will continue in callback
- =========== ===========
-
- :param string name: if the name isn't provided, whole cache is purged
+ :param string name: subtree to purge; if the name isn't provided, whole cache is purged
(and any other parameters are disregarded).
- Otherwise only records in that subtree are removed.
:param bool exact_name: if set to ``true``, only records with *the same* name are removed;
default: false.
:param kres.type rr_type: you may additionally specify the type to remove,
- but that is only supported with ``exact_name == true``; default: nil;
- :param integer chunk_size: the number of records to remove at one go; default: 100.
+ but that is only supported with ``exact_name == true``; default: nil.
+ :param integer chunk_size: the number of records to remove in one round; default: 100.
The purpose is not to block the resolver for long.
The default ``callback`` repeats the command after one millisecond
until all matching data are cleared.
- :param function callback: custom code to handle result of the underlying C call.
- As the first parameter it gets the return code from :func:`kr_cache_remove_subtree()`,
- and the following parameters are copies of those passed to `cache.clear()`.
+ :param function callback: a custom code to handle result of the underlying C call.
+ Its parameters are copies of those passed to `cache.clear()` with one additional
+ parameter ``rettable`` containing table with return value from current call.
+ ``count`` field contains a return code from :func:`kr_cache_remove_subtree()`.
+ :param table prev_state: return value from previous run (can be used by callback)
+
+ :rtype: table
+ :return: ``count`` key is always present. Other keys are optional and their presense indicate special conditions.
+
+ * **count** *(integer)* - number of items removed from cache by this call (can be 0 if no entry matched criteria)
+ * **not_apex** - cleared subtree is not cached as zone apex; proofs of non-existence were not removed
+ * **subtree** *(string)* - hint where zone apex lies (this is estimation from cache content and might not be accurate)
+ * **chunk_limit** - more than ``chunk_size`` items needs to be cleared, clearing will continue asynchonously
+
Examples:
.. code-block:: lua
-- Clear whole cache
- cache.clear()
- -- Clear records at and below 'bad.cz'
- cache.clear('bad.cz')
-
- .. attention::
-
- To minimize surprises with partial cache removal,
- you may prefer to specify names that have NS/SOA records,
- e.g. ``example.com``. Details: validated NSEC and NSEC3 records
- (which are used for aggressive non-existence proofs)
- will be removed only for zones whose **apex** is at or below the specified name.
+ > cache.clear()
+ [count] => 76
+
+ -- Clear records at and below 'com.'
+ > cache.clear('com.')
+ [chunk_limit] => chunk size limit reached; the default callback will continue asynchronously
+ [not_apex] => to clear proofs of non-existence call cache.clear('com.')
+ [count] => 100
+ [round] => 1
+ [subtree] => com.
+ > worker.sleep(0.1)
+ [cache] asynchonous cache.clear('com', false) finished
+
+ -- Clear only 'www.example.com.'
+ > cache.clear('www.example.com.', true)
+ [round] => 1
+ [count] => 1
+ [not_apex] => to clear proofs of non-existence call cache.clear('example.com.')
+ [subtree] => example.com.
.. [#] This is a consequence of DNSSEC negative cache which relies on proofs of non-existence on various owner nodes. It is impossible to efficiently flush part of DNS zones signed with NSEC3.
local test_exactname = true
local test_rrtype = nil
local test_maxcount = 1
- local function check_callback(errors, name, exact_name, rr_type, chunk_size, callback)
- is(type(errors), 'table', 'callback received table of errors')
+ local test_prev_state = { works = true }
+ local function check_callback(name, exact_name, rr_type, chunk_size, callback, prev_state, errors)
is(errors.count, 1, 'callback received correct # of removed records')
is(test_name, name, 'callback received subtree name')
is(test_exactname, exact_name, 'callback received exact_name')
is(test_rrtype, rrtype, 'callback received rr_type')
is(test_chunksize, chunksize, 'callback received maxcount')
is(check_callback, callback, 'callback received reference to itself')
+ is(type(errors), 'table', 'callback received table of errors')
+ same(test_prev_state, prev_state, 'callback received previous state')
return 666
end
- same(cache.clear(test_name, test_exactname, test_rrtype, test_maxcount, check_callback),
+ same(cache.clear(test_name, test_exactname, test_rrtype, test_maxcount, check_callback, test_prev_state),
666, 'first callback return value is passed to cache.clear() caller')
local cnt_before_wait = cache.count()
worker.sleep(0.2)
test_exact_match_qtype,
test_exact_match_qname,
test_callback,
+ import_zone,
test_subtree,
test_subtree_limit,
test_apex,
})
-cache.clear = function (name, exact_name, rr_type, chunk_size, callback)
+cache.clear = function (name, exact_name, rr_type, chunk_size, callback, prev_state)
if name == nil then -- keep same output format as for 'standard' clear
local total_count = cache.count()
if not cache.clear_everything() then
then error('cache.clear(): incorrect exact_name passed') end
local cach = kres.context().cache;
- local errors = {}
+ local rettable = {}
-- Apex warning. If the caller passes a custom callback,
-- we assume they are advanced enough not to need the check.
-- The point is to avoid repeating the check in each callback iteration.
ffi.gc(names[0], ffi.C.free)
local apex = kres.dname2str(names[0])
if apex ~= name then
- errors.not_apex = 'to clear proofs of non-existence call '
+ rettable.not_apex = 'to clear proofs of non-existence call '
.. 'cache.clear(\'' .. tostring(apex) ..'\')'
- errors.subtree = apex
+ rettable.subtree = apex
end
end
then error('cache.clear(): chunk_size has to be a positive integer') end
-- Do the C call, and add chunk_size warning.
- errors.count = ffi.C.kr_cache_remove_subtree(cach, dname, exact_name, chunk_size)
- if errors.count == chunk_size then
+ rettable.count = ffi.C.kr_cache_remove_subtree(cach, dname, exact_name, chunk_size)
+ if rettable.count == chunk_size then
local msg_extra = ''
if callback == nil then
msg_extra = '; the default callback will continue asynchronously'
end
- errors.chunk_limit = 'chunk size limit of ' .. tostring(chunk_size)
- .. ' entries reached' .. msg_extra
+ rettable.chunk_limit = 'chunk size limit reached' .. msg_extra
end
-- Default callback function: repeat after 1ms
if callback == nil then callback =
- function (cberrors, cbname, cbexact_name, cbrr_type, cbchunk_size, cbself)
- if errors.count < 0 then error(ffi.string(ffi.C.knot_strerror(errors.count))) end
- if (errors.count == cbchunk_size) then
+ function (cbname, cbexact_name, cbrr_type, cbchunk_size, cbself, cbprev_state, cbrettable)
+ if cbrettable.count < 0 then error(ffi.string(ffi.C.knot_strerror(cbrettable.count))) end
+ if cbprev_state == nil then cbprev_state = { round = 0 } end
+ if type(cbprev_state) ~= 'table'
+ then error('cache.clear() callback: incorrect prev_state passed') end
+ cbrettable.round = cbprev_state.round + 1
+ if (cbrettable.count == cbchunk_size) then
event.after(1, function ()
- cache.clear(cbname, cbexact_name, cbrr_type, cbchunk_size, cbself)
+ cache.clear(cbname, cbexact_name, cbrr_type, cbchunk_size, cbself, cbrettable)
end)
- else
- log('[cache] asynchonous clear finished: ' .. table_print(cberrors))
+ elseif cbrettable.round > 1 then
+ log('[cache] asynchonous cache.clear(\'' .. cbname .. '\', '
+ .. tostring(cbexact_name) .. ') finished')
end
- return cberrors
+ return cbrettable
end
end
- return callback(errors, name, exact_name, rr_type, chunk_size, callback)
+ return callback(name, exact_name, rr_type, chunk_size, callback, prev_state, rettable)
end
-- Syntactic sugar for cache
-- `cache[x] -> cache.get(x)`