Repository navigation
Commit a4e2d5c
authored
🤖 perf: keep the config snapshot while it still matches the file (#5750)
## Summary
An edit no longer throws away a config snapshot that still matches
`config.json`. With a reader on every event-loop turn (the startup
tombstone heal sweep does this), one edit now parses `config.json` 2
times instead of 4. At 4,712 workspaces, that edit is 66 ms faster (205
ms to 138 ms, 95% CI -40.1% to -23.9%). In the startup scenario at 4,700
workspaces, the heal sweep now does 2 parses per save instead of 4.
Refs #5727
## Background
The edit's own fresh read (`readConfigOrDefault` inside
`enqueueConfigEditEffect`) cleared the shared snapshot, and the save
cleared it again after the rename. Each clear forced the next reader to
parse the same bytes again. So with a reader on every turn, one edit
cost 4 parses:
1. The edit's own read under the lock.
2. A reader during the save, because the edit's read had cleared the
snapshot.
3. A reader between the rename and the end of the save.
4. The first reader after the save, because the save cleared the
snapshot that parse 3 stored.
This change removes parses 2 and 4. The floor is 2: the edit's fresh
read, and one read that publishes the saved file.
## Implementation
All in `src/node/config/index.ts` (9 changed code lines plus comments):
1. `enqueueConfigEditEffect`: the transform reads with
`readConfigOrDefault({ keepSnapshot: true })`.
2. `readConfigOrDefault`: with `keepSnapshot`, it clears the snapshot as
before, then gives it back only after a successful, cacheable read. A
failed, missing or lenient read leaves it cleared. Otherwise the edit
gate's `loadConfigOrDefault` would hit the old snapshot, never retry the
read that clears a recorded load failure, and refuse every edit until a
restart.
3. `saveConfigEffect`: after the write, it keeps a snapshot only if the
snapshot's stat key equals the current stat key of `config.json`. That
snapshot came from a reader that parsed the real bytes on disk, whoever
wrote them. The cost is one `statSync` per save.
The bench file `src/node/config/configScale.bench.ts` adds live-shaped
synthetic rows (no real data). It is the file from the unmerged bench
branch, unchanged.
## Invariants and the tests that guard them
All tests are in `src/node/config/snapshot.test.ts`. The existing cases
are unchanged.
| Invariant | Test |
|---|---|
| With a reader on every event-loop turn, one edit reads `config.json`
exactly 2 times (main: 4) | `parses config.json twice per edit while a
reader runs on every event-loop turn` (new) |
| A reader during an edit gets the committed pre-edit snapshot, never
the transform's object | `serves the committed snapshot, never the
transform's object, to readers during an edit` (new) |
| Only a successful read gives the snapshot back: after a transient
`EIO` in the edit's read, the next edit succeeds without a file change
or restart | `accepts the next edit after the edit's own read fails once
on a warm snapshot` (new) |
| A failed save clears the snapshot, so readers never see an unsaved
in-memory change | `drops the snapshot when a save fails` (new) |
| A snapshot is served only for the file it was parsed from, and another
writer's rename between our rename and the save's end wins |
`invalidates on atomic external replacement, including same-size bytes
and mtime`, `isolates edits and reloads the saved snapshot once`, `sees
an external atomic replacement between our rename and save completion`
(existing, unchanged) |
| Corrupt and lenient files behave as before | `does not reuse a lenient
structurally invalid load for a strict read`, `does not cache
structurally invalid entries after an unrelated save`, `treats stat
failures as cache misses`, `does not reuse a cached snapshot after the
file disappears` (existing, unchanged) |
Mutation check, run locally against `snapshot.test.ts` (each mutant
fails only the expected test):
| Mutation of `index.ts` | Failing test |
|---|---|
| Both changes reverted (main) | reader-per-turn test (reads 4 times)
and committed-snapshot test |
| The transform does not keep the snapshot | reader-per-turn test (3
reads) and committed-snapshot test |
| The save always clears the snapshot | reader-per-turn test (3 reads) |
| The kept snapshot comes back even after a failed read | `EIO` recovery
test |
| No clear on a failed save | failed-save test (reader sees `"Unsaved"`)
|
| The transform's object is stored as the snapshot | committed-snapshot
test |
## Measurements
Host: shared Coder host (AMD EPYC 9454P), Node 22.19, `make
bench-compare ROUNDS=10` (interleaved base and head processes, paired
95% CI on per-round log ratio).
- Base `0c5e35d81638e85ed420c4b7c943d86ed502a2f7` (origin/main
merge-base), head `4fa387b2e78bf32f810d7c21bc1f488bd33ce265`.
### A/A noise floor: `make bench-compare BENCH=config BASE=HEAD
ROUNDS=10`
Both sides are the same commit (`712637f68e`, the product code of this
PR). Result: 0 `faster` or `slower` verdicts out of 42 rows.
```
runtime node | rounds 10 | base 712637f | head 712637f
loadavg start 18.98 30.03 35.74 | end 13.57 17.40 22.90
```
<details>
<summary>A/A table (42 rows)</summary>
```
benchmark base head delta 95% CI verdict
config-a28db80a: loadConfigOrDefault (fresh Config, 4500 workspaces) 11.82 ms 12.02 ms +1.4% [-1.6%, +4.6%] ~
config-a28db80a: loadConfigOrDefault (snapshot hit) 3.29 µs 3.27 µs -2.1% [-5.8%, +1.7%] ~
configScale-334200bf: loadConfigOrDefault, fresh Config (50) 510.54 µs 510.23 µs +13.1% [-14.4%, +49.4%] ~
configScale-334200bf: loadConfigOrDefault, fresh Config (500) 3.11 ms 3.13 ms +5.1% [-5.0%, +16.4%] ~
configScale-334200bf: loadConfigOrDefault, fresh Config (5000) 32.37 ms 32.28 ms -0.2% [-2.2%, +1.7%] ~
configScale-334200bf: loadConfigOrDefault, fresh Config (4712) 30.26 ms 30.70 ms +1.0% [-1.9%, +4.0%] ~
configScale-334200bf: loadConfigOrDefault, snapshot hit (50) 3.01 µs 3.02 µs +0.7% [-0.4%, +1.8%] ~
configScale-334200bf: loadConfigOrDefault, snapshot hit (500) 3.03 µs 3.06 µs +0.9% [-1.2%, +3.0%] ~
configScale-334200bf: loadConfigOrDefault, snapshot hit (5000) 3.03 µs 3.04 µs +0.4% [-1.2%, +2.0%] ~
configScale-334200bf: loadConfigOrDefault, snapshot hit (4712) 3.02 µs 3.04 µs +0.6% [-1.2%, +2.5%] ~
configScale-334200bf: findWorkspace(last id) (50) 3.12 µs 3.13 µs -0.7% [-2.5%, +1.1%] ~
configScale-334200bf: findWorkspace(last id) (500) 3.14 µs 3.12 µs -0.5% [-1.6%, +0.6%] ~
configScale-334200bf: findWorkspace(last id) (5000) 3.27 µs 3.30 µs +0.2% [-2.9%, +3.3%] ~
configScale-334200bf: findWorkspace(last id) (4712) 3.32 µs 3.33 µs +0.8% [-1.3%, +2.9%] ~
configScale-334200bf: getAllWorkspaceMetadata, full build (50) 1.36 ms 1.34 ms -2.5% [-6.9%, +2.1%] ~
configScale-334200bf: getAllWorkspaceMetadata, full build (500) 12.77 ms 12.67 ms -1.5% [-6.9%, +4.3%] ~
configScale-334200bf: getAllWorkspaceMetadata, full build (5000) 136.93 ms 134.83 ms -5.4% [-14.0%, +4.0%] ~
configScale-334200bf: getAllWorkspaceMetadata, full build (4712) 128.04 ms 126.63 ms -2.5% [-7.7%, +3.0%] ~
configScale-334200bf: getAllWorkspaceMetadata, last-known probes (50) 671.20 µs 670.10 µs +1.2% [-3.8%, +6.4%] ~
configScale-334200bf: getAllWorkspaceMetadata, last-known probes (500) 6.52 ms 6.65 ms +2.6% [-1.7%, +7.0%] ~
configScale-334200bf: getAllWorkspaceMetadata, last-known probes (5000) 69.80 ms 71.89 ms -6.0% [-23.8%, +16.0%] ~
configScale-334200bf: getAllWorkspaceMetadata, last-known probes (4712) 66.47 ms 68.29 ms +2.4% [-2.9%, +7.9%] ~
configScale-334200bf: getAllWorkspaceMetadata, registry memo (50) 3.54 µs 3.52 µs -1.9% [-5.3%, +1.5%] ~
configScale-334200bf: getAllWorkspaceMetadata, registry memo (500) 3.90 µs 3.84 µs -0.9% [-3.0%, +1.3%] ~
configScale-334200bf: getAllWorkspaceMetadata, registry memo (5000) 5.67 µs 5.52 µs -1.0% [-3.9%, +1.9%] ~
configScale-334200bf: getAllWorkspaceMetadata, registry memo (4712) 5.51 µs 5.49 µs +0.4% [-2.4%, +3.2%] ~
configScale-334200bf: getWorkspaceMetadataById(last id) (50) 78.58 µs 79.11 µs +0.9% [-3.2%, +5.3%] ~
configScale-334200bf: getWorkspaceMetadataById(last id) (500) 76.70 µs 74.88 µs -4.6% [-10.9%, +2.1%] ~
configScale-334200bf: getWorkspaceMetadataById(last id) (5000) 72.46 µs 73.24 µs -16.1% [-45.2%, +28.3%] ~
configScale-334200bf: getWorkspaceMetadataById(last id) (4712) 70.61 µs 72.19 µs -5.1% [-20.4%, +13.1%] ~
configScale-334200bf: editConfig, same-value edit (50) 5.47 ms 5.69 ms +11.5% [-2.0%, +26.9%] ~
configScale-334200bf: editConfig, same-value edit (500) 17.49 ms 17.66 ms +4.1% [-1.6%, +10.1%] ~
configScale-334200bf: editConfig, same-value edit (5000) 151.67 ms 153.64 ms +1.8% [-14.1%, +20.6%] ~
configScale-334200bf: editConfig, same-value edit (4712) 139.34 ms 140.59 ms +13.7% [-12.5%, +47.7%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (50) 5.63 ms 5.58 ms -0.6% [-6.1%, +5.3%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (500) 17.75 ms 17.35 ms -0.5% [-3.1%, +2.1%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (5000) 148.56 ms 145.67 ms -1.4% [-7.5%, +5.1%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (4712) 138.72 ms 138.13 ms -6.7% [-17.6%, +5.8%] ~
configScale-334200bf: editConfig, reader on every event-loop turn (50) 5.41 ms 5.61 ms +16.7% [-15.6%, +61.2%] ~
configScale-334200bf: editConfig, reader on every event-loop turn (500) 17.34 ms 17.41 ms +0.0% [-20.5%, +25.8%] ~
configScale-334200bf: editConfig, reader on every event-loop turn (5000) 151.02 ms 148.86 ms -7.3% [-26.1%, +16.2%] ~
configScale-334200bf: editConfig, reader on every event-loop turn (4712) 137.19 ms 139.04 ms -2.9% [-12.8%, +8.1%] ~
```
</details>
### Base vs head: `make bench-compare BENCH=config ROUNDS=10`
Result: 4 `faster` verdicts, all on the reader-per-turn row. 0 `slower`
verdicts. At the live-shaped size (4,712 workspaces) the reader-per-turn
edit takes 66 ms less (budget: at least 35 ms). Plain edits without a
concurrent reader do not change, as expected.
```
runtime node | rounds 10 | base 0c5e35d | head 4fa387b
loadavg start 14.26 17.36 22.80 | end 17.83 31.33 29.39
```
```
benchmark base head delta 95% CI verdict
configScale-334200bf: loadConfigOrDefault, fresh Config (4712) 31.70 ms 31.62 ms -8.9% [-25.5%, +11.5%] ~
configScale-334200bf: editConfig, same-value edit (4712) 139.47 ms 139.37 ms -0.6% [-6.8%, +5.9%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (4712) 142.68 ms 137.67 ms -13.0% [-24.7%, +0.5%] ~
configScale-334200bf: editConfig, reader on every event-loop turn (50) 7.02 ms 5.63 ms -23.0% [-34.2%, -9.9%] faster
configScale-334200bf: editConfig, reader on every event-loop turn (500) 23.62 ms 17.14 ms -39.0% [-54.9%, -17.6%] faster
configScale-334200bf: editConfig, reader on every event-loop turn (5000) 220.55 ms 149.48 ms -34.4% [-38.5%, -30.0%] faster
configScale-334200bf: editConfig, reader on every event-loop turn (4712) 204.65 ms 138.40 ms -32.5% [-40.1%, -23.9%] faster
```
<details>
<summary>Full base-vs-head table (42 rows)</summary>
```
benchmark base head delta 95% CI verdict
config-a28db80a: loadConfigOrDefault (fresh Config, 4500 workspaces) 12.59 ms 12.90 ms +0.9% [-2.9%, +5.0%] ~
config-a28db80a: loadConfigOrDefault (snapshot hit) 3.37 µs 3.36 µs -0.5% [-2.2%, +1.4%] ~
configScale-334200bf: loadConfigOrDefault, fresh Config (50) 532.11 µs 536.67 µs +0.7% [-2.7%, +4.2%] ~
configScale-334200bf: loadConfigOrDefault, fresh Config (500) 3.20 ms 3.14 ms -0.4% [-4.8%, +4.2%] ~
configScale-334200bf: loadConfigOrDefault, fresh Config (5000) 33.11 ms 31.97 ms -2.5% [-6.2%, +1.3%] ~
configScale-334200bf: loadConfigOrDefault, fresh Config (4712) 31.70 ms 31.62 ms -8.9% [-25.5%, +11.5%] ~
configScale-334200bf: loadConfigOrDefault, snapshot hit (50) 3.11 µs 3.09 µs -0.1% [-2.4%, +2.2%] ~
configScale-334200bf: loadConfigOrDefault, snapshot hit (500) 3.11 µs 3.10 µs -0.7% [-3.9%, +2.5%] ~
configScale-334200bf: loadConfigOrDefault, snapshot hit (5000) 3.11 µs 3.04 µs -1.6% [-4.5%, +1.3%] ~
configScale-334200bf: loadConfigOrDefault, snapshot hit (4712) 3.11 µs 3.09 µs -3.2% [-7.1%, +0.9%] ~
configScale-334200bf: findWorkspace(last id) (50) 3.22 µs 3.19 µs -1.5% [-5.4%, +2.5%] ~
configScale-334200bf: findWorkspace(last id) (500) 3.21 µs 3.22 µs +0.9% [-3.0%, +5.0%] ~
configScale-334200bf: findWorkspace(last id) (5000) 3.33 µs 3.38 µs -7.0% [-22.7%, +11.8%] ~
configScale-334200bf: findWorkspace(last id) (4712) 3.41 µs 3.36 µs +4.7% [-10.0%, +21.9%] ~
configScale-334200bf: getAllWorkspaceMetadata, full build (50) 1.37 ms 1.32 ms +17.7% [-26.0%, +87.3%] ~
configScale-334200bf: getAllWorkspaceMetadata, full build (500) 13.17 ms 12.28 ms -8.7% [-32.9%, +24.2%] ~
configScale-334200bf: getAllWorkspaceMetadata, full build (5000) 138.94 ms 135.98 ms +1.1% [-9.6%, +13.0%] ~
configScale-334200bf: getAllWorkspaceMetadata, full build (4712) 128.80 ms 130.51 ms -2.9% [-10.9%, +5.8%] ~
configScale-334200bf: getAllWorkspaceMetadata, last-known probes (50) 663.84 µs 659.88 µs -0.5% [-3.9%, +3.2%] ~
configScale-334200bf: getAllWorkspaceMetadata, last-known probes (500) 6.59 ms 6.56 ms -1.4% [-4.4%, +1.6%] ~
configScale-334200bf: getAllWorkspaceMetadata, last-known probes (5000) 69.84 ms 72.30 ms -0.3% [-4.6%, +4.3%] ~
configScale-334200bf: getAllWorkspaceMetadata, last-known probes (4712) 68.58 ms 67.88 ms -1.2% [-5.5%, +3.4%] ~
configScale-334200bf: getAllWorkspaceMetadata, registry memo (50) 3.61 µs 3.60 µs -1.5% [-6.1%, +3.3%] ~
configScale-334200bf: getAllWorkspaceMetadata, registry memo (500) 4.03 µs 3.87 µs -3.3% [-6.9%, +0.5%] ~
configScale-334200bf: getAllWorkspaceMetadata, registry memo (5000) 6.12 µs 5.85 µs -9.0% [-28.4%, +15.7%] ~
configScale-334200bf: getAllWorkspaceMetadata, registry memo (4712) 6.00 µs 5.73 µs -8.9% [-26.0%, +12.3%] ~
configScale-334200bf: getWorkspaceMetadataById(last id) (50) 80.81 µs 78.16 µs -5.7% [-16.9%, +6.9%] ~
configScale-334200bf: getWorkspaceMetadataById(last id) (500) 77.07 µs 77.58 µs -5.8% [-18.4%, +8.7%] ~
configScale-334200bf: getWorkspaceMetadataById(last id) (5000) 75.45 µs 74.15 µs -5.8% [-13.1%, +2.0%] ~
configScale-334200bf: getWorkspaceMetadataById(last id) (4712) 76.16 µs 73.88 µs -4.5% [-11.0%, +2.3%] ~
configScale-334200bf: editConfig, same-value edit (50) 6.17 ms 5.76 ms -17.9% [-43.1%, +18.5%] ~
configScale-334200bf: editConfig, same-value edit (500) 18.02 ms 17.55 ms -19.0% [-40.1%, +9.5%] ~
configScale-334200bf: editConfig, same-value edit (5000) 151.06 ms 151.14 ms -2.6% [-14.7%, +11.2%] ~
configScale-334200bf: editConfig, same-value edit (4712) 139.47 ms 139.37 ms -0.6% [-6.8%, +5.9%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (50) 5.86 ms 5.75 ms -9.0% [-21.2%, +5.1%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (500) 17.65 ms 17.19 ms -2.6% [-7.3%, +2.4%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (5000) 149.01 ms 147.88 ms -2.9% [-7.2%, +1.6%] ~
configScale-334200bf: editConfig, then loadConfigOrDefault (4712) 142.68 ms 137.67 ms -13.0% [-24.7%, +0.5%] ~
configScale-334200bf: editConfig, reader on every event-loop turn (50) 7.02 ms 5.63 ms -23.0% [-34.2%, -9.9%] faster
configScale-334200bf: editConfig, reader on every event-loop turn (500) 23.62 ms 17.14 ms -39.0% [-54.9%, -17.6%] faster
configScale-334200bf: editConfig, reader on every event-loop turn (5000) 220.55 ms 149.48 ms -34.4% [-38.5%, -30.0%] faster
configScale-334200bf: editConfig, reader on every event-loop turn (4712) 204.65 ms 138.40 ms -32.5% [-40.1%, -23.9%] faster
```
</details>
### Parses per save in the startup heal-sweep scenario (counting
harness, 4,700 workspaces)
Setup: a synthetic fixture from
`scripts/perf/workspace-scale/generate-fixture.ts` (4,700 workspaces, 19
projects, 57% archived, realistic profile, 8.5 MB `config.json`). Before
start, the driver writes 2,734 one-day-old tombstones for unregistered
ids, 2 one-day-old tombstones for registered ids, and 1 fresh tombstone
for a registered id. It starts `node --require counter.cjs
dist/cli/index.js server --no-auth` with an isolated `XUM_ROOT` and
`XUM_MOCK_AI=1`. As soon as the port opens, it sends 20
`workspace.updateTitle` calls. The preload counts `readConfigOrDefault`
and `saveConfig` calls while
`healRemovalTombstonesForRegisteredWorkspaces` runs. Counts are exact.
Timings come from single runs on a shared host (load average 11-32) and
are noisy. Run 1 used a build of `712637f68e`; runs 2, 3 and the control
used the head build. Both have the same product code (the later commit
changes only a test).
| Run | Build | Parses during sweep | Saves during sweep | Parses per
save | Sweep ms | Start to `housekeeping settled` (s) |
|---|---|---|---|---|---|---|
| 1 | base | 85 | 21 | 4.05 | 17619 | 31.49 |
| 1 | head | 43 | 21 | 2.05 | 13499 | 26.54 |
| 2 | base | 85 | 21 | 4.05 | 17271 | 30.74 |
| 2 | head | 43 | 21 | 2.05 | 13872 | 27.71 |
| 3 | base | 85 | 21 | 4.05 | 17458 | 31.53 |
| 3 | head | 43 | 21 | 2.05 | 15617 | 29.87 |
| control, 0 edits | base | 5 | 1 | - | 5475 | 16.61 |
| control, 0 edits | head | 3 | 1 | - | 5335 | 17.11 |
- Per save, after taking away the one cold read that the 0-edit control
shows: base (85 - 1) / 21 = 4.0, head (43 - 1) / 21 = 2.0. The plan
projected 4.4 on base from live captures; this harness measures 4.0.
- On both builds, the sweep healed exactly the 2 old registered
tombstones ("Healed a removal tombstone" logged 2 times), and the other
2,735 tombstones stayed.
## Dogfood (head `4fa387b2e78bf32f810d7c21bc1f488bd33ce265`, synthetic
4,700-workspace root, no real data)
The same driver ran a cross-process check after each startup run, on
base and head:
1. Rename a workspace through `workspace.updateTitle`:
`workspace.getInfo` returns `API title`.
2. A script writes a changed title into `config.json` through a temp
file and a rename, as another backend does: `workspace.getInfo` returns
`External atomic title`.
3. A script edits `config.json` in place with a different length:
`workspace.getInfo` returns `In-place edit, longer title`.
All 3 steps passed on base and head in all 3 runs. The harness source
and raw results are in a PR comment.
## Risks
Low. The change only stops clearing a snapshot whose stat key still
matches the file, and every snapshot read compares keys first, as
before. The remaining risk is the existing stat-key limit (an in-place
rewrite that keeps inode, mtime and size), which this PR does not
change. A save still keeps no snapshot of the file it replaced, and a
failed save still clears it.
---
_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high`_
<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
---------
Signed-off-by: Thomas Kosiewski <tk@coder.com>1 parent 5e011d0 commit a4e2d5c
3 files changed
Lines changed: 388 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1636 | 1636 | | |
1637 | 1637 | | |
1638 | 1638 | | |
1639 | | - | |
| 1639 | + | |
1640 | 1640 | | |
1641 | 1641 | | |
1642 | 1642 | | |
| |||
1679 | 1679 | | |
1680 | 1680 | | |
1681 | 1681 | | |
1682 | | - | |
| 1682 | + | |
| 1683 | + | |
| 1684 | + | |
| 1685 | + | |
1683 | 1686 | | |
1684 | 1687 | | |
1685 | 1688 | | |
1686 | 1689 | | |
| 1690 | + | |
| 1691 | + | |
| 1692 | + | |
| 1693 | + | |
1687 | 1694 | | |
1688 | 1695 | | |
1689 | 1696 | | |
| |||
1718 | 1725 | | |
1719 | 1726 | | |
1720 | 1727 | | |
| 1728 | + | |
| 1729 | + | |
| 1730 | + | |
| 1731 | + | |
| 1732 | + | |
1721 | 1733 | | |
1722 | 1734 | | |
1723 | 1735 | | |
| |||
2647 | 2659 | | |
2648 | 2660 | | |
2649 | 2661 | | |
2650 | | - | |
| 2662 | + | |
| 2663 | + | |
| 2664 | + | |
| 2665 | + | |
2651 | 2666 | | |
2652 | 2667 | | |
2653 | 2668 | | |
| |||
3169 | 3184 | | |
3170 | 3185 | | |
3171 | 3186 | | |
3172 | | - | |
| 3187 | + | |
3173 | 3188 | | |
3174 | 3189 | | |
3175 | 3190 | | |
| |||
0 commit comments