perf(historian): page SQL Server HistoryUpdate DeleteRaw (lmxopcua#746)
DeleteRaw deleted an unbounded range in one statement and transaction with OUTPUT INTO the modification trail. Over years of a 1 Hz tag that is tens of millions of rows deleted and audited at once: lock escalation on Sample (blocking the live writer), transaction-log growth, and a 30 s CommandTimeout that rolled the whole thing back. It now runs DELETE TOP (4000) batches, each its own transaction with its trail rows in the same statement (every deleted row is still audited), cancellable between batches. A failure part-way leaves the committed batches deleted and reports the failure; re-issuing completes it (documented, same as QuestDB's paged tombstoning). The existing atomicity tests still hold per batch. Rebased from the unpushed remed/lane-H2 branch (originally authored 2026-10-22 in session_01Ub9hGmYLQvn9XETzw7CMsT) onto current master. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -2222,6 +2222,13 @@ server to answer `BadTypeMismatch` for a value of the wrong type; this server de
|
||||
**`DeleteRawModifiedDetails` needs both bounds:** a `StartTime` or `EndTime` of `DateTime.MinValue` (the wire's "unspecified") is
|
||||
`BadInvalidArgument` for that node and never reaches the backend.
|
||||
|
||||
**DeleteRaw is paged on both SQL backends.** `SqlServer` deletes in `DELETE TOP (4000)` batches, each its own transaction with its trail
|
||||
rows (`OUTPUT … INTO` on the same statement, so every deleted row is still audited); it used to delete — and audit — the whole range in
|
||||
one statement and transaction, which over years of a 1 Hz tag meant lock escalation on `hist.Sample` (blocking the live writer),
|
||||
transaction-log growth, and a `CommandTimeout` that rolled everything back. QuestDB tombstones 1,000 rows per page. ⚠️ On both, a
|
||||
DeleteRaw that fails part-way (timeout, cancellation, outage) has already deleted the batches before the failure — each with its trail
|
||||
rows — and reports the failure; re-issuing the same DeleteRaw completes it.
|
||||
|
||||
**Duplicate timestamps inside one request** (compared at 100 ns): *Insert* — the first occurrence is applied, later ones are `BadEntryExists`;
|
||||
*Replace* / *Update* — the last one wins and every earlier occurrence reports the winner's code; *DeleteAtTime* — the first reports the real
|
||||
outcome, later ones `BadNoEntryExists`. **Unknown tag:** Insert / Update create the tag row; Replace and the deletes report `BadNoEntryExists`
|
||||
|
||||
Reference in New Issue
Block a user