@mhmdhammoud/meritt-utils 1.5.9 → 1.6.0
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/dist/lib/elastic-transport.js +53 -16
- package/package.json +1 -1
- package/src/lib/elastic-transport.ts +67 -21
|
@@ -33,24 +33,12 @@ function getIndexName(index, time) {
|
|
|
33
33
|
}
|
|
34
34
|
return index.replace('%{DATE}', time.substring(0, 10));
|
|
35
35
|
}
|
|
36
|
-
function initializeBulkHandler(opts, client, splitter) {
|
|
36
|
+
function initializeBulkHandler(opts, client, splitter, onFatalError) {
|
|
37
37
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
38
38
|
const esVersion = Number((_b = (_a = opts.esVersion) !== null && _a !== void 0 ? _a : opts['es-version']) !== null && _b !== void 0 ? _b : 7);
|
|
39
39
|
const index = (_c = opts.index) !== null && _c !== void 0 ? _c : 'pino';
|
|
40
40
|
const buildIndexName = typeof index === 'function' ? index : null;
|
|
41
41
|
const opType = esVersion >= 7 ? undefined : undefined;
|
|
42
|
-
// CRITICAL FIX (issue #140): When bulk helper destroys stream after retries exhausted,
|
|
43
|
-
// we must BOTH resurrect the pool AND reinitialize the bulk handler so logging continues.
|
|
44
|
-
// connectionPool.resurrect exists at runtime (elastic-transport) but may not be in types
|
|
45
|
-
const pool = client.connectionPool;
|
|
46
|
-
const splitterWithDestroy = splitter;
|
|
47
|
-
splitterWithDestroy.destroy = function () {
|
|
48
|
-
if (typeof pool.resurrect === 'function') {
|
|
49
|
-
pool.resurrect({ name: 'elasticsearch-js' });
|
|
50
|
-
}
|
|
51
|
-
// Reinitialize bulk handler - without this, logging stops permanently until restart
|
|
52
|
-
initializeBulkHandler(opts, client, splitter);
|
|
53
|
-
};
|
|
54
42
|
const indexName = (time = new Date().toISOString()) => buildIndexName ? buildIndexName(time) : getIndexName(index, time);
|
|
55
43
|
const bulkInsert = client.helpers.bulk({
|
|
56
44
|
datasource: splitter,
|
|
@@ -77,7 +65,10 @@ function initializeBulkHandler(opts, client, splitter) {
|
|
|
77
65
|
splitter.emit('insertError', error);
|
|
78
66
|
},
|
|
79
67
|
});
|
|
80
|
-
bulkInsert.then((stats) => splitter.emit('insert', stats), (err) =>
|
|
68
|
+
bulkInsert.then((stats) => splitter.emit('insert', stats), (err) => {
|
|
69
|
+
splitter.emit('error', err);
|
|
70
|
+
onFatalError(err);
|
|
71
|
+
});
|
|
81
72
|
}
|
|
82
73
|
const createElasticTransport = (opts = {}) => {
|
|
83
74
|
const splitter = split(function (line) {
|
|
@@ -128,10 +119,56 @@ const createElasticTransport = (opts = {}) => {
|
|
|
128
119
|
clientOpts.ConnectionPool = opts.ConnectionPool;
|
|
129
120
|
}
|
|
130
121
|
const client = new elasticsearch_1.Client(clientOpts);
|
|
122
|
+
// CRITICAL FIX (pino-elasticsearch issues #140/#72): after retries are
|
|
123
|
+
// exhausted the bulk helper can stop consuming the stream while the process
|
|
124
|
+
// stays alive. Keep exactly one helper active and replace it after fatal
|
|
125
|
+
// helper failures instead of waiting for a server restart.
|
|
126
|
+
let isBulkHandlerActive = false;
|
|
127
|
+
let isRestartScheduled = false;
|
|
128
|
+
let isTransportClosed = false;
|
|
129
|
+
const pool = client.connectionPool;
|
|
130
|
+
const splitterWithDestroy = splitter;
|
|
131
|
+
const originalDestroy = splitterWithDestroy.destroy.bind(splitterWithDestroy);
|
|
132
|
+
const startBulkHandler = () => {
|
|
133
|
+
if (isTransportClosed || isBulkHandlerActive) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
isBulkHandlerActive = true;
|
|
137
|
+
initializeBulkHandler(opts, client, splitter, () => {
|
|
138
|
+
isBulkHandlerActive = false;
|
|
139
|
+
scheduleBulkHandlerRestart();
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const scheduleBulkHandlerRestart = () => {
|
|
143
|
+
var _a, _b, _c;
|
|
144
|
+
if (isTransportClosed || isRestartScheduled) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
isRestartScheduled = true;
|
|
148
|
+
if (typeof pool.resurrect === 'function') {
|
|
149
|
+
pool.resurrect({ name: 'elasticsearch-js' });
|
|
150
|
+
}
|
|
151
|
+
const retryDelayMs = Math.min(Number((_b = (_a = opts.flushInterval) !== null && _a !== void 0 ? _a : opts['flush-interval']) !== null && _b !== void 0 ? _b : 3000), 5000);
|
|
152
|
+
const timer = setTimeout(() => {
|
|
153
|
+
isRestartScheduled = false;
|
|
154
|
+
startBulkHandler();
|
|
155
|
+
}, retryDelayMs);
|
|
156
|
+
(_c = timer.unref) === null || _c === void 0 ? void 0 : _c.call(timer);
|
|
157
|
+
};
|
|
158
|
+
splitterWithDestroy.destroy = function (err) {
|
|
159
|
+
if (err && !isTransportClosed) {
|
|
160
|
+
scheduleBulkHandlerRestart();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
isTransportClosed = true;
|
|
164
|
+
originalDestroy(err);
|
|
165
|
+
};
|
|
131
166
|
client.diagnostic.on('resurrect', () => {
|
|
132
|
-
|
|
167
|
+
if (!isBulkHandlerActive) {
|
|
168
|
+
scheduleBulkHandlerRestart();
|
|
169
|
+
}
|
|
133
170
|
});
|
|
134
|
-
|
|
171
|
+
startBulkHandler();
|
|
135
172
|
return splitter;
|
|
136
173
|
};
|
|
137
174
|
exports.createElasticTransport = createElasticTransport;
|
package/package.json
CHANGED
|
@@ -76,30 +76,14 @@ function getIndexName(
|
|
|
76
76
|
function initializeBulkHandler(
|
|
77
77
|
opts: ElasticTransportOptions,
|
|
78
78
|
client: Client,
|
|
79
|
-
splitter: NodeJS.ReadWriteStream
|
|
79
|
+
splitter: NodeJS.ReadWriteStream,
|
|
80
|
+
onFatalError: (err: Error) => void
|
|
80
81
|
): void {
|
|
81
82
|
const esVersion = Number(opts.esVersion ?? opts['es-version'] ?? 7)
|
|
82
83
|
const index = opts.index ?? 'pino'
|
|
83
84
|
const buildIndexName = typeof index === 'function' ? index : null
|
|
84
85
|
const opType = esVersion >= 7 ? undefined : undefined
|
|
85
86
|
|
|
86
|
-
// CRITICAL FIX (issue #140): When bulk helper destroys stream after retries exhausted,
|
|
87
|
-
// we must BOTH resurrect the pool AND reinitialize the bulk handler so logging continues.
|
|
88
|
-
// connectionPool.resurrect exists at runtime (elastic-transport) but may not be in types
|
|
89
|
-
const pool = client.connectionPool as {
|
|
90
|
-
resurrect?: (opts: { name: string }) => void
|
|
91
|
-
}
|
|
92
|
-
const splitterWithDestroy = splitter as NodeJS.ReadWriteStream & {
|
|
93
|
-
destroy: (err?: Error) => void
|
|
94
|
-
}
|
|
95
|
-
splitterWithDestroy.destroy = function () {
|
|
96
|
-
if (typeof pool.resurrect === 'function') {
|
|
97
|
-
pool.resurrect({ name: 'elasticsearch-js' })
|
|
98
|
-
}
|
|
99
|
-
// Reinitialize bulk handler - without this, logging stops permanently until restart
|
|
100
|
-
initializeBulkHandler(opts, client, splitter)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
87
|
const indexName = (time = new Date().toISOString()) =>
|
|
104
88
|
buildIndexName ? buildIndexName(time) : getIndexName(index as string, time)
|
|
105
89
|
|
|
@@ -132,7 +116,10 @@ function initializeBulkHandler(
|
|
|
132
116
|
|
|
133
117
|
bulkInsert.then(
|
|
134
118
|
(stats) => splitter.emit('insert', stats),
|
|
135
|
-
(err) =>
|
|
119
|
+
(err) => {
|
|
120
|
+
splitter.emit('error', err)
|
|
121
|
+
onFatalError(err)
|
|
122
|
+
}
|
|
136
123
|
)
|
|
137
124
|
}
|
|
138
125
|
|
|
@@ -193,11 +180,70 @@ export const createElasticTransport = (
|
|
|
193
180
|
|
|
194
181
|
const client = new Client(clientOpts)
|
|
195
182
|
|
|
183
|
+
// CRITICAL FIX (pino-elasticsearch issues #140/#72): after retries are
|
|
184
|
+
// exhausted the bulk helper can stop consuming the stream while the process
|
|
185
|
+
// stays alive. Keep exactly one helper active and replace it after fatal
|
|
186
|
+
// helper failures instead of waiting for a server restart.
|
|
187
|
+
let isBulkHandlerActive = false
|
|
188
|
+
let isRestartScheduled = false
|
|
189
|
+
let isTransportClosed = false
|
|
190
|
+
|
|
191
|
+
const pool = client.connectionPool as {
|
|
192
|
+
resurrect?: (opts: { name: string }) => void
|
|
193
|
+
}
|
|
194
|
+
const splitterWithDestroy = splitter as NodeJS.ReadWriteStream & {
|
|
195
|
+
destroy: (err?: Error) => void
|
|
196
|
+
}
|
|
197
|
+
const originalDestroy = splitterWithDestroy.destroy.bind(splitterWithDestroy)
|
|
198
|
+
|
|
199
|
+
const startBulkHandler = () => {
|
|
200
|
+
if (isTransportClosed || isBulkHandlerActive) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
isBulkHandlerActive = true
|
|
204
|
+
initializeBulkHandler(opts, client, splitter, () => {
|
|
205
|
+
isBulkHandlerActive = false
|
|
206
|
+
scheduleBulkHandlerRestart()
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const scheduleBulkHandlerRestart = () => {
|
|
211
|
+
if (isTransportClosed || isRestartScheduled) {
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
isRestartScheduled = true
|
|
215
|
+
|
|
216
|
+
if (typeof pool.resurrect === 'function') {
|
|
217
|
+
pool.resurrect({ name: 'elasticsearch-js' })
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const retryDelayMs = Math.min(
|
|
221
|
+
Number(opts.flushInterval ?? opts['flush-interval'] ?? 3000),
|
|
222
|
+
5000
|
|
223
|
+
)
|
|
224
|
+
const timer = setTimeout(() => {
|
|
225
|
+
isRestartScheduled = false
|
|
226
|
+
startBulkHandler()
|
|
227
|
+
}, retryDelayMs)
|
|
228
|
+
timer.unref?.()
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
splitterWithDestroy.destroy = function (err?: Error) {
|
|
232
|
+
if (err && !isTransportClosed) {
|
|
233
|
+
scheduleBulkHandlerRestart()
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
isTransportClosed = true
|
|
237
|
+
originalDestroy(err)
|
|
238
|
+
}
|
|
239
|
+
|
|
196
240
|
client.diagnostic.on('resurrect', () => {
|
|
197
|
-
|
|
241
|
+
if (!isBulkHandlerActive) {
|
|
242
|
+
scheduleBulkHandlerRestart()
|
|
243
|
+
}
|
|
198
244
|
})
|
|
199
245
|
|
|
200
|
-
|
|
246
|
+
startBulkHandler()
|
|
201
247
|
|
|
202
248
|
return splitter
|
|
203
249
|
}
|