Error groups name an old number legacy_number
The field kept 'issue' in its name. A new telemetry migration renames the column; the contracts, store, web page and docs follow. The reference example's session_url uses the /<ws>/-/work/sessions/<id> address.
- G1T-Session:
- ses_01m56w3nj9ecqa57g0647a7evd
8 files+36−140/8 viewed
Binary or large file; its contents are not shown.
| 81 | 81 | An error group names its work item as `item`, its number in the | |
| 82 | 82 | workspace; `update_error` with `item_number` links one, and `null` unlinks | |
| 83 | 83 | it. A group linked to an issue before work items, whose work item is not | |
| 84 | − | known yet, has that issue's number as `legacy_issue`. | |
| 84 | + | known yet, has that old number as `legacy_number`. | |
| 85 | 85 | ||
| 86 | 86 | ## Apps on g1t.page | |
| 87 | 87 |
Binary or large file; its contents are not shown.
| 119 | 119 | {g.service || "app"} | |
| 120 | 120 | </Badge> | |
| 121 | 121 | {g.environment && <Badge tone="neutral">{g.environment}</Badge>} | |
| 122 | − | {(g.item ?? g.legacy_issue) != null && ( | |
| 122 | + | {(g.item ?? g.legacy_number) != null && ( | |
| 123 | 123 | <Link | |
| 124 | − | to={g.item != null ? itemPath(params.owner, g.item) : `${base}/ref/${g.legacy_issue}`} | |
| 124 | + | to={g.item != null ? itemPath(params.owner, g.item) : `${base}/ref/${g.legacy_number}`} | |
| 125 | 125 | className="inline-flex items-center gap-1 text-xs text-accent hover:underline" | |
| 126 | 126 | > | |
| 127 | − | <CircleDot size={12} />#{g.item ?? g.legacy_issue} | |
| 127 | + | <CircleDot size={12} />#{g.item ?? g.legacy_number} | |
| 128 | 128 | </Link> | |
| 129 | 129 | )} | |
| 130 | 130 | </p> | |
| ⋯ | |||
| 162 | 162 | <pre className="mt-2 max-h-72 overflow-auto rounded-md border border-line bg-bg p-3 font-mono text-xs whitespace-pre">{g.stack}</pre> | |
| 163 | 163 | </details> | |
| 164 | 164 | )} | |
| 165 | − | {(can.triage || (can.item && g.item == null && g.legacy_issue == null)) && ( | |
| 165 | + | {(can.triage || (can.item && g.item == null && g.legacy_number == null)) && ( | |
| 166 | 166 | <div className="mt-3 flex flex-wrap items-center gap-2 border-t border-line pt-3"> | |
| 167 | − | {can.item && g.item == null && g.legacy_issue == null && ( | |
| 167 | + | {can.item && g.item == null && g.legacy_number == null && ( | |
| 168 | 168 | <Form method="post" className="flex flex-wrap items-center gap-2"> | |
| 169 | 169 | <input type="hidden" name="fingerprint" value={g.fingerprint} /> | |
| 170 | 170 | <SubmitButton name="intent" value="item" size="sm" variant="outline"> | |
| 227 | 227 | * project's repository, whose address (`/<owner>/<repo>/issues/<n>`) | |
| 228 | 228 | * still leads to the work item. Null once `item` is set. | |
| 229 | 229 | */ | |
| 230 | − | legacy_issue: number | null; | |
| 230 | + | legacy_number: number | null; | |
| 231 | 231 | }; | |
| 232 | 232 | ||
| 233 | 233 | /** What every list query can narrow by. */ |
| 6 | 6 | ||
| 7 | 7 | test("an error group linked to an issue keeps its old number when issues become work items", () => { | |
| 8 | 8 | const db = new DatabaseSync(":memory:"); | |
| 9 | − | for (const step of MIGRATIONS.slice(0, -1)) db.exec(step); | |
| 9 | + | for (const step of MIGRATIONS.slice(0, -2)) db.exec(step); | |
| 10 | 10 | db.exec( | |
| 11 | 11 | `INSERT INTO error_groups (fingerprint, name, message, service, first_us, last_us, issue_number) | |
| 12 | 12 | VALUES ('fp_1', 'TypeError', 'boom', 'web', 1, 2, 7), ('fp_2', 'TypeError', 'bang', 'web', 1, 2, NULL)`, | |
| 13 | 13 | ); | |
| 14 | + | for (const step of MIGRATIONS.slice(-2)) db.exec(step); | |
| 15 | + | const rows = db.prepare("SELECT fingerprint, item, legacy_number FROM error_groups ORDER BY fingerprint").all(); | |
| 16 | + | assert.deepEqual( | |
| 17 | + | rows.map((row) => ({ ...row })), | |
| 18 | + | [ | |
| 19 | + | { fingerprint: "fp_1", item: null, legacy_number: 7 }, | |
| 20 | + | { fingerprint: "fp_2", item: null, legacy_number: null }, | |
| 21 | + | ], | |
| 22 | + | ); | |
| 23 | + | }); | |
| 24 | + | ||
| 25 | + | test("an error group's old number moves to legacy_number", () => { | |
| 26 | + | const db = new DatabaseSync(":memory:"); | |
| 27 | + | for (const step of MIGRATIONS.slice(0, -1)) db.exec(step); | |
| 28 | + | db.exec( | |
| 29 | + | `INSERT INTO error_groups (fingerprint, name, message, service, first_us, last_us, item, legacy_issue) | |
| 30 | + | VALUES ('fp_1', 'TypeError', 'boom', 'web', 1, 2, NULL, 7), ('fp_2', 'TypeError', 'bang', 'web', 1, 2, 3, NULL)`, | |
| 31 | + | ); | |
| 14 | 32 | db.exec(MIGRATIONS.at(-1)!); | |
| 15 | − | const rows = db.prepare("SELECT fingerprint, item, legacy_issue FROM error_groups ORDER BY fingerprint").all(); | |
| 33 | + | const rows = db.prepare("SELECT fingerprint, item, legacy_number FROM error_groups ORDER BY fingerprint").all(); | |
| 16 | 34 | assert.deepEqual( | |
| 17 | 35 | rows.map((row) => ({ ...row })), | |
| 18 | 36 | [ | |
| 19 | − | { fingerprint: "fp_1", item: null, legacy_issue: 7 }, | |
| 20 | − | { fingerprint: "fp_2", item: null, legacy_issue: null }, | |
| 37 | + | { fingerprint: "fp_1", item: null, legacy_number: 7 }, | |
| 38 | + | { fingerprint: "fp_2", item: 3, legacy_number: null }, | |
| 21 | 39 | ], | |
| 22 | 40 | ); | |
| 23 | 41 | }); |
| 206 | 206 | ALTER TABLE error_groups RENAME COLUMN issue_number TO legacy_issue; | |
| 207 | 207 | ALTER TABLE error_groups ADD COLUMN item INTEGER; | |
| 208 | 208 | `, | |
| 209 | + | // That old number is a work item number too, so its column says so. | |
| 210 | + | ` | |
| 211 | + | ALTER TABLE error_groups RENAME COLUMN legacy_issue TO legacy_number; | |
| 212 | + | `, | |
| 209 | 213 | ]; | |
| 210 | 214 | ||
| 211 | 215 | /** |
| 573 | 573 | this.sql.exec("DELETE FROM spans WHERE start_us < ?", keep); | |
| 574 | 574 | this.sql.exec("DELETE FROM metric_points WHERE time_us < ?", keep); | |
| 575 | 575 | this.sql.exec("DELETE FROM rollups WHERE minute < ?", Math.floor((now - ROLLUP_DAYS * DAY_US) / MINUTE_US)); | |
| 576 | − | this.sql.exec("DELETE FROM error_groups WHERE last_us < ? AND item IS NULL AND legacy_issue IS NULL", now - ROLLUP_DAYS * DAY_US); | |
| 576 | + | this.sql.exec("DELETE FROM error_groups WHERE last_us < ? AND item IS NULL AND legacy_number IS NULL", now - ROLLUP_DAYS * DAY_US); | |
| 577 | 577 | this.sql.exec("DELETE FROM usage_days WHERE day < ?", day(now - 40 * DAY_US)); | |
| 578 | 578 | // Over its size, the store sheds a day at a time from the oldest. | |
| 579 | 579 | for (let i = 0; i < 30 && this.state.storage.sql.databaseSize > MAX_BYTES; i++) { | |
| ⋯ | |||
| 879 | 879 | setError(fp: string, change: { state?: ErrorGroup["state"]; item?: number | null }): ErrorGroup | null { | |
| 880 | 880 | if (change.state && !["open", "resolved", "ignored"].includes(change.state)) throw new Error("State is open, resolved or ignored."); | |
| 881 | 881 | if (change.state) this.sql.exec("UPDATE error_groups SET state = ? WHERE fingerprint = ?", change.state, fp); | |
| 882 | − | if (change.item !== undefined) this.sql.exec("UPDATE error_groups SET item = ?, legacy_issue = NULL WHERE fingerprint = ?", change.item, fp); | |
| 882 | + | if (change.item !== undefined) this.sql.exec("UPDATE error_groups SET item = ?, legacy_number = NULL WHERE fingerprint = ?", change.item, fp); | |
| 883 | 883 | const row = this.rows("SELECT * FROM error_groups WHERE fingerprint = ?", fp)[0]; | |
| 884 | 884 | return row ? errorOf(row) : null; | |
| 885 | 885 | } | |
| ⋯ | |||
| 1129 | 1129 | last_trace_id: strOrNull(r.last_trace_id), | |
| 1130 | 1130 | state: String(r.state) as ErrorGroup["state"], | |
| 1131 | 1131 | item: numOrNull(r.item), | |
| 1132 | − | legacy_issue: numOrNull(r.legacy_issue), | |
| 1132 | + | legacy_number: numOrNull(r.legacy_number), | |
| 1133 | 1133 | }; | |
| 1134 | 1134 | } | |
| 1135 | 1135 | ||
Comments
Sign in to comment.