Skip to content

Commit

Permalink
refactor: migrate cluster-related code to TypeScript (#717)
Browse files Browse the repository at this point in the history
  • Loading branch information
luin committed Oct 14, 2018
1 parent 80f4a45 commit 16643e2
Show file tree
Hide file tree
Showing 15 changed files with 848 additions and 699 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ node_modules
*.cpuprofile
/test.js
built

.vscode
112 changes: 112 additions & 0 deletions lib/cluster/ClusterOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import {NodeRole} from './util'

/**
* Options for Cluster constructor
*
* @export
* @interface IClusterOptions
*/
export interface IClusterOptions {
/**
* See "Quick Start" section.
*
* @default (times) => Math.min(100 + times * 2, 2000)
*/
clusterRetryStrategy?: (times: number) => number | null

/**
* See Redis class.
*
* @default true
*/
enableOfflineQueue?: boolean

/**
* When enabled, ioredis only emits "ready" event when `CLUSTER INFO`
* command reporting the cluster is ready for handling commands.
*
* @default true
*/
enableReadyCheck?: boolean

/**
* Scale reads to the node with the specified role.
*
* @default "master"
*/
scaleReads?: NodeRole | Function

/**
* When a MOVED or ASK error is received, client will redirect the
* command to another node.
* This option limits the max redirections allowed to send a command.
*
* @default 16
*/
maxRedirections?: number

/**
* When an error is received when sending a command (e.g.
* "Connection is closed." when the target Redis node is down), client will retry
* if `retryDelayOnFailover` is valid delay time (in ms).
*
* @default 100
*/
retryDelayOnFailover?: number

/**
* When a CLUSTERDOWN error is received, client will retry
* if `retryDelayOnClusterDown` is valid delay time (in ms).
*
* @default 100
*/
retryDelayOnClusterDown?: number

/**
* When a TRYAGAIN error is received, client will retry
* if `retryDelayOnTryAgain` is valid delay time (in ms).
*
* @default 100
*/
retryDelayOnTryAgain?: number

/**
* The milliseconds before a timeout occurs while refreshing
* slots from the cluster.
*
* @default 1000
*/
slotsRefreshTimeout?: number

/**
* The milliseconds between every automatic slots refresh.
*
* @default 5000
*/
slotsRefreshInterval?: number

/**
* Passed to the constructor of `Redis`
*
* @default null
*/
redisOptions?: any

/**
* @default false
*/
lazyConnect?: boolean
}

export const DEFAULT_CLUSTER_OPTIONS: IClusterOptions = {
clusterRetryStrategy: (times) => Math.min(100 + times * 2, 2000),
enableOfflineQueue: true,
enableReadyCheck: true,
scaleReads: 'master',
maxRedirections: 16,
retryDelayOnFailover: 100,
retryDelayOnClusterDown: 100,
retryDelayOnTryAgain: 100,
slotsRefreshTimeout: 1000,
slotsRefreshInterval: 5000
}
44 changes: 16 additions & 28 deletions lib/cluster/ConnectionPool.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {parseURL} from '../utils'
import {EventEmitter} from 'events'
import {sample} from '../utils'
import {noop, defaults} from '../utils/lodash'
import {IRedisOptions, getNodeKey} from './util'
import {IRedisOptions, getNodeKey, NodeKey, NodeRole} from './util'

const Redis = require('../redis')
const debug = require('../utils/debug')('ioredis:cluster:connectionPool')
Expand All @@ -22,11 +22,21 @@ export default class ConnectionPool extends EventEmitter {
super()
}

public getNodes(role: 'all' | 'master' | 'slave' = 'all'): any[] {
public getNodes(role: NodeRole = 'all'): any[] {
const nodes = this.nodes[role]
return Object.keys(nodes).map((key) => nodes[key])
}

public getInstanceByKey(key: NodeKey): any {
return this.nodes.all[key]
}

public getSampleInstance(role: NodeRole): any {
const keys = Object.keys(this.nodes[role])
const sampleKey = sample(keys)
return this.nodes[role][sampleKey]
}

/**
* Find or create a connection to the node
*
Expand All @@ -36,7 +46,6 @@ export default class ConnectionPool extends EventEmitter {
* @memberof ConnectionPool
*/
public findOrCreate(node: IRedisOptions, readOnly: boolean = false): any {
fillDefaultOptions(node)
const key = getNodeKey(node)
readOnly = Boolean(readOnly)

Expand Down Expand Up @@ -104,28 +113,12 @@ export default class ConnectionPool extends EventEmitter {
* @param {(Array<string | number | object>)} nodes
* @memberof ConnectionPool
*/
public reset(nodes: Array<string | number | object>): void {
public reset(nodes: IRedisOptions[]): void {
debug('Reset with %O', nodes);
const newNodes = {}
nodes.forEach((node) => {
const options: IRedisOptions = {}
if (typeof node === 'object') {
Object.assign(options, node)
} else if (typeof node === 'string') {
Object.assign(options, parseURL(node))
} else if (typeof node === 'number') {
options.port = node
} else {
throw new Error('Invalid argument ' + node)
}
if (typeof options.port === 'string') {
options.port = parseInt(options.port, 10)
}
delete options.db

fillDefaultOptions(options)
newNodes[getNodeKey(options)] = options
}, this)
newNodes[getNodeKey(node)] = node
})

Object.keys(this.nodes.all).forEach((key) => {
if (!newNodes[key]) {
Expand All @@ -139,8 +132,3 @@ export default class ConnectionPool extends EventEmitter {
})
}
}

function fillDefaultOptions(node: IRedisOptions): void {
node.port = node.port || 6379
node.host = node.host || '127.0.0.1'
}

0 comments on commit 16643e2

Please sign in to comment.