backlens 0.2.4 → 0.2.5
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/README.md +1 -1
- package/analyze.js +4 -3
- package/package.json +1 -1
- package/server.js +34 -6
- package/ui.html +1 -1
package/README.md
CHANGED
|
@@ -151,7 +151,7 @@ cd backlens && npm install
|
|
|
151
151
|
node --test test/
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
Bug reports and pull requests are welcome.
|
|
154
|
+
Bug reports and pull requests are welcome: see [CONTRIBUTING.md](CONTRIBUTING.md). To report a security problem privately, see [SECURITY.md](SECURITY.md).
|
|
155
155
|
|
|
156
156
|
## License
|
|
157
157
|
|
package/analyze.js
CHANGED
|
@@ -237,8 +237,8 @@ function percentile(values, p) {
|
|
|
237
237
|
|
|
238
238
|
/** Routes and issues across every trace kept. */
|
|
239
239
|
/**
|
|
240
|
-
* fixed: issue key -> when a fix for it was kept
|
|
241
|
-
* that moment shows
|
|
240
|
+
* fixed: issue key -> when a fix for it was kept, and 'route:<route>' -> when code behind that
|
|
241
|
+
* route changed. Such issues are hidden until a request after that moment shows them again.
|
|
242
242
|
*/
|
|
243
243
|
export function summarize(traces, known = [], advice = [], fixed = new Map()) {
|
|
244
244
|
const routes = new Map();
|
|
@@ -323,7 +323,8 @@ export function summarize(traces, known = [], advice = [], fixed = new Map()) {
|
|
|
323
323
|
if (!routes.has(route)) routes.set(route, { route, calls: 0, durations: [], queries: 0, redis: 0, http: 0, errors: 0, issueKeys: new Set(), last: 0 });
|
|
324
324
|
}
|
|
325
325
|
for (const [key, issue] of issues) {
|
|
326
|
-
|
|
326
|
+
const since = Math.max(fixed.get(key) || 0, fixed.get(`route:${issue.route}`) || 0);
|
|
327
|
+
if (since && since >= (issue.lastAt || 0)) {
|
|
327
328
|
issues.delete(key);
|
|
328
329
|
routes.get(issue.route)?.issueKeys.delete(key);
|
|
329
330
|
}
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -35,6 +35,20 @@ export function startServer({ port, root = process.cwd(), token, manualArgv = nu
|
|
|
35
35
|
// Issue key -> the index created for it from the dashboard, so Undo still knows it.
|
|
36
36
|
const createdIndexes = new Map();
|
|
37
37
|
|
|
38
|
+
/** Sends a recorded read to the app without recording it, so the app opens its connections. */
|
|
39
|
+
function warmUp(port, payload) {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const r = http.request({ host: '127.0.0.1', port, method: payload.method, path: payload.url, headers: { ...payload.headers, 'x-backlens': 'skip' } }, (res) => {
|
|
42
|
+
res.resume();
|
|
43
|
+
res.on('end', resolve);
|
|
44
|
+
});
|
|
45
|
+
r.setTimeout(20000, () => r.destroy());
|
|
46
|
+
r.on('error', resolve);
|
|
47
|
+
r.on('close', resolve);
|
|
48
|
+
r.end();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
/** Sends one command to the recorder inside the app (see register.cjs). */
|
|
39
53
|
function appCommand(port, body) {
|
|
40
54
|
return new Promise((resolve, reject) => {
|
|
@@ -337,14 +351,24 @@ export function startServer({ port, root = process.cwd(), token, manualArgv = nu
|
|
|
337
351
|
if (!app || !app.port) return json(res, 409, { error: 'The app is not running. Start it first.' });
|
|
338
352
|
const body = (await readJson(req)) || {};
|
|
339
353
|
let index = null;
|
|
354
|
+
const { issues } = summarize(traces, [...known.keys()], [...advice.values()]);
|
|
355
|
+
const issue = issues.find((i) => i.key === body.key);
|
|
340
356
|
if (action !== 'info') {
|
|
341
|
-
const { issues } = summarize(traces, [...known.keys()], [...advice.values()]);
|
|
342
|
-
const issue = issues.find((i) => i.key === body.key);
|
|
343
357
|
index = issue && issue.index ? issue.index : createdIndexes.get(body.key);
|
|
344
358
|
if (!index) return json(res, 404, { error: 'That suggestion is gone. Refresh the list.' });
|
|
345
359
|
}
|
|
346
360
|
try {
|
|
347
|
-
|
|
361
|
+
let out;
|
|
362
|
+
try {
|
|
363
|
+
out = await appCommand(app.port, { action, ...(index || {}) });
|
|
364
|
+
} catch (err) {
|
|
365
|
+
// A restarted app has no database connection until a request uses one. Replay the
|
|
366
|
+
// request that showed the issue once, unrecorded, so it opens one, then try again.
|
|
367
|
+
const payload = issue && replays.get(issue.example);
|
|
368
|
+
if (!/not used Postgres yet/.test(err.message) || !payload || !['GET', 'HEAD'].includes(payload.method)) throw err;
|
|
369
|
+
await warmUp(app.port, payload);
|
|
370
|
+
out = await appCommand(app.port, { action, ...(index || {}) });
|
|
371
|
+
}
|
|
348
372
|
if (action === 'create') {
|
|
349
373
|
createdIndexes.set(body.key, index);
|
|
350
374
|
fixedAt.set(body.key, Date.now());
|
|
@@ -425,14 +449,18 @@ export function startServer({ port, root = process.cwd(), token, manualArgv = nu
|
|
|
425
449
|
// Recorded requests on a route since the last fix kept there (the code they ran is the
|
|
426
450
|
// code being changed now), oldest first.
|
|
427
451
|
routeTraces: (route) => {
|
|
428
|
-
const since =
|
|
452
|
+
const since = fixedAt.get(`route:${route}`) || 0;
|
|
429
453
|
return traces.filter((t) => t.at > since && routeOf(t.method, t.url, t.template) === route);
|
|
430
454
|
},
|
|
431
455
|
markFixed: (key, after) => {
|
|
432
|
-
|
|
456
|
+
const now = Date.now();
|
|
457
|
+
fixedAt.set(key, now);
|
|
458
|
+
// The code behind this route changed: issues from its older requests point at old
|
|
459
|
+
// lines, so they wait for a request on the new code (the after-fix one below, or later).
|
|
460
|
+
fixedAt.set(`route:${key.split('|')[1]}`, now);
|
|
433
461
|
if (after) {
|
|
434
462
|
const { replayOf, ...trace } = after;
|
|
435
|
-
traces.push({ ...trace, afterFix: true });
|
|
463
|
+
traces.push({ ...trace, at: now + 1, afterFix: true });
|
|
436
464
|
received++;
|
|
437
465
|
if (traces.length > KEEP) replays.delete(traces.shift().id);
|
|
438
466
|
}
|
package/ui.html
CHANGED
|
@@ -790,7 +790,7 @@ function compareCard(j) {
|
|
|
790
790
|
let idx = {};
|
|
791
791
|
async function loadIndexInfo() {
|
|
792
792
|
idx.loading = true;
|
|
793
|
-
try { idx.db = await aiPost('/api/index/info', {}); } catch (err) { idx.dbError = err.message; }
|
|
793
|
+
try { idx.db = await aiPost('/api/index/info', { key: fixJob && fixJob.issueKey }); } catch (err) { idx.dbError = err.message; }
|
|
794
794
|
idx.loading = false;
|
|
795
795
|
renderFix();
|
|
796
796
|
}
|