historian(sqlserver): Part 11 modified values (ReadModified + audit of HistoryUpdate) #668
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
OPC UA HistoryUpdate on the SQL Server backend overwrites or deletes rows in place. Part 11 "modified values" are not kept: there is no audit history of replaced or deleted values, and
ReadModified(HistoryRead withIsReadModified=true) is unsupported.Scope: a
hist.SampleModifiedtable recording the prior value, the user/principal, the update type and the time. HistoryUpdate writes to it in the same transaction, andReadModifiedserves it.Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
Implemented in
062086f9, review fix-ups57deaeba(both merged to master).002_sample_modified.sql,RequiredVersion = 2; v1 upgraded in place under the bootstrap applock). Every HistoryUpdate writes its trail row viaOUTPUT … INTO hist.SampleModifiedin the same statement as the change — a failed update leaves no trail row (atomicity test forces the trail insert to fail). Retention (#667) purgesSampleModifiedand never writes trail rows.{prefix}sample_modified(same TTL), written before the change; a crash between the two leaves an orphan audit row (documented; a non-crash failure now logs a Warning naming tag + range).HistoryUpdateContext(Principal, ApiKeyId)threaded into the updater — the recorded user is the session's API-key principal (oranonymous); only the public key id, never secret material.IsReadModified=trueserved byReadModifiedAsync→HistoryModifiedData+ModificationInfoper value, with continuation points (cursor(SourceTime, ModifiedId), bound to node + read kind), reverse and open-ended reads. Authorized by the existing HistoryRead gate. Gateway/Null ⇒BadHistoryOperationUnsupported.historyread --modified.⚠️ Upgrade note: with
AutoCreateSchema=false, run002_sample_modified.sql(SqlServer) / createsample_modified(QuestDB) before upgrading, or every historian call fails. Upgrade both pair nodes together (a not-yet-upgraded peer writes HistoryUpdates without trail rows).Live gate (docker-dev, gate 3) — PASS on SqlServer and QuestDB: fresh schema v2 + in-place v1→v2 upgrade (seeded v1 sample survived); insert/replace/delete-at-time as an API key then
--modifiedreturned exactly the 4 expected rows with correct priors and the key's principal; 18-entry paging forward and reverse at cap 5 (5/5/5/3, ties in modification order, no dup/skip) on both centrals; ACL on: a key without HistoryRead and anonymous getBadUserAccessDenied; Gateway ⇒BadHistoryOperationUnsupported; plain raw read shows the replaced value.Follow-ups filed (QuestDB TTL vs far-future timestamps, v2-required-for-all-traffic posture, cross-node QuestDB ordering/priors, IsDeleteModified).