273 lines
23 KiB
Plaintext
273 lines
23 KiB
Plaintext
6be3e7da12c7bf9f973845cf07967d35
|
|
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.drain = void 0;
|
|
const content = `--[[
|
|
Drains the queue, removes all jobs that are waiting
|
|
or delayed, but not active, completed or failed
|
|
Input:
|
|
KEYS[1] 'wait',
|
|
KEYS[2] 'paused'
|
|
KEYS[3] 'delayed'
|
|
KEYS[4] 'prioritized'
|
|
KEYS[5] 'jobschedulers' (repeat)
|
|
ARGV[1] queue key prefix
|
|
ARGV[2] should clean delayed jobs
|
|
]]
|
|
local rcall = redis.call
|
|
local queueBaseKey = ARGV[1]
|
|
--[[
|
|
Functions to remove jobs.
|
|
]]
|
|
-- Includes
|
|
--[[
|
|
Function to filter out jobs to ignore from a table.
|
|
]]
|
|
local function filterOutJobsToIgnore(jobs, jobsToIgnore)
|
|
local filteredJobs = {}
|
|
for i = 1, #jobs do
|
|
if not jobsToIgnore[jobs[i]] then
|
|
table.insert(filteredJobs, jobs[i])
|
|
end
|
|
end
|
|
return filteredJobs
|
|
end
|
|
--[[
|
|
Functions to remove jobs.
|
|
]]
|
|
-- Includes
|
|
--[[
|
|
Function to remove job.
|
|
]]
|
|
-- Includes
|
|
--[[
|
|
Function to remove deduplication key if needed
|
|
when a job is being removed.
|
|
]]
|
|
local function removeDeduplicationKeyIfNeededOnRemoval(prefixKey,
|
|
jobKey, jobId)
|
|
local deduplicationId = rcall("HGET", jobKey, "deid")
|
|
if deduplicationId then
|
|
local deduplicationKey = prefixKey .. "de:" .. deduplicationId
|
|
local currentJobId = rcall('GET', deduplicationKey)
|
|
if currentJobId and currentJobId == jobId then
|
|
return rcall("DEL", deduplicationKey)
|
|
end
|
|
end
|
|
end
|
|
--[[
|
|
Function to remove job keys.
|
|
]]
|
|
local function removeJobKeys(jobKey)
|
|
return rcall("DEL", jobKey, jobKey .. ':logs', jobKey .. ':dependencies',
|
|
jobKey .. ':processed', jobKey .. ':failed', jobKey .. ':unsuccessful')
|
|
end
|
|
--[[
|
|
Check if this job has a parent. If so we will just remove it from
|
|
the parent child list, but if it is the last child we should move the parent to "wait/paused"
|
|
which requires code from "moveToFinished"
|
|
]]
|
|
-- Includes
|
|
--[[
|
|
Function to add job in target list and add marker if needed.
|
|
]]
|
|
-- Includes
|
|
--[[
|
|
Add marker if needed when a job is available.
|
|
]]
|
|
local function addBaseMarkerIfNeeded(markerKey, isPausedOrMaxed)
|
|
if not isPausedOrMaxed then
|
|
rcall("ZADD", markerKey, 0, "0")
|
|
end
|
|
end
|
|
local function addJobInTargetList(targetKey, markerKey, pushCmd, isPausedOrMaxed, jobId)
|
|
rcall(pushCmd, targetKey, jobId)
|
|
addBaseMarkerIfNeeded(markerKey, isPausedOrMaxed)
|
|
end
|
|
--[[
|
|
Functions to destructure job key.
|
|
Just a bit of warning, these functions may be a bit slow and affect performance significantly.
|
|
]]
|
|
local getJobIdFromKey = function (jobKey)
|
|
return string.match(jobKey, ".*:(.*)")
|
|
end
|
|
local getJobKeyPrefix = function (jobKey, jobId)
|
|
return string.sub(jobKey, 0, #jobKey - #jobId)
|
|
end
|
|
--[[
|
|
Function to check for the meta.paused key to decide if we are paused or not
|
|
(since an empty list and !EXISTS are not really the same).
|
|
]]
|
|
local function getTargetQueueList(queueMetaKey, activeKey, waitKey, pausedKey)
|
|
local queueAttributes = rcall("HMGET", queueMetaKey, "paused", "concurrency", "max", "duration")
|
|
if queueAttributes[1] then
|
|
return pausedKey, true, queueAttributes[3], queueAttributes[4]
|
|
else
|
|
if queueAttributes[2] then
|
|
local activeCount = rcall("LLEN", activeKey)
|
|
if activeCount >= tonumber(queueAttributes[2]) then
|
|
return waitKey, true, queueAttributes[3], queueAttributes[4]
|
|
else
|
|
return waitKey, false, queueAttributes[3], queueAttributes[4]
|
|
end
|
|
end
|
|
end
|
|
return waitKey, false, queueAttributes[3], queueAttributes[4]
|
|
end
|
|
local function _moveParentToWait(parentPrefix, parentId, emitEvent)
|
|
local parentTarget, isPausedOrMaxed = getTargetQueueList(parentPrefix .. "meta", parentPrefix .. "active",
|
|
parentPrefix .. "wait", parentPrefix .. "paused")
|
|
addJobInTargetList(parentTarget, parentPrefix .. "marker", "RPUSH", isPausedOrMaxed, parentId)
|
|
if emitEvent then
|
|
local parentEventStream = parentPrefix .. "events"
|
|
rcall("XADD", parentEventStream, "*", "event", "waiting", "jobId", parentId, "prev", "waiting-children")
|
|
end
|
|
end
|
|
local function removeParentDependencyKey(jobKey, hard, parentKey, baseKey, debounceId)
|
|
if parentKey then
|
|
local parentDependenciesKey = parentKey .. ":dependencies"
|
|
local result = rcall("SREM", parentDependenciesKey, jobKey)
|
|
if result > 0 then
|
|
local pendingDependencies = rcall("SCARD", parentDependenciesKey)
|
|
if pendingDependencies == 0 then
|
|
local parentId = getJobIdFromKey(parentKey)
|
|
local parentPrefix = getJobKeyPrefix(parentKey, parentId)
|
|
local numRemovedElements = rcall("ZREM", parentPrefix .. "waiting-children", parentId)
|
|
if numRemovedElements == 1 then
|
|
if hard then -- remove parent in same queue
|
|
if parentPrefix == baseKey then
|
|
removeParentDependencyKey(parentKey, hard, nil, baseKey, nil)
|
|
removeJobKeys(parentKey)
|
|
if debounceId then
|
|
rcall("DEL", parentPrefix .. "de:" .. debounceId)
|
|
end
|
|
else
|
|
_moveParentToWait(parentPrefix, parentId)
|
|
end
|
|
else
|
|
_moveParentToWait(parentPrefix, parentId, true)
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
else
|
|
local parentAttributes = rcall("HMGET", jobKey, "parentKey", "deid")
|
|
local missedParentKey = parentAttributes[1]
|
|
if( (type(missedParentKey) == "string") and missedParentKey ~= ""
|
|
and (rcall("EXISTS", missedParentKey) == 1)) then
|
|
local parentDependenciesKey = missedParentKey .. ":dependencies"
|
|
local result = rcall("SREM", parentDependenciesKey, jobKey)
|
|
if result > 0 then
|
|
local pendingDependencies = rcall("SCARD", parentDependenciesKey)
|
|
if pendingDependencies == 0 then
|
|
local parentId = getJobIdFromKey(missedParentKey)
|
|
local parentPrefix = getJobKeyPrefix(missedParentKey, parentId)
|
|
local numRemovedElements = rcall("ZREM", parentPrefix .. "waiting-children", parentId)
|
|
if numRemovedElements == 1 then
|
|
if hard then
|
|
if parentPrefix == baseKey then
|
|
removeParentDependencyKey(missedParentKey, hard, nil, baseKey, nil)
|
|
removeJobKeys(missedParentKey)
|
|
if parentAttributes[2] then
|
|
rcall("DEL", parentPrefix .. "de:" .. parentAttributes[2])
|
|
end
|
|
else
|
|
_moveParentToWait(parentPrefix, parentId)
|
|
end
|
|
else
|
|
_moveParentToWait(parentPrefix, parentId, true)
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
local function removeJob(jobId, hard, baseKey, shouldRemoveDeduplicationKey)
|
|
local jobKey = baseKey .. jobId
|
|
removeParentDependencyKey(jobKey, hard, nil, baseKey)
|
|
if shouldRemoveDeduplicationKey then
|
|
removeDeduplicationKeyIfNeededOnRemoval(baseKey, jobKey, jobId)
|
|
end
|
|
removeJobKeys(jobKey)
|
|
end
|
|
local function removeJobs(keys, hard, baseKey, max)
|
|
for i, key in ipairs(keys) do
|
|
removeJob(key, hard, baseKey, true --[[remove debounce key]])
|
|
end
|
|
return max - #keys
|
|
end
|
|
local function getListItems(keyName, max)
|
|
return rcall('LRANGE', keyName, 0, max - 1)
|
|
end
|
|
local function removeListJobs(keyName, hard, baseKey, max, jobsToIgnore)
|
|
local jobs = getListItems(keyName, max)
|
|
if jobsToIgnore then
|
|
jobs = filterOutJobsToIgnore(jobs, jobsToIgnore)
|
|
end
|
|
local count = removeJobs(jobs, hard, baseKey, max)
|
|
rcall("LTRIM", keyName, #jobs, -1)
|
|
return count
|
|
end
|
|
-- Includes
|
|
--[[
|
|
Function to loop in batches.
|
|
Just a bit of warning, some commands as ZREM
|
|
could receive a maximum of 7000 parameters per call.
|
|
]]
|
|
local function batches(n, batchSize)
|
|
local i = 0
|
|
return function()
|
|
local from = i * batchSize + 1
|
|
i = i + 1
|
|
if (from <= n) then
|
|
local to = math.min(from + batchSize - 1, n)
|
|
return from, to
|
|
end
|
|
end
|
|
end
|
|
--[[
|
|
Function to get ZSet items.
|
|
]]
|
|
local function getZSetItems(keyName, max)
|
|
return rcall('ZRANGE', keyName, 0, max - 1)
|
|
end
|
|
local function removeZSetJobs(keyName, hard, baseKey, max, jobsToIgnore)
|
|
local jobs = getZSetItems(keyName, max)
|
|
if jobsToIgnore then
|
|
jobs = filterOutJobsToIgnore(jobs, jobsToIgnore)
|
|
end
|
|
local count = removeJobs(jobs, hard, baseKey, max)
|
|
if(#jobs > 0) then
|
|
for from, to in batches(#jobs, 7000) do
|
|
rcall("ZREM", keyName, unpack(jobs, from, to))
|
|
end
|
|
end
|
|
return count
|
|
end
|
|
-- We must not remove delayed jobs if they are associated to a job scheduler.
|
|
local scheduledJobs = {}
|
|
local jobSchedulers = rcall("ZRANGE", KEYS[5], 0, -1, "WITHSCORES")
|
|
-- For every job scheduler, get the current delayed job id.
|
|
for i = 1, #jobSchedulers, 2 do
|
|
local jobSchedulerId = jobSchedulers[i]
|
|
local jobSchedulerMillis = jobSchedulers[i + 1]
|
|
local delayedJobId = "repeat:" .. jobSchedulerId .. ":" .. jobSchedulerMillis
|
|
scheduledJobs[delayedJobId] = true
|
|
end
|
|
removeListJobs(KEYS[1], true, queueBaseKey, 0, scheduledJobs) -- wait
|
|
removeListJobs(KEYS[2], true, queueBaseKey, 0, scheduledJobs) -- paused
|
|
if ARGV[2] == "1" then
|
|
removeZSetJobs(KEYS[3], true, queueBaseKey, 0, scheduledJobs) -- delayed
|
|
end
|
|
removeZSetJobs(KEYS[4], true, queueBaseKey, 0, scheduledJobs) -- prioritized
|
|
`;
|
|
exports.drain = {
|
|
name: 'drain',
|
|
content,
|
|
keys: 5,
|
|
};
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJmaWxlIjoiRTpcXG5wLWRtc1xcbGNicDNcXG5vZGVfbW9kdWxlc1xcLnBucG1cXGJ1bGxtcUA1LjY1LjBcXG5vZGVfbW9kdWxlc1xcYnVsbG1xXFxkaXN0XFxjanNcXHNjcmlwdHNcXGRyYWluLTUuanMiLCJtYXBwaW5ncyI6IkFBQUEsWUFBWSxDQUFDO0FBQ2IsTUFBTSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7QUFDOUQsT0FBTyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUMsQ0FBQztBQUN2QixNQUFNLE9BQU8sR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztDQXNRZixDQUFDO0FBQ0YsT0FBTyxDQUFDLEtBQUssR0FBRztJQUNaLElBQUksRUFBRSxPQUFPO0lBQ2IsT0FBTztJQUNQLElBQUksRUFBRSxDQUFDO0NBQ1YsQ0FBQyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyJFOlxcbnAtZG1zXFxsY2JwM1xcbm9kZV9tb2R1bGVzXFwucG5wbVxcYnVsbG1xQDUuNjUuMFxcbm9kZV9tb2R1bGVzXFxidWxsbXFcXGRpc3RcXGNqc1xcc2NyaXB0c1xcZHJhaW4tNS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJcInVzZSBzdHJpY3RcIjtcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmV4cG9ydHMuZHJhaW4gPSB2b2lkIDA7XG5jb25zdCBjb250ZW50ID0gYC0tW1tcbiAgRHJhaW5zIHRoZSBxdWV1ZSwgcmVtb3ZlcyBhbGwgam9icyB0aGF0IGFyZSB3YWl0aW5nXG4gIG9yIGRlbGF5ZWQsIGJ1dCBub3QgYWN0aXZlLCBjb21wbGV0ZWQgb3IgZmFpbGVkXG4gIElucHV0OlxuICAgIEtFWVNbMV0gJ3dhaXQnLFxuICAgIEtFWVNbMl0gJ3BhdXNlZCdcbiAgICBLRVlTWzNdICdkZWxheWVkJ1xuICAgIEtFWVNbNF0gJ3ByaW9yaXRpemVkJ1xuICAgIEtFWVNbNV0gJ2pvYnNjaGVkdWxlcnMnIChyZXBlYXQpXG4gICAgQVJHVlsxXSAgcXVldWUga2V5IHByZWZpeFxuICAgIEFSR1ZbMl0gIHNob3VsZCBjbGVhbiBkZWxheWVkIGpvYnNcbl1dXG5sb2NhbCByY2FsbCA9IHJlZGlzLmNhbGxcbmxvY2FsIHF1ZXVlQmFzZUtleSA9IEFSR1ZbMV1cbi0tW1tcbiAgRnVuY3Rpb25zIHRvIHJlbW92ZSBqb2JzLlxuXV1cbi0tIEluY2x1ZGVzXG4tLVtbXG4gIEZ1bmN0aW9uIHRvIGZpbHRlciBvdXQgam9icyB0byBpZ25vcmUgZnJvbSBhIHRhYmxlLlxuXV1cbmxvY2FsIGZ1bmN0aW9uIGZpbHRlck91dEpvYnNUb0lnbm9yZShqb2JzLCBqb2JzVG9JZ25vcmUpXG4gIGxvY2FsIGZpbHRlcmVkSm9icyA9IHt9XG4gIGZvciBpID0gMSwgI2pvYnMgZG9cbiAgICBpZiBub3Qgam9ic1RvSWdub3JlW2pvYnNbaV1dIHRoZW5cbiAgICAgIHRhYmxlLmluc2VydChmaWx0ZXJlZEpvYnMsIGpvYnNbaV0pXG4gICAgZW5kXG4gIGVuZFxuICByZXR1cm4gZmlsdGVyZWRKb2JzXG5lbmRcbi0tW1tcbiAgRnVuY3Rpb25zIHRvIHJlbW92ZSBqb2JzLlxuXV1cbi0tIEluY2x1ZGVzXG4tLVtbXG4gIEZ1bmN0aW9uIHRvIHJlbW92ZSBqb2IuXG5dXVxuLS0gSW5jbHVkZXNcbi0tW1tcbiAgRnVuY3Rpb24gdG8gcmVtb3ZlIGRlZHVwbGljYXRpb24ga2V5IGlmIG5lZWRlZFxuICB3aGVuIGEgam9iIGlzIGJlaW5nIHJlbW92ZWQuXG5dXVxubG9jYWwgZnVuY3Rpb24gcmVtb3ZlRGVkdXBsaWNhdGlvbktleUlmTmVlZGVkT25SZW1vdmFsKHByZWZpeEtleSxcbiAgam9iS2V5LCBqb2JJZClcbiAgbG9jYWwgZGVkdXBsaWNhdGlvbklkID0gcmNhbGwoXCJIR0VUXCIsIGpvYktleSwgXCJkZWlkXCIpXG4gIGlmIGRlZHVwbGljYXRpb25JZCB0aGVuXG4gICAgbG9jYWwgZGVkdXBsaWNhdGlvbktleSA9IHByZWZpeEtleSAuLiBcImRlOlwiIC4uIGRlZHVwbGljYXRpb25JZFxuICAgIGxvY2FsIGN1cnJlbnRKb2JJZCA9IHJjYWxsKCdHRVQnLCBkZWR1cGxpY2F0aW9uS2V5KVxuICAgIGlmIGN1cnJlbnRKb2JJZCBhbmQgY3VycmVudEpvYklkID09IGpvYklkIHRoZW5cbiAgICAgIHJldHVybiByY2FsbChcIkRFTFwiLCBkZWR1cGxpY2F0aW9uS2V5KVxuICAgIGVuZFxuICBlbmRcbmVuZFxuLS1bW1xuICBGdW5jdGlvbiB0byByZW1vdmUgam9iIGtleXMuXG5dXVxubG9jYWwgZnVuY3Rpb24gcmVtb3ZlSm9iS2V5cyhqb2JLZXkpXG4gIHJldHVybiByY2FsbChcIkRFTFwiLCBqb2JLZXksIGpvYktleSAuLiAnOmxvZ3MnLCBqb2JLZXkgLi4gJzpkZXBlbmRlbmNpZXMnLFxuICAgIGpvYktleSAuLiAnOnByb2Nlc3NlZCcsIGpvYktleSAuLiAnOmZhaWxlZCcsIGpvYktleSAuLiAnOnVuc3VjY2Vzc2Z1bCcpXG5lbmRcbi0tW1tcbiAgQ2hlY2sgaWYgdGhpcyBqb2IgaGFzIGEgcGFyZW50LiBJZiBzbyB3ZSB3aWxsIGp1c3QgcmVtb3ZlIGl0IGZyb21cbiAgdGhlIHBhcmVudCBjaGlsZCBsaXN0LCBidXQgaWYgaXQgaXMgdGhlIGxhc3QgY2hpbGQgd2Ugc2hvdWxkIG1vdmUgdGhlIHBhcmVudCB0byBcIndhaXQvcGF1c2VkXCJcbiAgd2hpY2ggcmVxdWlyZXMgY29kZSBmcm9tIFwibW92ZVRvRmluaXNoZWRcIlxuXV1cbi0tIEluY2x1ZGVzXG4tLVtbXG4gIEZ1bmN0aW9uIHRvIGFkZCBqb2IgaW4gdGFyZ2V0IGxpc3QgYW5kIGFkZCBtYXJrZXIgaWYgbmVlZGVkLlxuXV1cbi0tIEluY2x1ZGVzXG4tLVtbXG4gIEFkZCBtYXJrZXIgaWYgbmVlZGVkIHdoZW4gYSBqb2IgaXMgYXZhaWxhYmxlLlxuXV1cbmxvY2FsIGZ1bmN0aW9uIGFkZEJhc2VNYXJrZXJJZk5lZWRlZChtYXJrZXJLZXksIGlzUGF1c2VkT3JNYXhlZClcbiAgaWYgbm90IGlzUGF1c2VkT3JNYXhlZCB0aGVuXG4gICAgcmNhbGwoXCJaQUREXCIsIG1hcmtlcktleSwgMCwgXCIwXCIpXG4gIGVuZCAgXG5lbmRcbmxvY2FsIGZ1bmN0aW9uIGFkZEpvYkluVGFyZ2V0TGlzdCh0YXJnZXRLZXksIG1hcmtlcktleSwgcHVzaENtZCwgaXNQYXVzZWRPck1heGVkLCBqb2JJZClcbiAgcmNhbGwocHVzaENtZCwgdGFyZ2V0S2V5LCBqb2JJZClcbiAgYWRkQmFzZU1hcmtlcklmTmVlZGVkKG1hcmtlcktleSwgaXNQYXVzZWRPck1heGVkKVxuZW5kXG4tLVtbXG4gIEZ1bmN0aW9ucyB0byBkZXN0cnVjdHVyZSBqb2Iga2V5LlxuICBKdXN0IGEgYml0IG9mIHdhcm5pbmcsIHRoZXNlIGZ1bmN0aW9ucyBtYXkgYmUgYSBiaXQgc2xvdyBhbmQgYWZmZWN0IHBlcmZvcm1hbmNlIHNpZ25pZmljYW50bHkuXG5dXVxubG9jYWwgZ2V0Sm9iSWRGcm9tS2V5ID0gZnVuY3Rpb24gKGpvYktleSlcbiAgcmV0dXJuIHN0cmluZy5tYXRjaChqb2JLZXksIFwiLio6KC4qKVwiKVxuZW5kXG5sb2NhbCBnZXRKb2JLZXlQcmVmaXggPSBmdW5jdGlvbiAoam9iS2V5LCBqb2JJZClcbiAgcmV0dXJuIHN0cmluZy5zdWIoam9iS2V5LCAwLCAjam9iS2V5IC0gI2pvYklkKVxuZW5kXG4tLVtbXG4gIEZ1bmN0aW9uIHRvIGNoZWNrIGZvciB0aGUgbWV0YS5wYXVzZWQga2V5IHRvIGRlY2lkZSBpZiB3ZSBhcmUgcGF1c2VkIG9yIG5vdFxuICAoc2luY2UgYW4gZW1wdHkgbGlzdCBhbmQgIUVYSVNUUyBhcmUgbm90IHJlYWxseSB0aGUgc2FtZSkuXG5dXVxubG9jYWwgZnVuY3Rpb24gZ2V0VGFyZ2V0UXVldWVMaXN0KHF1ZXVlTWV0YUtleSwgYWN0aXZlS2V5LCB3YWl0S2V5LCBwYXVzZWRLZXkpXG4gIGxvY2FsIHF1ZXVlQXR0cmlidXRlcyA9IHJjYWxsKFwiSE1HRVRcIiwgcXVldWVNZXRhS2V5LCBcInBhdXNlZFwiLCBcImNvbmN1cnJlbmN5XCIsIFwibWF4XCIsIFwiZHVyYXRpb25cIilcbiAgaWYgcXVldWVBdHRyaWJ1dGVzWzFdIHRoZW5cbiAgICByZXR1cm4gcGF1c2VkS2V5LCB0cnVlLCBxdWV1ZUF0dHJpYnV0ZXNbM10sIHF1ZXVlQXR0cmlidXRlc1s0XVxuICBlbHNlXG4gICAgaWYgcXVldWVBdHRyaWJ1dGVzWzJdIHRoZW5cbiAgICAgIGxvY2FsIGFjdGl2ZUNvdW50ID0gcmNhbGwoXCJMTEVOXCIsIGFjdGl2ZUtleSlcbiAgICAgIGlmIGFjdGl2ZUNvdW50ID49IHRvbnVtYmVyKHF1ZXVlQXR0cmlidXRlc1syXSkgdGhlblxuICAgICAgICByZXR1cm4gd2FpdEtleSwgdHJ1ZSwgcXVldWVBdHRyaWJ1dGVzWzNdLCBxdWV1ZUF0dHJpYnV0ZXNbNF1cbiAgICAgIGVsc2VcbiAgICAgICAgcmV0dXJuIHdhaXRLZXksIGZhbHNlLCBxdWV1ZUF0dHJpYnV0ZXNbM10sIHF1ZXVlQXR0cmlidXRlc1s0XVxuICAgICAgZW5kXG4gICAgZW5kXG4gIGVuZFxuICByZXR1cm4gd2FpdEtleSwgZmFsc2UsIHF1ZXVlQXR0cmlidXRlc1szXSwgcXVldWVBdHRyaWJ1dGVzWzRdXG5lbmRcbmxvY2FsIGZ1bmN0aW9uIF9tb3ZlUGFyZW50VG9XYWl0KHBhcmVudFByZWZpeCwgcGFyZW50SWQsIGVtaXRFdmVudClcbiAgbG9jYWwgcGFyZW50VGFyZ2V0LCBpc1BhdXNlZE9yTWF4ZWQgPSBnZXRUYXJnZXRRdWV1ZUxpc3QocGFyZW50UHJlZml4IC4uIFwibWV0YVwiLCBwYXJlbnRQcmVmaXggLi4gXCJhY3RpdmVcIixcbiAgICBwYXJlbnRQcmVmaXggLi4gXCJ3YWl0XCIsIHBhcmVudFByZWZpeCAuLiBcInBhdXNlZFwiKVxuICBhZGRKb2JJblRhcmdldExpc3QocGFyZW50VGFyZ2V0LCBwYXJlbnRQcmVmaXggLi4gXCJtYXJrZXJcIiwgXCJSUFVTSFwiLCBpc1BhdXNlZE9yTWF4ZWQsIHBhcmVudElkKVxuICBpZiBlbWl0RXZlbnQgdGhlblxuICAgIGxvY2FsIHBhcmVudEV2ZW50U3RyZWFtID0gcGFyZW50UHJlZml4IC4uIFwiZXZlbnRzXCJcbiAgICByY2FsbChcIlhBRERcIiwgcGFyZW50RXZlbnRTdHJlYW0sIFwiKlwiLCBcImV2ZW50XCIsIFwid2FpdGluZ1wiLCBcImpvYklkXCIsIHBhcmVudElkLCBcInByZXZcIiwgXCJ3YWl0aW5nLWNoaWxkcmVuXCIpXG4gIGVuZFxuZW5kXG5sb2NhbCBmdW5jdGlvbiByZW1vdmVQYXJlbnREZXBlbmRlbmN5S2V5KGpvYktleSwgaGFyZCwgcGFyZW50S2V5LCBiYXNlS2V5LCBkZWJvdW5jZUlkKVxuICBpZiBwYXJlbnRLZXkgdGhlblxuICAgIGxvY2FsIHBhcmVudERlcGVuZGVuY2llc0tleSA9IHBhcmVudEtleSAuLiBcIjpkZXBlbmRlbmNpZXNcIlxuICAgIGxvY2FsIHJlc3VsdCA9IHJjYWxsKFwiU1JFTVwiLCBwYXJlbnREZXBlbmRlbmNpZXNLZXksIGpvYktleSlcbiAgICBpZiByZXN1bHQgPiAwIHRoZW5cbiAgICAgIGxvY2FsIHBlbmRpbmdEZXBlbmRlbmNpZXMgPSByY2FsbChcIlNDQVJEXCIsIHBhcmVudERlcGVuZGVuY2llc0tleSlcbiAgICAgIGlmIHBlbmRpbmdEZXBlbmRlbmNpZXMgPT0gMCB0aGVuXG4gICAgICAgIGxvY2FsIHBhcmVudElkID0gZ2V0Sm9iSWRGcm9tS2V5KHBhcmVudEtleSlcbiAgICAgICAgbG9jYWwgcGFyZW50UHJlZml4ID0gZ2V0Sm9iS2V5UHJlZml4KHBhcmVudEtleSwgcGFyZW50SWQpXG4gICAgICAgIGxvY2FsIG51bVJlbW92ZWRFbGVtZW50cyA9IHJjYWxsKFwiWlJFTVwiLCBwYXJlbnRQcmVmaXggLi4gXCJ3YWl0aW5nLWNoaWxkcmVuXCIsIHBhcmVudElkKVxuICAgICAgICBpZiBudW1SZW1vdmVkRWxlbWVudHMgPT0gMSB0aGVuXG4gICAgICAgICAgaWYgaGFyZCB0aGVuIC0tIHJlbW92ZSBwYXJlbnQgaW4gc2FtZSBxdWV1ZVxuICAgICAgICAgICAgaWYgcGFyZW50UHJlZml4ID09IGJhc2VLZXkgdGhlblxuICAgICAgICAgICAgICByZW1vdmVQYXJlbnREZXBlbmRlbmN5S2V5KHBhcmVudEtleSwgaGFyZCwgbmlsLCBiYXNlS2V5LCBuaWwpXG4gICAgICAgICAgICAgIHJlbW92ZUpvYktleXMocGFyZW50S2V5KVxuICAgICAgICAgICAgICBpZiBkZWJvdW5jZUlkIHRoZW5cbiAgICAgICAgICAgICAgICByY2FsbChcIkRFTFwiLCBwYXJlbnRQcmVmaXggLi4gXCJkZTpcIiAuLiBkZWJvdW5jZUlkKVxuICAgICAgICAgICAgICBlbmRcbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgX21vdmVQYXJlbnRUb1dhaXQocGFyZW50UHJlZml4LCBwYXJlbnRJZClcbiAgICAgICAgICAgIGVuZFxuICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgIF9tb3ZlUGFyZW50VG9XYWl0KHBhcmVudFByZWZpeCwgcGFyZW50SWQsIHRydWUpXG4gICAgICAgICAgZW5kXG4gICAgICAgIGVuZFxuICAgICAgZW5kXG4gICAgICByZXR1cm4gdHJ1ZVxuICAgIGVuZFxuICBlbHNlXG4gICAgbG9jYWwgcGFyZW50QXR0cmlidXRlcyA9IHJjYWxsKFwiSE1HRVRcIiwgam9iS2V5LCBcInBhcmVudEtleVwiLCBcImRlaWRcIilcbiAgICBsb2NhbCBtaXNzZWRQYXJlbnRLZXkgPSBwYXJlbnRBdHRyaWJ1dGVzWzFdXG4gICAgaWYoICh0eXBlKG1pc3NlZFBhcmVudEtleSkgPT0gXCJzdHJpbmdcIikgYW5kIG1pc3NlZFBhcmVudEtleSB+PSBcIlwiXG4gICAgICBhbmQgKHJjYWxsKFwiRVhJU1RTXCIsIG1pc3NlZFBhcmVudEtleSkgPT0gMSkpIHRoZW5cbiAgICAgIGxvY2FsIHBhcmVudERlcGVuZGVuY2llc0tleSA9IG1pc3NlZFBhcmVudEtleSAuLiBcIjpkZXBlbmRlbmNpZXNcIlxuICAgICAgbG9jYWwgcmVzdWx0ID0gcmNhbGwoXCJTUkVNXCIsIHBhcmVudERlcGVuZGVuY2llc0tleSwgam9iS2V5KVxuICAgICAgaWYgcmVzdWx0ID4gMCB0aGVuXG4gICAgICAgIGxvY2FsIHBlbmRpbmdEZXBlbmRlbmNpZXMgPSByY2FsbChcIlNDQVJEXCIsIHBhcmVudERlcGVuZGVuY2llc0tleSlcbiAgICAgICAgaWYgcGVuZGluZ0RlcGVuZGVuY2llcyA9PSAwIHRoZW5cbiAgICAgICAgICBsb2NhbCBwYXJlbnRJZCA9IGdldEpvYklkRnJvbUtleShtaXNzZWRQYXJlbnRLZXkpXG4gICAgICAgICAgbG9jYWwgcGFyZW50UHJlZml4ID0gZ2V0Sm9iS2V5UHJlZml4KG1pc3NlZFBhcmVudEtleSwgcGFyZW50SWQpXG4gICAgICAgICAgbG9jYWwgbnVtUmVtb3ZlZEVsZW1lbnRzID0gcmNhbGwoXCJaUkVNXCIsIHBhcmVudFByZWZpeCAuLiBcIndhaXRpbmctY2hpbGRyZW5cIiwgcGFyZW50SWQpXG4gICAgICAgICAgaWYgbnVtUmVtb3ZlZEVsZW1lbnRzID09IDEgdGhlblxuICAgICAgICAgICAgaWYgaGFyZCB0aGVuXG4gICAgICAgICAgICAgIGlmIHBhcmVudFByZWZpeCA9PSBiYXNlS2V5IHRoZW5cbiAgICAgICAgICAgICAgICByZW1vdmVQYXJlbnREZXBlbmRlbmN5S2V5KG1pc3NlZFBhcmVudEtleSwgaGFyZCwgbmlsLCBiYXNlS2V5LCBuaWwpXG4gICAgICAgICAgICAgICAgcmVtb3ZlSm9iS2V5cyhtaXNzZWRQYXJlbnRLZXkpXG4gICAgICAgICAgICAgICAgaWYgcGFyZW50QXR0cmlidXRlc1syXSB0aGVuXG4gICAgICAgICAgICAgICAgICByY2FsbChcIkRFTFwiLCBwYXJlbnRQcmVmaXggLi4gXCJkZTpcIiAuLiBwYXJlbnRBdHRyaWJ1dGVzWzJdKVxuICAgICAgICAgICAgICAgIGVuZFxuICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgX21vdmVQYXJlbnRUb1dhaXQocGFyZW50UHJlZml4LCBwYXJlbnRJZClcbiAgICAgICAgICAgICAgZW5kXG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgIF9tb3ZlUGFyZW50VG9XYWl0KHBhcmVudFByZWZpeCwgcGFyZW50SWQsIHRydWUpXG4gICAgICAgICAgICBlbmRcbiAgICAgICAgICBlbmRcbiAgICAgICAgZW5kXG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgICBlbmRcbiAgICBlbmRcbiAgZW5kXG4gIHJldHVybiBmYWxzZVxuZW5kXG5sb2NhbCBmdW5jdGlvbiByZW1vdmVKb2Ioam9iSWQsIGhhcmQsIGJhc2VLZXksIHNob3VsZFJlbW92ZURlZHVwbGljYXRpb25LZXkpXG4gIGxvY2FsIGpvYktleSA9IGJhc2VLZXkgLi4gam9iSWRcbiAgcmVtb3ZlUGFyZW50RGVwZW5kZW5jeUtleShqb2JLZXksIGhhcmQsIG5pbCwgYmFzZUtleSlcbiAgaWYgc2hvdWxkUmVtb3ZlRGVkdXBsaWNhdGlvbktleSB0aGVuXG4gICAgcmVtb3ZlRGVkdXBsaWNhdGlvbktleUlmTmVlZGVkT25SZW1vdmFsKGJhc2VLZXksIGpvYktleSwgam9iSWQpXG4gIGVuZFxuICByZW1vdmVKb2JLZXlzKGpvYktleSlcbmVuZFxubG9jYWwgZnVuY3Rpb24gcmVtb3ZlSm9icyhrZXlzLCBoYXJkLCBiYXNlS2V5LCBtYXgpXG4gIGZvciBpLCBrZXkgaW4gaXBhaXJzKGtleXMpIGRvXG4gICAgcmVtb3ZlSm9iKGtleSwgaGFyZCwgYmFzZUtleSwgdHJ1ZSAtLVtbcmVtb3ZlIGRlYm91bmNlIGtleV1dKVxuICBlbmRcbiAgcmV0dXJuIG1heCAtICNrZXlzXG5lbmRcbmxvY2FsIGZ1bmN0aW9uIGdldExpc3RJdGVtcyhrZXlOYW1lLCBtYXgpXG4gIHJldHVybiByY2FsbCgnTFJBTkdFJywga2V5TmFtZSwgMCwgbWF4IC0gMSlcbmVuZFxubG9jYWwgZnVuY3Rpb24gcmVtb3ZlTGlzdEpvYnMoa2V5TmFtZSwgaGFyZCwgYmFzZUtleSwgbWF4LCBqb2JzVG9JZ25vcmUpXG4gIGxvY2FsIGpvYnMgPSBnZXRMaXN0SXRlbXMoa2V5TmFtZSwgbWF4KVxuICBpZiBqb2JzVG9JZ25vcmUgdGhlblxuICAgIGpvYnMgPSBmaWx0ZXJPdXRKb2JzVG9JZ25vcmUoam9icywgam9ic1RvSWdub3JlKVxuICBlbmRcbiAgbG9jYWwgY291bnQgPSByZW1vdmVKb2JzKGpvYnMsIGhhcmQsIGJhc2VLZXksIG1heClcbiAgcmNhbGwoXCJMVFJJTVwiLCBrZXlOYW1lLCAjam9icywgLTEpXG4gIHJldHVybiBjb3VudFxuZW5kXG4tLSBJbmNsdWRlc1xuLS1bW1xuICBGdW5jdGlvbiB0byBsb29wIGluIGJhdGNoZXMuXG4gIEp1c3QgYSBiaXQgb2Ygd2FybmluZywgc29tZSBjb21tYW5kcyBhcyBaUkVNXG4gIGNvdWxkIHJlY2VpdmUgYSBtYXhpbXVtIG9mIDcwMDAgcGFyYW1ldGVycyBwZXIgY2FsbC5cbl1dXG5sb2NhbCBmdW5jdGlvbiBiYXRjaGVzKG4sIGJhdGNoU2l6ZSlcbiAgbG9jYWwgaSA9IDBcbiAgcmV0dXJuIGZ1bmN0aW9uKClcbiAgICBsb2NhbCBmcm9tID0gaSAqIGJhdGNoU2l6ZSArIDFcbiAgICBpID0gaSArIDFcbiAgICBpZiAoZnJvbSA8PSBuKSB0aGVuXG4gICAgICBsb2NhbCB0byA9IG1hdGgubWluKGZyb20gKyBiYXRjaFNpemUgLSAxLCBuKVxuICAgICAgcmV0dXJuIGZyb20sIHRvXG4gICAgZW5kXG4gIGVuZFxuZW5kXG4tLVtbXG4gIEZ1bmN0aW9uIHRvIGdldCBaU2V0IGl0ZW1zLlxuXV1cbmxvY2FsIGZ1bmN0aW9uIGdldFpTZXRJdGVtcyhrZXlOYW1lLCBtYXgpXG4gIHJldHVybiByY2FsbCgnWlJBTkdFJywga2V5TmFtZSwgMCwgbWF4IC0gMSlcbmVuZFxubG9jYWwgZnVuY3Rpb24gcmVtb3ZlWlNldEpvYnMoa2V5TmFtZSwgaGFyZCwgYmFzZUtleSwgbWF4LCBqb2JzVG9JZ25vcmUpXG4gIGxvY2FsIGpvYnMgPSBnZXRaU2V0SXRlbXMoa2V5TmFtZSwgbWF4KVxuICBpZiBqb2JzVG9JZ25vcmUgdGhlblxuICAgIGpvYnMgPSBmaWx0ZXJPdXRKb2JzVG9JZ25vcmUoam9icywgam9ic1RvSWdub3JlKVxuICBlbmRcbiAgbG9jYWwgY291bnQgPSByZW1vdmVKb2JzKGpvYnMsIGhhcmQsIGJhc2VLZXksIG1heClcbiAgaWYoI2pvYnMgPiAwKSB0aGVuXG4gICAgZm9yIGZyb20sIHRvIGluIGJhdGNoZXMoI2pvYnMsIDcwMDApIGRvXG4gICAgICByY2FsbChcIlpSRU1cIiwga2V5TmFtZSwgdW5wYWNrKGpvYnMsIGZyb20sIHRvKSlcbiAgICBlbmRcbiAgZW5kXG4gIHJldHVybiBjb3VudFxuZW5kXG4tLSBXZSBtdXN0IG5vdCByZW1vdmUgZGVsYXllZCBqb2JzIGlmIHRoZXkgYXJlIGFzc29jaWF0ZWQgdG8gYSBqb2Igc2NoZWR1bGVyLlxubG9jYWwgc2NoZWR1bGVkSm9icyA9IHt9XG5sb2NhbCBqb2JTY2hlZHVsZXJzID0gcmNhbGwoXCJaUkFOR0VcIiwgS0VZU1s1XSwgMCwgLTEsIFwiV0lUSFNDT1JFU1wiKVxuLS0gRm9yIGV2ZXJ5IGpvYiBzY2hlZHVsZXIsIGdldCB0aGUgY3VycmVudCBkZWxheWVkIGpvYiBpZC5cbmZvciBpID0gMSwgI2pvYlNjaGVkdWxlcnMsIDIgZG9cbiAgICBsb2NhbCBqb2JTY2hlZHVsZXJJZCA9IGpvYlNjaGVkdWxlcnNbaV1cbiAgICBsb2NhbCBqb2JTY2hlZHVsZXJNaWxsaXMgPSBqb2JTY2hlZHVsZXJzW2kgKyAxXVxuICAgIGxvY2FsIGRlbGF5ZWRKb2JJZCA9IFwicmVwZWF0OlwiIC4uIGpvYlNjaGVkdWxlcklkIC4uIFwiOlwiIC4uIGpvYlNjaGVkdWxlck1pbGxpc1xuICAgIHNjaGVkdWxlZEpvYnNbZGVsYXllZEpvYklkXSA9IHRydWVcbmVuZFxucmVtb3ZlTGlzdEpvYnMoS0VZU1sxXSwgdHJ1ZSwgcXVldWVCYXNlS2V5LCAwLCBzY2hlZHVsZWRKb2JzKSAtLSB3YWl0XG5yZW1vdmVMaXN0Sm9icyhLRVlTWzJdLCB0cnVlLCBxdWV1ZUJhc2VLZXksIDAsIHNjaGVkdWxlZEpvYnMpIC0tIHBhdXNlZFxuaWYgQVJHVlsyXSA9PSBcIjFcIiB0aGVuXG4gIHJlbW92ZVpTZXRKb2JzKEtFWVNbM10sIHRydWUsIHF1ZXVlQmFzZUtleSwgMCwgc2NoZWR1bGVkSm9icykgLS0gZGVsYXllZFxuZW5kXG5yZW1vdmVaU2V0Sm9icyhLRVlTWzRdLCB0cnVlLCBxdWV1ZUJhc2VLZXksIDAsIHNjaGVkdWxlZEpvYnMpIC0tIHByaW9yaXRpemVkXG5gO1xuZXhwb3J0cy5kcmFpbiA9IHtcbiAgICBuYW1lOiAnZHJhaW4nLFxuICAgIGNvbnRlbnQsXG4gICAga2V5czogNSxcbn07XG4vLyMgc291cmNlTWFwcGluZ1VSTD1kcmFpbi01LmpzLm1hcCJdLCJ2ZXJzaW9uIjozfQ== |