docs(audit): AuditLogEntries index-usage measurement, window too short, no drop yet (#70)
Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
# AuditLogEntries index usage: first measurement (2026-11-13)
|
||||
|
||||
Tracks: Gitea **ScadaBridge#70** (deferred-work register row 135).
|
||||
Question: are the single-column `IX_AuditLogEntries_Timestamp`, `_User` and `_EntityType` indexes
|
||||
dead weight now that `ReviewRemediationIndexes` added `(EntityType, Timestamp DESC)`,
|
||||
`(User, Timestamp DESC)` and `(Timestamp DESC, Id DESC)`?
|
||||
|
||||
**Decision: no migration yet. The measurement window is too short on both tiers, and no index on the
|
||||
table was read at all, the new composites included. Re-measure on or after 2026-11-27.**
|
||||
|
||||
## Method
|
||||
|
||||
Read-only `SELECT`s over a direct SQL login (`sqlcmd -C`), no remote exec, run 2026-11-13 ~02:10 server time:
|
||||
`sys.dm_db_index_usage_stats` LEFT JOIN `sys.indexes` for `dbo.AuditLogEntries`, plus
|
||||
`sys.dm_db_index_operational_stats`, `sys.partitions`/`sys.allocation_units` for rows and size, and
|
||||
`sys.dm_os_sys_info.sqlserver_start_time` (the DMVs reset when SQL Server restarts).
|
||||
|
||||
## Measurement window
|
||||
|
||||
| Tier | SQL host / DB | `sqlserver_start_time` | Window | Latest migration |
|
||||
|---|---|---|---|---|
|
||||
| DEV | `wonder-app-vd03` / `ScadaBridge` | 2026-11-10 06:42 | **~3 days** | `20261110120000_AuditMaintenanceGrants` (64) |
|
||||
| QA | `wonder-sql-vq03` / `ScadaBridge` | 2026-11-03 07:09 | **~10 days** | `20261022133907_ReviewRemediationIndexes` (58) |
|
||||
|
||||
Both are under the two weeks needed. The migration went in on 2026-10-22, but SQL restarts since then
|
||||
have reset the counters.
|
||||
|
||||
## Results
|
||||
|
||||
Table size: DEV 471 rows (Timestamp 2026-05-29 to 2026-10-28), QA 567 rows (to 2026-11-06).
|
||||
|
||||
| Index | Keys | DEV seeks/scans/lookups | DEV updates | QA seeks/scans/lookups | QA updates | Size |
|
||||
|---|---|---|---|---|---|---|
|
||||
| PK_AuditLogEntries (clustered) | Id | 0/0/0 | 0 | 0/0/0 | 10 | 1.4 MB / 4.3 MB |
|
||||
| IX_AuditLogEntries_Action | Action | 0/0/0 | 0 | 0/0/0 | 10 | 72 KB |
|
||||
| IX_AuditLogEntries_EntityId | EntityId | 0/0/0 | 0 | 0/0/0 | 10 | 72 KB |
|
||||
| **IX_AuditLogEntries_EntityType** | EntityType | 0/0/0 | 0 | 0/0/0 | 10 | 72 KB |
|
||||
| **IX_AuditLogEntries_Timestamp** | Timestamp | 0/0/0 | 0 | 0/0/0 | 10 | 72 KB |
|
||||
| **IX_AuditLogEntries_User** | User | 0/0/0 | 0 | 0/0/0 | 10 | 72 KB |
|
||||
| IX_AuditLogEntries_BundleImportId | BundleImportId | 0/0/0 | 0 | 0/0/0 | 10 | 72 KB |
|
||||
| IX_AuditLogEntries_EntityType_Timestamp | EntityType, Timestamp DESC | 0/0/0 | 0 | 0/0/0 | 10 | 200 KB |
|
||||
| IX_AuditLogEntries_Timestamp_Id | Timestamp DESC, Id DESC | 0/0/0 | 0 | 0/0/0 | 10 | 200 KB |
|
||||
| IX_AuditLogEntries_User_Timestamp | User, Timestamp DESC | 0/0/0 | 0 | 0/0/0 | 10 | 200 KB |
|
||||
| IX_AuditLogEntries_EntityName (DEV only, migration 59+) | EntityName | 0/0/0 | 0 | — | — | 200 KB |
|
||||
|
||||
`sys.dm_db_index_operational_stats` agrees: zero range scans and singleton lookups on every index on both
|
||||
tiers, with 10 leaf inserts per index on QA and none on DEV.
|
||||
|
||||
## Reading
|
||||
|
||||
- The data does not settle the question. Every index reads zero, including the composites that are
|
||||
supposed to replace the single-column ones. Nobody used the config-audit page or the CLI query on
|
||||
either tier during the window, and the purge job did not touch the table. A zero on an old index
|
||||
only means something if the composite that replaces it shows reads.
|
||||
- On write cost: the table is tiny (about 500 rows) and takes about 1 write a day on QA. The three
|
||||
single-column indexes cost about 216 KB and 3 extra index writes per insert. Nothing is urgent.
|
||||
- Static coverage (from code, not from the measurement): each single-column index is a left prefix of
|
||||
a composite. The filters in `CentralUiRepository` (`User ==`, `EntityType ==`, `Timestamp` range,
|
||||
ordered by `Timestamp DESC, Id DESC`) and the purge in `ConfigAuditPurgeService`
|
||||
(`Timestamp < cutoff ORDER BY Timestamp`, which can use a backward scan of `IX_AuditLogEntries_Timestamp_Id`)
|
||||
are all served by the composites. So the case for dropping the three indexes is sound in principle.
|
||||
The register's rule is "measured, not assumed", and this run measured nothing useful.
|
||||
|
||||
## Next step
|
||||
|
||||
Re-run the same query on or after **2026-11-27**, which is two weeks after both current
|
||||
`sqlserver_start_time`s. Check `sqlserver_start_time` again first. If the composites show reads and
|
||||
the single-column indexes still show zero, drop `IX_AuditLogEntries_Timestamp`, `_User` and
|
||||
`_EntityType` in one migration whose `Down()` recreates them. Leave `Action`, `EntityId`,
|
||||
`BundleImportId` and `EntityName` alone, because no composite covers them. If every index still reads
|
||||
zero, the owner has to decide whether to drop on static grounds or to exercise the config-audit page
|
||||
on QA first and then measure.
|
||||
Reference in New Issue
Block a user