Redis.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const commands_1 = require("@ioredis/commands");
  4. const events_1 = require("events");
  5. const standard_as_callback_1 = require("standard-as-callback");
  6. const cluster_1 = require("./cluster");
  7. const Command_1 = require("./Command");
  8. const connectors_1 = require("./connectors");
  9. const SentinelConnector_1 = require("./connectors/SentinelConnector");
  10. const eventHandler = require("./redis/event_handler");
  11. const RedisOptions_1 = require("./redis/RedisOptions");
  12. const ScanStream_1 = require("./ScanStream");
  13. const transaction_1 = require("./transaction");
  14. const utils_1 = require("./utils");
  15. const applyMixin_1 = require("./utils/applyMixin");
  16. const Commander_1 = require("./utils/Commander");
  17. const lodash_1 = require("./utils/lodash");
  18. const Deque = require("denque");
  19. const debug = (0, utils_1.Debug)("redis");
  20. /**
  21. * This is the major component of ioredis.
  22. * Use it to connect to a standalone Redis server or Sentinels.
  23. *
  24. * ```typescript
  25. * const redis = new Redis(); // Default port is 6379
  26. * async function main() {
  27. * redis.set("foo", "bar");
  28. * redis.get("foo", (err, result) => {
  29. * // `result` should be "bar"
  30. * console.log(err, result);
  31. * });
  32. * // Or use Promise
  33. * const result = await redis.get("foo");
  34. * }
  35. * ```
  36. */
  37. class Redis extends Commander_1.default {
  38. constructor(arg1, arg2, arg3) {
  39. super();
  40. this.status = "wait";
  41. /**
  42. * @ignore
  43. */
  44. this.isCluster = false;
  45. this.reconnectTimeout = null;
  46. this.connectionEpoch = 0;
  47. this.retryAttempts = 0;
  48. this.manuallyClosing = false;
  49. // Prepare autopipelines structures
  50. this._autoPipelines = new Map();
  51. this._runningAutoPipelines = new Set();
  52. this.parseOptions(arg1, arg2, arg3);
  53. events_1.EventEmitter.call(this);
  54. this.resetCommandQueue();
  55. this.resetOfflineQueue();
  56. if (this.options.Connector) {
  57. this.connector = new this.options.Connector(this.options);
  58. }
  59. else if (this.options.sentinels) {
  60. const sentinelConnector = new SentinelConnector_1.default(this.options);
  61. sentinelConnector.emitter = this;
  62. this.connector = sentinelConnector;
  63. }
  64. else {
  65. this.connector = new connectors_1.StandaloneConnector(this.options);
  66. }
  67. if (this.options.scripts) {
  68. Object.entries(this.options.scripts).forEach(([name, definition]) => {
  69. this.defineCommand(name, definition);
  70. });
  71. }
  72. // end(or wait) -> connecting -> connect -> ready -> end
  73. if (this.options.lazyConnect) {
  74. this.setStatus("wait");
  75. }
  76. else {
  77. this.connect().catch(lodash_1.noop);
  78. }
  79. }
  80. /**
  81. * Create a Redis instance.
  82. * This is the same as `new Redis()` but is included for compatibility with node-redis.
  83. */
  84. static createClient(...args) {
  85. return new Redis(...args);
  86. }
  87. get autoPipelineQueueSize() {
  88. let queued = 0;
  89. for (const pipeline of this._autoPipelines.values()) {
  90. queued += pipeline.length;
  91. }
  92. return queued;
  93. }
  94. /**
  95. * Create a connection to Redis.
  96. * This method will be invoked automatically when creating a new Redis instance
  97. * unless `lazyConnect: true` is passed.
  98. *
  99. * When calling this method manually, a Promise is returned, which will
  100. * be resolved when the connection status is ready.
  101. */
  102. connect(callback) {
  103. const promise = new Promise((resolve, reject) => {
  104. if (this.status === "connecting" ||
  105. this.status === "connect" ||
  106. this.status === "ready") {
  107. reject(new Error("Redis is already connecting/connected"));
  108. return;
  109. }
  110. this.connectionEpoch += 1;
  111. this.setStatus("connecting");
  112. const { options } = this;
  113. this.condition = {
  114. select: options.db,
  115. auth: options.username
  116. ? [options.username, options.password]
  117. : options.password,
  118. subscriber: false,
  119. };
  120. const _this = this;
  121. (0, standard_as_callback_1.default)(this.connector.connect(function (type, err) {
  122. _this.silentEmit(type, err);
  123. }), function (err, stream) {
  124. if (err) {
  125. _this.flushQueue(err);
  126. _this.silentEmit("error", err);
  127. reject(err);
  128. _this.setStatus("end");
  129. return;
  130. }
  131. let CONNECT_EVENT = options.tls ? "secureConnect" : "connect";
  132. if ("sentinels" in options &&
  133. options.sentinels &&
  134. !options.enableTLSForSentinelMode) {
  135. CONNECT_EVENT = "connect";
  136. }
  137. _this.stream = stream;
  138. if (options.noDelay) {
  139. stream.setNoDelay(true);
  140. }
  141. // Node ignores setKeepAlive before connect, therefore we wait for the event:
  142. // https://github.com/nodejs/node/issues/31663
  143. if (typeof options.keepAlive === "number") {
  144. if (stream.connecting) {
  145. stream.once(CONNECT_EVENT, () => {
  146. stream.setKeepAlive(true, options.keepAlive);
  147. });
  148. }
  149. else {
  150. stream.setKeepAlive(true, options.keepAlive);
  151. }
  152. }
  153. if (stream.connecting) {
  154. stream.once(CONNECT_EVENT, eventHandler.connectHandler(_this));
  155. if (options.connectTimeout) {
  156. /*
  157. * Typically, Socket#setTimeout(0) will clear the timer
  158. * set before. However, in some platforms (Electron 3.x~4.x),
  159. * the timer will not be cleared. So we introduce a variable here.
  160. *
  161. * See https://github.com/electron/electron/issues/14915
  162. */
  163. let connectTimeoutCleared = false;
  164. stream.setTimeout(options.connectTimeout, function () {
  165. if (connectTimeoutCleared) {
  166. return;
  167. }
  168. stream.setTimeout(0);
  169. stream.destroy();
  170. const err = new Error("connect ETIMEDOUT");
  171. // @ts-expect-error
  172. err.errorno = "ETIMEDOUT";
  173. // @ts-expect-error
  174. err.code = "ETIMEDOUT";
  175. // @ts-expect-error
  176. err.syscall = "connect";
  177. eventHandler.errorHandler(_this)(err);
  178. });
  179. stream.once(CONNECT_EVENT, function () {
  180. connectTimeoutCleared = true;
  181. stream.setTimeout(0);
  182. });
  183. }
  184. }
  185. else if (stream.destroyed) {
  186. const firstError = _this.connector.firstError;
  187. if (firstError) {
  188. process.nextTick(() => {
  189. eventHandler.errorHandler(_this)(firstError);
  190. });
  191. }
  192. process.nextTick(eventHandler.closeHandler(_this));
  193. }
  194. else {
  195. process.nextTick(eventHandler.connectHandler(_this));
  196. }
  197. if (!stream.destroyed) {
  198. stream.once("error", eventHandler.errorHandler(_this));
  199. stream.once("close", eventHandler.closeHandler(_this));
  200. }
  201. const connectionReadyHandler = function () {
  202. _this.removeListener("close", connectionCloseHandler);
  203. resolve();
  204. };
  205. var connectionCloseHandler = function () {
  206. _this.removeListener("ready", connectionReadyHandler);
  207. reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
  208. };
  209. _this.once("ready", connectionReadyHandler);
  210. _this.once("close", connectionCloseHandler);
  211. });
  212. });
  213. return (0, standard_as_callback_1.default)(promise, callback);
  214. }
  215. /**
  216. * Disconnect from Redis.
  217. *
  218. * This method closes the connection immediately,
  219. * and may lose some pending replies that haven't written to client.
  220. * If you want to wait for the pending replies, use Redis#quit instead.
  221. */
  222. disconnect(reconnect = false) {
  223. if (!reconnect) {
  224. this.manuallyClosing = true;
  225. }
  226. if (this.reconnectTimeout && !reconnect) {
  227. clearTimeout(this.reconnectTimeout);
  228. this.reconnectTimeout = null;
  229. }
  230. if (this.status === "wait") {
  231. eventHandler.closeHandler(this)();
  232. }
  233. else {
  234. this.connector.disconnect();
  235. }
  236. }
  237. /**
  238. * Disconnect from Redis.
  239. *
  240. * @deprecated
  241. */
  242. end() {
  243. this.disconnect();
  244. }
  245. /**
  246. * Create a new instance with the same options as the current one.
  247. *
  248. * @example
  249. * ```js
  250. * var redis = new Redis(6380);
  251. * var anotherRedis = redis.duplicate();
  252. * ```
  253. */
  254. duplicate(override) {
  255. return new Redis({ ...this.options, ...override });
  256. }
  257. /**
  258. * Mode of the connection.
  259. *
  260. * One of `"normal"`, `"subscriber"`, or `"monitor"`. When the connection is
  261. * not in `"normal"` mode, certain commands are not allowed.
  262. */
  263. get mode() {
  264. var _a;
  265. return this.options.monitor
  266. ? "monitor"
  267. : ((_a = this.condition) === null || _a === void 0 ? void 0 : _a.subscriber)
  268. ? "subscriber"
  269. : "normal";
  270. }
  271. /**
  272. * Listen for all requests received by the server in real time.
  273. *
  274. * This command will create a new connection to Redis and send a
  275. * MONITOR command via the new connection in order to avoid disturbing
  276. * the current connection.
  277. *
  278. * @param callback The callback function. If omit, a promise will be returned.
  279. * @example
  280. * ```js
  281. * var redis = new Redis();
  282. * redis.monitor(function (err, monitor) {
  283. * // Entering monitoring mode.
  284. * monitor.on('monitor', function (time, args, source, database) {
  285. * console.log(time + ": " + util.inspect(args));
  286. * });
  287. * });
  288. *
  289. * // supports promise as well as other commands
  290. * redis.monitor().then(function (monitor) {
  291. * monitor.on('monitor', function (time, args, source, database) {
  292. * console.log(time + ": " + util.inspect(args));
  293. * });
  294. * });
  295. * ```
  296. */
  297. monitor(callback) {
  298. const monitorInstance = this.duplicate({
  299. monitor: true,
  300. lazyConnect: false,
  301. });
  302. return (0, standard_as_callback_1.default)(new Promise(function (resolve, reject) {
  303. monitorInstance.once("error", reject);
  304. monitorInstance.once("monitoring", function () {
  305. resolve(monitorInstance);
  306. });
  307. }), callback);
  308. }
  309. /**
  310. * Send a command to Redis
  311. *
  312. * This method is used internally and in most cases you should not
  313. * use it directly. If you need to send a command that is not supported
  314. * by the library, you can use the `call` method:
  315. *
  316. * ```js
  317. * const redis = new Redis();
  318. *
  319. * redis.call('set', 'foo', 'bar');
  320. * // or
  321. * redis.call(['set', 'foo', 'bar']);
  322. * ```
  323. *
  324. * @ignore
  325. */
  326. sendCommand(command, stream) {
  327. var _a, _b;
  328. if (this.status === "wait") {
  329. this.connect().catch(lodash_1.noop);
  330. }
  331. if (this.status === "end") {
  332. command.reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
  333. return command.promise;
  334. }
  335. if (((_a = this.condition) === null || _a === void 0 ? void 0 : _a.subscriber) &&
  336. !Command_1.default.checkFlag("VALID_IN_SUBSCRIBER_MODE", command.name)) {
  337. command.reject(new Error("Connection in subscriber mode, only subscriber commands may be used"));
  338. return command.promise;
  339. }
  340. if (typeof this.options.commandTimeout === "number") {
  341. command.setTimeout(this.options.commandTimeout);
  342. }
  343. let writable = this.status === "ready" ||
  344. (!stream &&
  345. this.status === "connect" &&
  346. (0, commands_1.exists)(command.name) &&
  347. (0, commands_1.hasFlag)(command.name, "loading"));
  348. if (!this.stream) {
  349. writable = false;
  350. }
  351. else if (!this.stream.writable) {
  352. writable = false;
  353. // @ts-expect-error
  354. }
  355. else if (this.stream._writableState && this.stream._writableState.ended) {
  356. // TODO: We should be able to remove this as the PR has already been merged.
  357. // https://github.com/iojs/io.js/pull/1217
  358. writable = false;
  359. }
  360. if (!writable) {
  361. if (!this.options.enableOfflineQueue) {
  362. command.reject(new Error("Stream isn't writeable and enableOfflineQueue options is false"));
  363. return command.promise;
  364. }
  365. if (command.name === "quit" && this.offlineQueue.length === 0) {
  366. this.disconnect();
  367. command.resolve(Buffer.from("OK"));
  368. return command.promise;
  369. }
  370. // @ts-expect-error
  371. if (debug.enabled) {
  372. debug("queue command[%s]: %d -> %s(%o)", this._getDescription(), this.condition.select, command.name, command.args);
  373. }
  374. this.offlineQueue.push({
  375. command: command,
  376. stream: stream,
  377. select: this.condition.select,
  378. });
  379. }
  380. else {
  381. // @ts-expect-error
  382. if (debug.enabled) {
  383. debug("write command[%s]: %d -> %s(%o)", this._getDescription(), (_b = this.condition) === null || _b === void 0 ? void 0 : _b.select, command.name, command.args);
  384. }
  385. if (stream) {
  386. if ("isPipeline" in stream && stream.isPipeline) {
  387. stream.write(command.toWritable(stream.destination.redis.stream));
  388. }
  389. else {
  390. stream.write(command.toWritable(stream));
  391. }
  392. }
  393. else {
  394. this.stream.write(command.toWritable(this.stream));
  395. }
  396. this.commandQueue.push({
  397. command: command,
  398. stream: stream,
  399. select: this.condition.select,
  400. });
  401. if (Command_1.default.checkFlag("WILL_DISCONNECT", command.name)) {
  402. this.manuallyClosing = true;
  403. }
  404. if (this.options.socketTimeout !== undefined && this.socketTimeoutTimer === undefined) {
  405. this.setSocketTimeout();
  406. }
  407. }
  408. if (command.name === "select" && (0, utils_1.isInt)(command.args[0])) {
  409. const db = parseInt(command.args[0], 10);
  410. if (this.condition.select !== db) {
  411. this.condition.select = db;
  412. this.emit("select", db);
  413. debug("switch to db [%d]", this.condition.select);
  414. }
  415. }
  416. return command.promise;
  417. }
  418. setSocketTimeout() {
  419. this.socketTimeoutTimer = setTimeout(() => {
  420. this.stream.destroy(new Error(`Socket timeout. Expecting data, but didn't receive any in ${this.options.socketTimeout}ms.`));
  421. this.socketTimeoutTimer = undefined;
  422. }, this.options.socketTimeout);
  423. // this handler must run after the "data" handler in "DataHandler"
  424. // so that `this.commandQueue.length` will be updated
  425. this.stream.once("data", () => {
  426. clearTimeout(this.socketTimeoutTimer);
  427. this.socketTimeoutTimer = undefined;
  428. if (this.commandQueue.length === 0)
  429. return;
  430. this.setSocketTimeout();
  431. });
  432. }
  433. scanStream(options) {
  434. return this.createScanStream("scan", { options });
  435. }
  436. scanBufferStream(options) {
  437. return this.createScanStream("scanBuffer", { options });
  438. }
  439. sscanStream(key, options) {
  440. return this.createScanStream("sscan", { key, options });
  441. }
  442. sscanBufferStream(key, options) {
  443. return this.createScanStream("sscanBuffer", { key, options });
  444. }
  445. hscanStream(key, options) {
  446. return this.createScanStream("hscan", { key, options });
  447. }
  448. hscanBufferStream(key, options) {
  449. return this.createScanStream("hscanBuffer", { key, options });
  450. }
  451. zscanStream(key, options) {
  452. return this.createScanStream("zscan", { key, options });
  453. }
  454. zscanBufferStream(key, options) {
  455. return this.createScanStream("zscanBuffer", { key, options });
  456. }
  457. /**
  458. * Emit only when there's at least one listener.
  459. *
  460. * @ignore
  461. */
  462. silentEmit(eventName, arg) {
  463. let error;
  464. if (eventName === "error") {
  465. error = arg;
  466. if (this.status === "end") {
  467. return;
  468. }
  469. if (this.manuallyClosing) {
  470. // ignore connection related errors when manually disconnecting
  471. if (error instanceof Error &&
  472. (error.message === utils_1.CONNECTION_CLOSED_ERROR_MSG ||
  473. // @ts-expect-error
  474. error.syscall === "connect" ||
  475. // @ts-expect-error
  476. error.syscall === "read")) {
  477. return;
  478. }
  479. }
  480. }
  481. if (this.listeners(eventName).length > 0) {
  482. return this.emit.apply(this, arguments);
  483. }
  484. if (error && error instanceof Error) {
  485. console.error("[ioredis] Unhandled error event:", error.stack);
  486. }
  487. return false;
  488. }
  489. /**
  490. * @ignore
  491. */
  492. recoverFromFatalError(_commandError, err, options) {
  493. this.flushQueue(err, options);
  494. this.silentEmit("error", err);
  495. this.disconnect(true);
  496. }
  497. /**
  498. * @ignore
  499. */
  500. handleReconnection(err, item) {
  501. var _a;
  502. let needReconnect = false;
  503. if (this.options.reconnectOnError) {
  504. needReconnect = this.options.reconnectOnError(err);
  505. }
  506. switch (needReconnect) {
  507. case 1:
  508. case true:
  509. if (this.status !== "reconnecting") {
  510. this.disconnect(true);
  511. }
  512. item.command.reject(err);
  513. break;
  514. case 2:
  515. if (this.status !== "reconnecting") {
  516. this.disconnect(true);
  517. }
  518. if (((_a = this.condition) === null || _a === void 0 ? void 0 : _a.select) !== item.select &&
  519. item.command.name !== "select") {
  520. this.select(item.select);
  521. }
  522. // TODO
  523. // @ts-expect-error
  524. this.sendCommand(item.command);
  525. break;
  526. default:
  527. item.command.reject(err);
  528. }
  529. }
  530. /**
  531. * Get description of the connection. Used for debugging.
  532. */
  533. _getDescription() {
  534. let description;
  535. if ("path" in this.options && this.options.path) {
  536. description = this.options.path;
  537. }
  538. else if (this.stream &&
  539. this.stream.remoteAddress &&
  540. this.stream.remotePort) {
  541. description = this.stream.remoteAddress + ":" + this.stream.remotePort;
  542. }
  543. else if ("host" in this.options && this.options.host) {
  544. description = this.options.host + ":" + this.options.port;
  545. }
  546. else {
  547. // Unexpected
  548. description = "";
  549. }
  550. if (this.options.connectionName) {
  551. description += ` (${this.options.connectionName})`;
  552. }
  553. return description;
  554. }
  555. resetCommandQueue() {
  556. this.commandQueue = new Deque();
  557. }
  558. resetOfflineQueue() {
  559. this.offlineQueue = new Deque();
  560. }
  561. parseOptions(...args) {
  562. const options = {};
  563. let isTls = false;
  564. for (let i = 0; i < args.length; ++i) {
  565. const arg = args[i];
  566. if (arg === null || typeof arg === "undefined") {
  567. continue;
  568. }
  569. if (typeof arg === "object") {
  570. (0, lodash_1.defaults)(options, arg);
  571. }
  572. else if (typeof arg === "string") {
  573. (0, lodash_1.defaults)(options, (0, utils_1.parseURL)(arg));
  574. if (arg.startsWith("rediss://")) {
  575. isTls = true;
  576. }
  577. }
  578. else if (typeof arg === "number") {
  579. options.port = arg;
  580. }
  581. else {
  582. throw new Error("Invalid argument " + arg);
  583. }
  584. }
  585. if (isTls) {
  586. (0, lodash_1.defaults)(options, { tls: true });
  587. }
  588. (0, lodash_1.defaults)(options, Redis.defaultOptions);
  589. if (typeof options.port === "string") {
  590. options.port = parseInt(options.port, 10);
  591. }
  592. if (typeof options.db === "string") {
  593. options.db = parseInt(options.db, 10);
  594. }
  595. // @ts-expect-error
  596. this.options = (0, utils_1.resolveTLSProfile)(options);
  597. }
  598. /**
  599. * Change instance's status
  600. */
  601. setStatus(status, arg) {
  602. // @ts-expect-error
  603. if (debug.enabled) {
  604. debug("status[%s]: %s -> %s", this._getDescription(), this.status || "[empty]", status);
  605. }
  606. this.status = status;
  607. process.nextTick(this.emit.bind(this, status, arg));
  608. }
  609. createScanStream(command, { key, options = {} }) {
  610. return new ScanStream_1.default({
  611. objectMode: true,
  612. key: key,
  613. redis: this,
  614. command: command,
  615. ...options,
  616. });
  617. }
  618. /**
  619. * Flush offline queue and command queue with error.
  620. *
  621. * @param error The error object to send to the commands
  622. * @param options options
  623. */
  624. flushQueue(error, options) {
  625. options = (0, lodash_1.defaults)({}, options, {
  626. offlineQueue: true,
  627. commandQueue: true,
  628. });
  629. let item;
  630. if (options.offlineQueue) {
  631. while ((item = this.offlineQueue.shift())) {
  632. item.command.reject(error);
  633. }
  634. }
  635. if (options.commandQueue) {
  636. if (this.commandQueue.length > 0) {
  637. if (this.stream) {
  638. this.stream.removeAllListeners("data");
  639. }
  640. while ((item = this.commandQueue.shift())) {
  641. item.command.reject(error);
  642. }
  643. }
  644. }
  645. }
  646. /**
  647. * Check whether Redis has finished loading the persistent data and is able to
  648. * process commands.
  649. */
  650. _readyCheck(callback) {
  651. const _this = this;
  652. this.info(function (err, res) {
  653. if (err) {
  654. if (err.message && err.message.includes("NOPERM")) {
  655. console.warn(`Skipping the ready check because INFO command fails: "${err.message}". You can disable ready check with "enableReadyCheck". More: https://github.com/luin/ioredis/wiki/Disable-ready-check.`);
  656. return callback(null, {});
  657. }
  658. return callback(err);
  659. }
  660. if (typeof res !== "string") {
  661. return callback(null, res);
  662. }
  663. const info = {};
  664. const lines = res.split("\r\n");
  665. for (let i = 0; i < lines.length; ++i) {
  666. const [fieldName, ...fieldValueParts] = lines[i].split(":");
  667. const fieldValue = fieldValueParts.join(":");
  668. if (fieldValue) {
  669. info[fieldName] = fieldValue;
  670. }
  671. }
  672. if (!info.loading || info.loading === "0") {
  673. callback(null, info);
  674. }
  675. else {
  676. const loadingEtaMs = (info.loading_eta_seconds || 1) * 1000;
  677. const retryTime = _this.options.maxLoadingRetryTime &&
  678. _this.options.maxLoadingRetryTime < loadingEtaMs
  679. ? _this.options.maxLoadingRetryTime
  680. : loadingEtaMs;
  681. debug("Redis server still loading, trying again in " + retryTime + "ms");
  682. setTimeout(function () {
  683. _this._readyCheck(callback);
  684. }, retryTime);
  685. }
  686. }).catch(lodash_1.noop);
  687. }
  688. }
  689. Redis.Cluster = cluster_1.default;
  690. Redis.Command = Command_1.default;
  691. /**
  692. * Default options
  693. */
  694. Redis.defaultOptions = RedisOptions_1.DEFAULT_REDIS_OPTIONS;
  695. (0, applyMixin_1.default)(Redis, events_1.EventEmitter);
  696. (0, transaction_1.addTransactionSupport)(Redis.prototype);
  697. exports.default = Redis;