@quark-fw/plugin-threads 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +6 -6
  2. package/src/index.ts +26 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quark-fw/plugin-threads",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "main": "src/index.ts",
5
5
  "types": "src/index.ts",
6
6
  "type": "module",
@@ -21,16 +21,16 @@
21
21
  "access": "public"
22
22
  },
23
23
  "dependencies": {
24
- "@quark-fw/atom": "0.1.15",
25
- "@quark-fw/plugin": "0.1.15",
26
- "@quark-fw/plugin-env": "0.1.15",
27
- "@quark-fw/plugin-redis": "0.1.15",
24
+ "@quark-fw/atom": "0.1.16",
25
+ "@quark-fw/plugin": "0.1.16",
26
+ "@quark-fw/plugin-env": "0.1.16",
27
+ "@quark-fw/plugin-redis": "0.1.16",
28
28
  "chokidar": "^5.0.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "ioredis": "^5.9.3"
32
32
  },
33
33
  "peerDependencies": {
34
- "@quark-fw/core": "0.1.15"
34
+ "@quark-fw/core": "0.1.16"
35
35
  }
36
36
  }
package/src/index.ts CHANGED
@@ -38,6 +38,19 @@ await quark.registerPlugin({
38
38
  const handles = new Map<string, WorkerHandle[]>();
39
39
  // "file#instance" -> число рестартов подряд
40
40
  const restarts = new Map<string, number>();
41
+ // "file#instance" -> отложенный рестарт упавшего инстанса. Упавший
42
+ // инстанс уже убран из handles, поэтому stop() его не видит — без
43
+ // отмены таймера остановленный (или удалённый) воркер воскресал, а
44
+ // stop+spawn при правке файла давали дубль инстанса.
45
+ const pendingRestarts = new Map<string, NodeJS.Timeout>();
46
+ const cancelRestarts = (file: string) => {
47
+ for (const [key, timer] of pendingRestarts) {
48
+ if (!key.startsWith(`${file}#`)) continue;
49
+ clearTimeout(timer);
50
+ pendingRestarts.delete(key);
51
+ restarts.delete(key);
52
+ }
53
+ };
41
54
 
42
55
  const updateList = () => {
43
56
  list.value = [...handles.values()].flat().map((h) => ({
@@ -104,11 +117,13 @@ await quark.registerPlugin({
104
117
  restarts.set(key, n + 1);
105
118
  const delay = Math.min(1000 * 2 ** n, RESTART_MAX_DELAY_MS);
106
119
 
107
- setTimeout(() => {
120
+ const timer = setTimeout(() => {
121
+ pendingRestarts.delete(key);
108
122
  if (destroyed) return;
109
123
  quark.ee.value.emit("thread:restart", file, instance);
110
124
  forkInstance(file, instance);
111
125
  }, delay).unref();
126
+ pendingRestarts.set(key, timer);
112
127
  });
113
128
  };
114
129
 
@@ -117,11 +132,19 @@ await quark.registerPlugin({
117
132
  handle.stopping = true;
118
133
  if (handle.child.exitCode !== null) return resolve();
119
134
 
135
+ // SIGTERM после таймаута, SIGKILL — если и его проигнорировали:
136
+ // иначе stop() и выключение приложения висели бы вечно
137
+ let killTimer: NodeJS.Timeout | undefined;
120
138
  const timeout = setTimeout(() => {
121
139
  handle.child.kill();
140
+ killTimer = setTimeout(
141
+ () => handle.child.kill("SIGKILL"),
142
+ SHUTDOWN_TIMEOUT_MS,
143
+ );
122
144
  }, SHUTDOWN_TIMEOUT_MS);
123
145
  handle.child.once("exit", () => {
124
146
  clearTimeout(timeout);
147
+ clearTimeout(killTimer);
125
148
  resolve();
126
149
  });
127
150
  try {
@@ -135,11 +158,13 @@ await quark.registerPlugin({
135
158
  list,
136
159
  async spawn(file) {
137
160
  const abs = path.resolve(file);
161
+ cancelRestarts(abs);
138
162
  if ((handles.get(abs) ?? []).length) return;
139
163
  forkInstance(abs, 0);
140
164
  },
141
165
  async stop(file) {
142
166
  const abs = path.resolve(file);
167
+ cancelRestarts(abs);
143
168
  const all = handles.get(abs) ?? [];
144
169
  await Promise.all(all.map(stopHandle));
145
170
  handles.delete(abs);