taist 0.1.5 → 0.1.7
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.
- package/lib/trace-collector.js +29 -13
- package/lib/trace-reporter.js +67 -9
- package/package.json +1 -1
package/lib/trace-collector.js
CHANGED
|
@@ -184,28 +184,44 @@ export class TraceCollector extends EventEmitter {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
return new Promise((resolve) => {
|
|
187
|
-
//
|
|
187
|
+
// Gracefully close any remaining connections (allows pending data to be read)
|
|
188
188
|
for (const socket of this.connections) {
|
|
189
|
-
|
|
189
|
+
try {
|
|
190
|
+
socket.end();
|
|
191
|
+
} catch {
|
|
192
|
+
try { socket.destroy(); } catch { /* ignore */ }
|
|
193
|
+
}
|
|
190
194
|
}
|
|
191
|
-
this.connections.clear();
|
|
192
|
-
|
|
193
|
-
// Close server
|
|
194
|
-
this.server.close(() => {
|
|
195
|
-
this.started = false;
|
|
196
195
|
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
// Give a moment for final data to arrive before destroying
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
// Force destroy any sockets that didn't close gracefully
|
|
199
|
+
for (const socket of this.connections) {
|
|
199
200
|
try {
|
|
200
|
-
|
|
201
|
+
socket.destroy();
|
|
201
202
|
} catch {
|
|
202
203
|
// Ignore
|
|
203
204
|
}
|
|
204
205
|
}
|
|
206
|
+
this.connections.clear();
|
|
207
|
+
|
|
208
|
+
// Close server
|
|
209
|
+
this.server.close(() => {
|
|
210
|
+
this.started = false;
|
|
211
|
+
|
|
212
|
+
// Clean up socket file
|
|
213
|
+
if (process.platform !== "win32") {
|
|
214
|
+
try {
|
|
215
|
+
fs.unlinkSync(this.socketPath);
|
|
216
|
+
} catch {
|
|
217
|
+
// Ignore
|
|
218
|
+
}
|
|
219
|
+
}
|
|
205
220
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
221
|
+
this.emit("stopped");
|
|
222
|
+
resolve();
|
|
223
|
+
});
|
|
224
|
+
}, 100); // 100ms grace period for data to be read
|
|
209
225
|
});
|
|
210
226
|
}
|
|
211
227
|
|
package/lib/trace-reporter.js
CHANGED
|
@@ -30,6 +30,7 @@ export class TraceReporter extends EventEmitter {
|
|
|
30
30
|
this.flushTimer = null;
|
|
31
31
|
this.workerId = options.workerId || process.pid;
|
|
32
32
|
this.closed = false;
|
|
33
|
+
this.shuttingDown = false; // Prevents race between shutdown signal and SIGTERM
|
|
33
34
|
|
|
34
35
|
logger.debug("[reporter] Created with socketPath:", this.socketPath);
|
|
35
36
|
|
|
@@ -41,7 +42,12 @@ export class TraceReporter extends EventEmitter {
|
|
|
41
42
|
|
|
42
43
|
_setupExitHandlers() {
|
|
43
44
|
const cleanup = (signal) => {
|
|
44
|
-
logger.debug("[reporter] Cleanup triggered by:", signal, "buffer size:", this.buffer.length);
|
|
45
|
+
logger.debug("[reporter] Cleanup triggered by:", signal, "buffer size:", this.buffer.length, "shuttingDown:", this.shuttingDown);
|
|
46
|
+
// If shutdown signal handler is already handling graceful shutdown, don't interfere
|
|
47
|
+
if (this.shuttingDown) {
|
|
48
|
+
logger.debug("[reporter] Shutdown already in progress, skipping cleanup");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
45
51
|
if (!this.closed) {
|
|
46
52
|
this.flushSync();
|
|
47
53
|
this.close();
|
|
@@ -152,12 +158,16 @@ export class TraceReporter extends EventEmitter {
|
|
|
152
158
|
|
|
153
159
|
/**
|
|
154
160
|
* Handle shutdown signal from collector.
|
|
155
|
-
*
|
|
161
|
+
* Flushes buffer and waits for data to be sent before closing.
|
|
156
162
|
*/
|
|
157
163
|
_handleShutdown() {
|
|
158
|
-
if (this.closed) return;
|
|
164
|
+
if (this.closed || this.shuttingDown) return;
|
|
159
165
|
|
|
160
|
-
//
|
|
166
|
+
// Set flag immediately to prevent exit handlers from interfering
|
|
167
|
+
this.shuttingDown = true;
|
|
168
|
+
this._stopFlushTimer();
|
|
169
|
+
|
|
170
|
+
// Flush and wait for data to be sent
|
|
161
171
|
if (this.buffer.length > 0 && this.socket && this.connected) {
|
|
162
172
|
const traces = this.buffer.splice(0, this.buffer.length);
|
|
163
173
|
const message = JSON.stringify({
|
|
@@ -166,15 +176,63 @@ export class TraceReporter extends EventEmitter {
|
|
|
166
176
|
data: traces,
|
|
167
177
|
});
|
|
168
178
|
|
|
179
|
+
logger.debug("[reporter] Shutdown flushing", traces.length, "traces");
|
|
180
|
+
|
|
169
181
|
try {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
182
|
+
// Write data
|
|
183
|
+
const flushed = this.socket.write(message + "\n");
|
|
184
|
+
|
|
185
|
+
if (flushed) {
|
|
186
|
+
// Data was written to kernel buffer, use setImmediate to allow
|
|
187
|
+
// the event loop to process and send the data before closing
|
|
188
|
+
setImmediate(() => {
|
|
189
|
+
this._gracefulClose();
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
// Buffer is full, wait for drain
|
|
193
|
+
this.socket.once('drain', () => {
|
|
194
|
+
logger.debug("[reporter] Drained, closing");
|
|
195
|
+
this._gracefulClose();
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
logger.debug("[reporter] Write error:", err.message);
|
|
175
200
|
this.close();
|
|
176
201
|
}
|
|
177
202
|
} else {
|
|
203
|
+
this._gracefulClose();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Close connection gracefully, allowing pending data to be sent.
|
|
209
|
+
*/
|
|
210
|
+
_gracefulClose() {
|
|
211
|
+
if (!this.socket) {
|
|
212
|
+
this.close();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Use socket.end() for graceful TCP close (sends FIN, allows pending data)
|
|
217
|
+
// Add a small delay to allow the collector to read the data
|
|
218
|
+
this.socket.once('close', () => {
|
|
219
|
+
logger.debug("[reporter] Socket closed gracefully");
|
|
220
|
+
this.closed = true;
|
|
221
|
+
this.connected = false;
|
|
222
|
+
this.socket = null;
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Set a timeout in case close doesn't happen
|
|
226
|
+
const closeTimeout = setTimeout(() => {
|
|
227
|
+
logger.debug("[reporter] Close timeout, forcing");
|
|
228
|
+
this.close();
|
|
229
|
+
}, 500);
|
|
230
|
+
closeTimeout.unref();
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
this.socket.end();
|
|
234
|
+
} catch {
|
|
235
|
+
clearTimeout(closeTimeout);
|
|
178
236
|
this.close();
|
|
179
237
|
}
|
|
180
238
|
}
|