event_handler.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const command_1 = require("../command");
  4. const errors_1 = require("../errors");
  5. const utils_1 = require("../utils");
  6. const DataHandler_1 = require("../DataHandler");
  7. const debug = utils_1.Debug("connection");
  8. function connectHandler(self) {
  9. return function () {
  10. self.setStatus("connect");
  11. self.resetCommandQueue();
  12. // AUTH command should be processed before any other commands
  13. let flushed = false;
  14. const { connectionEpoch } = self;
  15. if (self.condition.auth) {
  16. self.auth(self.condition.auth, function (err) {
  17. if (connectionEpoch !== self.connectionEpoch) {
  18. return;
  19. }
  20. if (err) {
  21. if (err.message.indexOf("no password is set") === -1) {
  22. flushed = true;
  23. self.recoverFromFatalError(err, err);
  24. }
  25. else {
  26. console.warn("[WARN] Redis server does not require a password, but a password was supplied.");
  27. }
  28. }
  29. });
  30. }
  31. if (self.condition.select) {
  32. self.select(self.condition.select);
  33. }
  34. if (!self.options.enableReadyCheck) {
  35. exports.readyHandler(self)();
  36. }
  37. /*
  38. No need to keep the reference of DataHandler here
  39. because we don't need to do the cleanup.
  40. `Stream#end()` will remove all listeners for us.
  41. */
  42. new DataHandler_1.default(self, {
  43. stringNumbers: self.options.stringNumbers,
  44. dropBufferSupport: self.options.dropBufferSupport
  45. });
  46. if (self.options.enableReadyCheck) {
  47. self._readyCheck(function (err, info) {
  48. if (connectionEpoch !== self.connectionEpoch) {
  49. return;
  50. }
  51. if (err) {
  52. if (!flushed) {
  53. self.recoverFromFatalError(new Error("Ready check failed: " + err.message), err);
  54. }
  55. }
  56. else {
  57. self.serverInfo = info;
  58. if (self.connector.check(info)) {
  59. exports.readyHandler(self)();
  60. }
  61. else {
  62. self.disconnect(true);
  63. }
  64. }
  65. });
  66. }
  67. };
  68. }
  69. exports.connectHandler = connectHandler;
  70. function closeHandler(self) {
  71. return function () {
  72. self.setStatus("close");
  73. if (!self.prevCondition) {
  74. self.prevCondition = self.condition;
  75. }
  76. if (self.commandQueue.length) {
  77. self.prevCommandQueue = self.commandQueue;
  78. }
  79. if (self.manuallyClosing) {
  80. self.manuallyClosing = false;
  81. debug("skip reconnecting since the connection is manually closed.");
  82. return close();
  83. }
  84. if (typeof self.options.retryStrategy !== "function") {
  85. debug("skip reconnecting because `retryStrategy` is not a function");
  86. return close();
  87. }
  88. const retryDelay = self.options.retryStrategy(++self.retryAttempts);
  89. if (typeof retryDelay !== "number") {
  90. debug("skip reconnecting because `retryStrategy` doesn't return a number");
  91. return close();
  92. }
  93. debug("reconnect in %sms", retryDelay);
  94. self.setStatus("reconnecting", retryDelay);
  95. self.reconnectTimeout = setTimeout(function () {
  96. self.reconnectTimeout = null;
  97. self.connect().catch(utils_1.noop);
  98. }, retryDelay);
  99. const { maxRetriesPerRequest } = self.options;
  100. if (typeof maxRetriesPerRequest === "number") {
  101. if (maxRetriesPerRequest < 0) {
  102. debug("maxRetriesPerRequest is negative, ignoring...");
  103. }
  104. else {
  105. const remainder = self.retryAttempts % (maxRetriesPerRequest + 1);
  106. if (remainder === 0) {
  107. debug("reach maxRetriesPerRequest limitation, flushing command queue...");
  108. self.flushQueue(new errors_1.MaxRetriesPerRequestError(maxRetriesPerRequest));
  109. }
  110. }
  111. }
  112. };
  113. function close() {
  114. self.setStatus("end");
  115. self.flushQueue(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
  116. }
  117. }
  118. exports.closeHandler = closeHandler;
  119. function errorHandler(self) {
  120. return function (error) {
  121. debug("error: %s", error);
  122. self.silentEmit("error", error);
  123. };
  124. }
  125. exports.errorHandler = errorHandler;
  126. function readyHandler(self) {
  127. return function () {
  128. self.setStatus("ready");
  129. self.retryAttempts = 0;
  130. if (self.options.monitor) {
  131. self.call("monitor");
  132. const { sendCommand } = self;
  133. self.sendCommand = function (command) {
  134. if (command_1.default.checkFlag("VALID_IN_MONITOR_MODE", command.name)) {
  135. return sendCommand.call(self, command);
  136. }
  137. command.reject(new Error("Connection is in monitoring mode, can't process commands."));
  138. return command.promise;
  139. };
  140. self.once("close", function () {
  141. delete self.sendCommand;
  142. });
  143. self.setStatus("monitoring");
  144. return;
  145. }
  146. const finalSelect = self.prevCondition
  147. ? self.prevCondition.select
  148. : self.condition.select;
  149. if (self.options.connectionName) {
  150. debug("set the connection name [%s]", self.options.connectionName);
  151. self.client("setname", self.options.connectionName).catch(utils_1.noop);
  152. }
  153. if (self.options.readOnly) {
  154. debug("set the connection to readonly mode");
  155. self.readonly().catch(utils_1.noop);
  156. }
  157. if (self.prevCondition) {
  158. const condition = self.prevCondition;
  159. self.prevCondition = null;
  160. if (condition.subscriber && self.options.autoResubscribe) {
  161. // We re-select the previous db first since
  162. // `SELECT` command is not valid in sub mode.
  163. if (self.condition.select !== finalSelect) {
  164. debug("connect to db [%d]", finalSelect);
  165. self.select(finalSelect);
  166. }
  167. const subscribeChannels = condition.subscriber.channels("subscribe");
  168. if (subscribeChannels.length) {
  169. debug("subscribe %d channels", subscribeChannels.length);
  170. self.subscribe(subscribeChannels);
  171. }
  172. const psubscribeChannels = condition.subscriber.channels("psubscribe");
  173. if (psubscribeChannels.length) {
  174. debug("psubscribe %d channels", psubscribeChannels.length);
  175. self.psubscribe(psubscribeChannels);
  176. }
  177. }
  178. }
  179. if (self.prevCommandQueue) {
  180. if (self.options.autoResendUnfulfilledCommands) {
  181. debug("resend %d unfulfilled commands", self.prevCommandQueue.length);
  182. while (self.prevCommandQueue.length > 0) {
  183. const item = self.prevCommandQueue.shift();
  184. if (item.select !== self.condition.select &&
  185. item.command.name !== "select") {
  186. self.select(item.select);
  187. }
  188. self.sendCommand(item.command, item.stream);
  189. }
  190. }
  191. else {
  192. self.prevCommandQueue = null;
  193. }
  194. }
  195. if (self.offlineQueue.length) {
  196. debug("send %d commands in offline queue", self.offlineQueue.length);
  197. const offlineQueue = self.offlineQueue;
  198. self.resetOfflineQueue();
  199. while (offlineQueue.length > 0) {
  200. const item = offlineQueue.shift();
  201. if (item.select !== self.condition.select &&
  202. item.command.name !== "select") {
  203. self.select(item.select);
  204. }
  205. self.sendCommand(item.command, item.stream);
  206. }
  207. }
  208. if (self.condition.select !== finalSelect) {
  209. debug("connect to db [%d]", finalSelect);
  210. self.select(finalSelect);
  211. }
  212. };
  213. }
  214. exports.readyHandler = readyHandler;