You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
chore: refactor flush and error
This commit is contained in:
30
index.js
30
index.js
@@ -144,30 +144,28 @@ RedisClient.prototype.initializeRetryVars = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Flush provided queues, erroring any items with a callback first
|
// Flush provided queues, erroring any items with a callback first
|
||||||
RedisClient.prototype.flushAndError = function (errorAttributes, options) {
|
RedisClient.prototype.flushAndError = function (message, code, options) {
|
||||||
options = options || {}
|
options = options || {}
|
||||||
const queueNames = options.queues || ['commandQueue', 'offlineQueue'] // Flush the commandQueue first to keep the order intact
|
const queueNames = options.queues || ['commandQueue', 'offlineQueue'] // Flush the commandQueue first to keep the order intact
|
||||||
for (var i = 0; i < queueNames.length; i++) {
|
for (var i = 0; i < queueNames.length; i++) {
|
||||||
// If the command was fired it might have been processed so far
|
// If the command was fired it might have been processed so far
|
||||||
if (queueNames[i] === 'commandQueue') {
|
const ErrorClass = queueNames[i] === 'commandQueue'
|
||||||
errorAttributes.message += ' It might have been processed.'
|
? Errors.InterruptError
|
||||||
} else { // As the commandQueue is flushed first, remove this for the offline queue
|
: Errors.AbortError
|
||||||
errorAttributes.message = errorAttributes.message.replace(' It might have been processed.', '')
|
|
||||||
}
|
while (this[queueNames[i]].length) {
|
||||||
// Don't flush everything from the queue
|
const command = this[queueNames[i]].shift()
|
||||||
for (var commandObj = this[queueNames[i]].shift(); commandObj; commandObj = this[queueNames[i]].shift()) {
|
const err = new ErrorClass(message)
|
||||||
const err = new errorClasses.AbortError(errorAttributes)
|
err.code = code
|
||||||
if (commandObj.error) {
|
err.command = command.command.toUpperCase()
|
||||||
err.stack = err.stack + commandObj.error.stack.replace(/^Error.*?\n/, '\n')
|
err.args = command.args
|
||||||
}
|
if (command.error) {
|
||||||
err.command = commandObj.command.toUpperCase()
|
err.stack = err.stack + command.error.stack.replace(/^Error.*?\n/, '\n')
|
||||||
if (commandObj.args && commandObj.args.length) {
|
|
||||||
err.args = commandObj.args
|
|
||||||
}
|
}
|
||||||
if (options.error) {
|
if (options.error) {
|
||||||
err.origin = options.error
|
err.origin = options.error
|
||||||
}
|
}
|
||||||
commandObj.callback(err)
|
command.callback(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,10 +29,7 @@ function createParser (client) {
|
|||||||
// Note: the execution order is important. First flush and emit, then create the stream
|
// Note: the execution order is important. First flush and emit, then create the stream
|
||||||
err.message += '. Please report this.'
|
err.message += '. Please report this.'
|
||||||
client.ready = false
|
client.ready = false
|
||||||
client.flushAndError({
|
client.flushAndError('Fatal error encountered. Command aborted.', 'NR_FATAL', {
|
||||||
message: 'Fatal error encountered. Command aborted.',
|
|
||||||
code: 'NR_FATAL'
|
|
||||||
}, {
|
|
||||||
error: err,
|
error: err,
|
||||||
queues: ['commandQueue']
|
queues: ['commandQueue']
|
||||||
})
|
})
|
||||||
|
@@ -46,10 +46,7 @@ RedisClient.prototype.end = function (flush) {
|
|||||||
|
|
||||||
// Flush queue if wanted
|
// Flush queue if wanted
|
||||||
if (flush) {
|
if (flush) {
|
||||||
this.flushAndError({
|
this.flushAndError('Connection forcefully ended and command aborted.', 'NR_CLOSED')
|
||||||
message: 'Connection forcefully ended and command aborted.',
|
|
||||||
code: 'NR_CLOSED'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
// Clear retryTimer
|
// Clear retryTimer
|
||||||
if (this.retryTimer) {
|
if (this.retryTimer) {
|
||||||
|
@@ -107,6 +107,12 @@ Multi.prototype.quit = function quit () {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Return a function that receives the raw info data and convert to an object.
|
||||||
|
*
|
||||||
|
* @param {RedisClient} client
|
||||||
|
* @returns {function}
|
||||||
|
*/
|
||||||
function infoCallback (client) {
|
function infoCallback (client) {
|
||||||
return function (err, res) {
|
return function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const Errors = require('redis-errors')
|
||||||
const Queue = require('denque')
|
const Queue = require('denque')
|
||||||
const utils = require('./utils')
|
const utils = require('./utils')
|
||||||
const Command = require('./command')
|
const Command = require('./command')
|
||||||
@@ -136,7 +137,7 @@ function execBatch (multi) {
|
|||||||
client.uncork()
|
client.uncork()
|
||||||
return Promise.all(promises).then((res) => {
|
return Promise.all(promises).then((res) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
const err = new Error('bla failed')
|
const err = new Errors.RedisError('bla failed')
|
||||||
err.code = 'foo'
|
err.code = 'foo'
|
||||||
err.replies = res
|
err.replies = res
|
||||||
return Promise.reject(err)
|
return Promise.reject(err)
|
||||||
|
@@ -12,26 +12,41 @@ function onConnect (client) {
|
|||||||
client._stream.setKeepAlive(client.options.socketKeepalive)
|
client._stream.setKeepAlive(client.options.socketKeepalive)
|
||||||
client._stream.setTimeout(0)
|
client._stream.setTimeout(0)
|
||||||
|
|
||||||
|
// TODO: Deprecate the connect event.
|
||||||
client.emit('connect')
|
client.emit('connect')
|
||||||
client.initializeRetryVars()
|
client.initializeRetryVars()
|
||||||
|
|
||||||
if (client.options.noReadyCheck) {
|
if (client.options.noReadyCheck) {
|
||||||
onReady(client)
|
readyHandler(client)
|
||||||
} else {
|
} else {
|
||||||
readyCheck(client)
|
readyCheck(client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Empty the offline queue and call the commands
|
||||||
|
*
|
||||||
|
* @param {RedisClient} client
|
||||||
|
*/
|
||||||
function sendOfflineQueue (client) {
|
function sendOfflineQueue (client) {
|
||||||
while (client.offlineQueue.length) {
|
const queue = client.offlineQueue
|
||||||
const command = client.offlineQueue.shift()
|
while (queue.length) {
|
||||||
|
const command = queue.shift()
|
||||||
debug('Sending offline command: %s', command.command)
|
debug('Sending offline command: %s', command.command)
|
||||||
client.internalSendCommand(command)
|
client.internalSendCommand(command)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onReady (client) {
|
/**
|
||||||
debug('onReady called %s id %s', client.address, client.connectionId)
|
* @description Transparently perform predefined commands and emit ready.
|
||||||
|
*
|
||||||
|
* Emit ready before the all commands returned.
|
||||||
|
* The order of the commands is important.
|
||||||
|
*
|
||||||
|
* @param {RedisClient} client
|
||||||
|
*/
|
||||||
|
function readyHandler (client) {
|
||||||
|
debug('readyHandler called %s id %s', client.address, client.connectionId)
|
||||||
client.ready = true
|
client.ready = true
|
||||||
|
|
||||||
client.cork = () => {
|
client.cork = () => {
|
||||||
@@ -50,7 +65,6 @@ function onReady (client) {
|
|||||||
client._stream.uncork()
|
client._stream.uncork()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore modal commands from previous connection. The order of the commands is important
|
|
||||||
if (client.selectedDb !== undefined) {
|
if (client.selectedDb !== undefined) {
|
||||||
client.internalSendCommand(new Command('select', [client.selectedDb])).catch((err) => {
|
client.internalSendCommand(new Command('select', [client.selectedDb])).catch((err) => {
|
||||||
if (!client.closing) {
|
if (!client.closing) {
|
||||||
@@ -78,7 +92,7 @@ function onReady (client) {
|
|||||||
// // individual: function noop () {}
|
// // individual: function noop () {}
|
||||||
// }
|
// }
|
||||||
if (!client.options.disableResubscribing && callbackCount) {
|
if (!client.options.disableResubscribing && callbackCount) {
|
||||||
debug('Sending pub/sub onReady commands')
|
debug('Sending pub/sub commands')
|
||||||
for (const key in client.subscriptionSet) {
|
for (const key in client.subscriptionSet) {
|
||||||
if (client.subscriptionSet.hasOwnProperty(key)) {
|
if (client.subscriptionSet.hasOwnProperty(key)) {
|
||||||
const command = key.slice(0, key.indexOf('_'))
|
const command = key.slice(0, key.indexOf('_'))
|
||||||
@@ -95,55 +109,54 @@ function onReady (client) {
|
|||||||
client.emit('ready')
|
client.emit('ready')
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInfoFail (client, err) {
|
/**
|
||||||
if (client.closing) {
|
* @description Perform a info command and check if Redis is ready
|
||||||
return
|
*
|
||||||
}
|
* @param {RedisClient} client
|
||||||
|
*/
|
||||||
if (err.message === "ERR unknown command 'info'") {
|
|
||||||
onReady(client)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err.message = `Ready check failed: ${err.message}`
|
|
||||||
client.emit('error', err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function onInfoCmd (client, res) {
|
|
||||||
/* istanbul ignore if: some servers might not respond with any info data. client is just a safety check that is difficult to test */
|
|
||||||
if (!res) {
|
|
||||||
debug('The info command returned without any data.')
|
|
||||||
onReady(client)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!client.serverInfo.loading || client.serverInfo.loading === '0') {
|
|
||||||
// If the master_link_status exists but the link is not up, try again after 50 ms
|
|
||||||
if (client.serverInfo.master_link_status && client.serverInfo.master_link_status !== 'up') {
|
|
||||||
client.serverInfo.loading_eta_seconds = 0.05
|
|
||||||
} else {
|
|
||||||
// Eta loading should change
|
|
||||||
debug('Redis server ready.')
|
|
||||||
onReady(client)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var retryTime = +client.serverInfo.loading_eta_seconds * 1000
|
|
||||||
if (retryTime > 1000) {
|
|
||||||
retryTime = 1000
|
|
||||||
}
|
|
||||||
debug('Redis server still loading, trying again in %s', retryTime)
|
|
||||||
setTimeout((client) => readyCheck(client), retryTime, client)
|
|
||||||
}
|
|
||||||
|
|
||||||
function readyCheck (client) {
|
function readyCheck (client) {
|
||||||
debug('Checking server ready state...')
|
debug('Checking server ready state...')
|
||||||
// Always fire client info command as first command even if other commands are already queued up
|
// Always fire client info command as first command even if other commands are already queued up
|
||||||
client.ready = true
|
client.ready = true
|
||||||
client.info()
|
client.info().then((res) => {
|
||||||
.then((res) => onInfoCmd(client, res))
|
/* istanbul ignore if: some servers might not respond with any info data. client is just a safety check that is difficult to test */
|
||||||
.catch((err) => onInfoFail(client, err))
|
if (!res) {
|
||||||
|
debug('The info command returned without any data.')
|
||||||
|
readyHandler(client)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.serverInfo.loading || client.serverInfo.loading === '0') {
|
||||||
|
// If the master_link_status exists but the link is not up, try again after 50 ms
|
||||||
|
if (client.serverInfo.master_link_status && client.serverInfo.master_link_status !== 'up') {
|
||||||
|
client.serverInfo.loading_eta_seconds = 0.05
|
||||||
|
} else {
|
||||||
|
// Eta loading should change
|
||||||
|
debug('Redis server ready.')
|
||||||
|
readyHandler(client)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var retryTime = +client.serverInfo.loading_eta_seconds * 1000
|
||||||
|
if (retryTime > 1000) {
|
||||||
|
retryTime = 1000
|
||||||
|
}
|
||||||
|
debug('Redis server still loading, trying again in %s', retryTime)
|
||||||
|
setTimeout((client) => readyCheck(client), retryTime, client)
|
||||||
|
}).catch((err) => {
|
||||||
|
if (client.closing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.message === "ERR unknown command 'info'") {
|
||||||
|
readyHandler(client)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err.message = `Ready check failed: ${err.message}`
|
||||||
|
client.emit('error', err)
|
||||||
|
return
|
||||||
|
})
|
||||||
client.ready = false
|
client.ready = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const Errors = require('redis-errors')
|
||||||
const debug = require('./debug')
|
const debug = require('./debug')
|
||||||
var lazyConnect = function (client) {
|
var lazyConnect = function (client) {
|
||||||
lazyConnect = require('./connect')
|
lazyConnect = require('./connect')
|
||||||
@@ -63,13 +64,10 @@ function reconnect (client, why, error) {
|
|||||||
|
|
||||||
if (why === 'timeout') {
|
if (why === 'timeout') {
|
||||||
var message = 'Redis connection in broken state: connection timeout exceeded.'
|
var message = 'Redis connection in broken state: connection timeout exceeded.'
|
||||||
const err = new Error(message)
|
const err = new Errors.RedisError(message)
|
||||||
// TODO: Find better error codes...
|
// TODO: Find better error codes...
|
||||||
err.code = 'CONNECTION_BROKEN'
|
err.code = 'CONNECTION_BROKEN'
|
||||||
client.flushAndError({
|
client.flushAndError(message, 'CONNECTION_BROKEN')
|
||||||
message: message,
|
|
||||||
code: 'CONNECTION_BROKEN'
|
|
||||||
})
|
|
||||||
client.emit('error', err)
|
client.emit('error', err)
|
||||||
client.end(false)
|
client.end(false)
|
||||||
return
|
return
|
||||||
@@ -78,10 +76,7 @@ function reconnect (client, why, error) {
|
|||||||
// If client is a requested shutdown, then don't retry
|
// If client is a requested shutdown, then don't retry
|
||||||
if (client.closing) {
|
if (client.closing) {
|
||||||
debug('Connection ended by quit / end command, not retrying.')
|
debug('Connection ended by quit / end command, not retrying.')
|
||||||
client.flushAndError({
|
client.flushAndError('Stream connection ended and command aborted.', 'NR_CLOSED', {
|
||||||
message: 'Stream connection ended and command aborted.',
|
|
||||||
code: 'NR_CLOSED'
|
|
||||||
}, {
|
|
||||||
error
|
error
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -98,10 +93,7 @@ function reconnect (client, why, error) {
|
|||||||
if (client.retryDelay instanceof Error) {
|
if (client.retryDelay instanceof Error) {
|
||||||
error = client.retryDelay
|
error = client.retryDelay
|
||||||
}
|
}
|
||||||
client.flushAndError({
|
client.flushAndError('Stream connection ended and command aborted.', 'NR_CLOSED', {
|
||||||
message: 'Stream connection ended and command aborted.',
|
|
||||||
code: 'NR_CLOSED'
|
|
||||||
}, {
|
|
||||||
error
|
error
|
||||||
})
|
})
|
||||||
// TODO: Check if client is so smart
|
// TODO: Check if client is so smart
|
||||||
@@ -117,10 +109,7 @@ function reconnect (client, why, error) {
|
|||||||
client.offlineQueue.unshift.apply(client.offlineQueue, client.commandQueue.toArray())
|
client.offlineQueue.unshift.apply(client.offlineQueue, client.commandQueue.toArray())
|
||||||
client.commandQueue.clear()
|
client.commandQueue.clear()
|
||||||
} else if (client.commandQueue.length !== 0) {
|
} else if (client.commandQueue.length !== 0) {
|
||||||
client.flushAndError({
|
client.flushAndError('Redis connection lost and command aborted.', 'UNCERTAIN_STATE', {
|
||||||
message: 'Redis connection lost and command aborted.',
|
|
||||||
code: 'UNCERTAIN_STATE'
|
|
||||||
}, {
|
|
||||||
error,
|
error,
|
||||||
queues: ['commandQueue']
|
queues: ['commandQueue']
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user