Reload.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /**
  2. * Copyright 2013-2022 the PM2 project authors. All rights reserved.
  3. * Use of this source code is governed by a license that
  4. * can be found in the LICENSE file.
  5. */
  6. 'use strict';
  7. /**
  8. * @file Reload functions related
  9. * @author Alexandre Strzelewicz <as@unitech.io>
  10. * @project PM2
  11. */
  12. var cst = require('../../constants.js');
  13. var Utility = require('../Utility.js');
  14. /**
  15. * softReload will wait permission from process to exit
  16. * @method softReload
  17. * @param {} God
  18. * @param {} id
  19. * @param {} cb
  20. * @return Literal
  21. */
  22. function softReload(God, id, cb) {
  23. var t_key = '_old_' + id;
  24. // Move old worker to tmp id
  25. God.clusters_db[t_key] = God.clusters_db[id];
  26. delete God.clusters_db[id];
  27. var old_worker = God.clusters_db[t_key];
  28. // Deep copy
  29. var new_env = Utility.clone(old_worker.pm2_env);
  30. // Reset created_at and unstable_restarts
  31. God.resetState(new_env);
  32. new_env.restart_time += 1;
  33. old_worker.pm2_env.pm_id = t_key;
  34. old_worker.pm_id = t_key;
  35. God.executeApp(new_env, function(err, new_worker) {
  36. if (err) return cb(err);
  37. var timer = null;
  38. var onListen = function () {
  39. clearTimeout(timer);
  40. softCleanDeleteProcess();
  41. console.log('-softReload- New worker listening');
  42. };
  43. // Bind to know when the new process is up
  44. new_worker.once('listening', onListen);
  45. timer = setTimeout(function() {
  46. new_worker.removeListener('listening', onListen);
  47. softCleanDeleteProcess();
  48. }, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
  49. // Remove old worker properly
  50. var softCleanDeleteProcess = function () {
  51. var cleanUp = function () {
  52. clearTimeout(timer);
  53. console.log('-softReload- Old worker disconnected');
  54. return God.deleteProcessId(t_key, cb);
  55. };
  56. old_worker.once('disconnect', cleanUp);
  57. try {
  58. if (old_worker.state != 'dead' && old_worker.state != 'disconnected')
  59. old_worker.send && old_worker.send('shutdown');
  60. else {
  61. clearTimeout(timer);
  62. console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id);
  63. return God.deleteProcessId(t_key, cb);
  64. }
  65. } catch(e) {
  66. clearTimeout(timer);
  67. console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id);
  68. return God.deleteProcessId(t_key, cb);
  69. }
  70. timer = setTimeout(function () {
  71. old_worker.removeListener('disconnect', cleanUp);
  72. return God.deleteProcessId(t_key, cb);
  73. }, cst.GRACEFUL_TIMEOUT);
  74. return false;
  75. };
  76. return false;
  77. });
  78. return false;
  79. };
  80. /**
  81. * hardReload will reload without waiting permission from process
  82. * @method hardReload
  83. * @param {} God
  84. * @param {} id
  85. * @param {} cb
  86. * @return Literal
  87. */
  88. function hardReload(God, id, wait_msg, cb) {
  89. var t_key = '_old_' + id;
  90. // Move old worker to tmp id
  91. God.clusters_db[t_key] = God.clusters_db[id];
  92. delete God.clusters_db[id];
  93. var old_worker = God.clusters_db[t_key];
  94. // Deep copy
  95. var new_env = Utility.clone(old_worker.pm2_env);
  96. new_env.restart_time += 1;
  97. // Reset created_at and unstable_restarts
  98. God.resetState(new_env);
  99. old_worker.pm2_env.pm_id = t_key;
  100. old_worker.pm_id = t_key;
  101. var timer = null;
  102. var readySignalSent = false;
  103. var onListen = function () {
  104. clearTimeout(timer);
  105. readySignalSent = true;
  106. console.log('-reload- New worker listening');
  107. return God.deleteProcessId(t_key, cb);
  108. };
  109. var listener = function (packet) {
  110. if (packet.raw === 'ready' &&
  111. packet.process.name === old_worker.pm2_env.name &&
  112. packet.process.pm_id === id) {
  113. God.bus.removeListener('process:msg', listener);
  114. return onListen();
  115. }
  116. };
  117. if (wait_msg !== 'listening') {
  118. God.bus.on('process:msg', listener);
  119. }
  120. God.executeApp(new_env, function(err, new_worker) {
  121. if (err) return cb(err);
  122. // Bind to know when the new process is up
  123. if (wait_msg === 'listening') {
  124. new_worker.once('listening', onListen);
  125. }
  126. timer = setTimeout(function() {
  127. if (readySignalSent) {
  128. return;
  129. }
  130. if (wait_msg === 'listening')
  131. new_worker.removeListener(wait_msg, onListen);
  132. else
  133. God.bus.removeListener('process:msg', listener);
  134. return God.deleteProcessId(t_key, cb);
  135. }, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
  136. return false;
  137. });
  138. return false;
  139. };
  140. /**
  141. * Description
  142. * @method exports
  143. * @param {} God
  144. * @return
  145. */
  146. module.exports = function(God) {
  147. /**
  148. * Reload
  149. * @method softReloadProcessId
  150. * @param {} id
  151. * @param {} cb
  152. * @return CallExpression
  153. */
  154. God.softReloadProcessId = function(opts, cb) {
  155. var id = opts.id;
  156. var env = opts.env || {};
  157. if (!(id in God.clusters_db))
  158. return cb(new Error(`pm_id ${id} not available in ${id}`));
  159. if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS &&
  160. God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode' &&
  161. !God.clusters_db[id].pm2_env.wait_ready) {
  162. Utility.extend(God.clusters_db[id].pm2_env.env, opts.env);
  163. Utility.extendExtraConfig(God.clusters_db[id], opts);
  164. return softReload(God, id, cb);
  165. }
  166. else {
  167. console.log('Process %s in a stopped status, starting it', id);
  168. return God.restartProcessId(opts, cb);
  169. }
  170. };
  171. /**
  172. * Reload
  173. * @method reloadProcessId
  174. * @param {} id
  175. * @param {} cb
  176. * @return CallExpression
  177. */
  178. God.reloadProcessId = function(opts, cb) {
  179. var id = opts.id;
  180. var env = opts.env || {};
  181. if (!(id in God.clusters_db))
  182. return cb(new Error('PM2 ID unknown'));
  183. if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS &&
  184. God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode') {
  185. Utility.extend(God.clusters_db[id].pm2_env.env, opts.env);
  186. Utility.extendExtraConfig(God.clusters_db[id], opts);
  187. var wait_msg = God.clusters_db[id].pm2_env.wait_ready ? 'ready' : 'listening';
  188. return hardReload(God, id, wait_msg, cb);
  189. }
  190. else {
  191. console.log('Process %s in a stopped status, starting it', id);
  192. return God.restartProcessId(opts, cb);
  193. }
  194. };
  195. };