SQL Server change tracking
No edit summary
(Adding page to Category:TOC because it contains a TOC.)
 
(23 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Tracking of changes is available from Sql Server 2008 and in Azure SQL. It's available in the '''Express''' editions, so you can use it in the build in Turnkey database.
=== Background ===
The MDrivenServer has built-in change tracking. It is implemented as a "dirty list" of objects that all clients can ask for. When the client gets this list, it marks all matched objects in memory as "dirty" and discards them. If any of these objects are present in a user interface (that is currently shown), these objects will be immediately reloaded from the MDrivenServer/ SQL Server.  


To turn change tracking On, do like this
MDrivenServer's dirty list is not part of the SQL Server and the MDrivenServer won't find out if some other client or service changes the underlying tables directly in the SQL Server.
ALTER DATABASE AdventureWorks2012 
 
SET CHANGE_TRACKING = ON 
The need for external dirty list handling (aka. change tracking) is often needed when there's legacy system reading and writing to the same SQL database. Because of this, it's not a good idea to have it on all tables in the database. You probably only want change tracking on the tables that can be affected by the non-MDriven system.
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)
 
== MDriven Implementation ==
 
==== Support for Change Tracking in SQL Server ====
Tracking of changes is available from SQL Server 2008 and in Azure SQL. It's available in the '''Express''' editions, so you can use it in the built-in Turnkey database.


====== More reading ======
==== Background About This on MS Site ====
https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-tracking-sql-server
https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-tracking-sql-server


====== See also when implementing ======
Article with details: https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/work-with-change-tracking-sql-server
Article with a lot of details: https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/work-with-change-tracking-sql-server


Setting ''Context Information'' provides a way to know that the MDrivenServer is the source of the change
==== How We Propose You Use the MDriven Features to Do Change Tracking ====
We want to put as little extra load as possible on the SQL Server. We, therefore, use different jobs to detect that '''''something changed''''' and then '''''what has changed.'''''
* '''Something changed''': The stored procedure ''CHANGE_TRACKING_CURRENT_VERSION()'' can be used to know if ANY change has occurred. We compare this version value with the value when we last processed change information.


Maybe '''last_synchronization_version''' can be used to know if ANY change has occured
* '''What has changed:''' If there's a change, we iterate '''change information''' for all tables we're interested in.
-- Check all tables with change tracking enabled 
Both of these methods use [[OCLOperators sqlpassthroughobjects|sqlpassthroughobjects]] to directly use SQL to return information.
IF EXISTS (
    SELECT COUNT(*) FROM sys.change_tracking_tables
    WHERE min_valid_version > @last_synchronization_version )
  BEGIN
    -- Handle invalid version & do not enumerate changes
    -- Client must be reinitialized
  END


Implementation notes
It then uses [[OCLOperators SuspectExternalUpdateInvalidate|SuspectExternalUpdateInvalidate]] to "invalidate", make dirty, the changes objects.


Skapade rader, ändrade rader
==== Support With Other Database Servers ====
The MDriven implementation is not dependent on HOW you find out what has changed. As long as you can find the primary key of changed rows, you can use this functionality. 


DBChange.ChangeKind.MembersChanged med MemberIds=-1  (hela raden invalideras)
=== Applying Change Tracking ===


Borttagna rader
==== Preparing Your SQL-server ====
The suggested pattern to allow for MDriven Applications to effectively discover changes to data made by others is this.
'''''Turn on change tracking on the DB and on the tables you want to discover foreign changes to.'''''


DBChange.ChangeKind.ObjectDeleted
To turn change tracking '''on''', do this:
 
ALTER DATABASE <database name>
Om jag stoppar in ChangeTrack info i samma kö som DBChange så måste jag översätta ChangeTrackId till VersionId
SET CHANGE_TRACKING = ON 
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)
Turn on change tracking for each table you want to track:
ALTER TABLE <nowiki><table name></nowiki>
ENABLE CHANGE_TRACKING
You do NOT need column track, we track whole rows/objects.


Det skulle bara vara 1(MDrivenServer) som frågar db changetrack(innan den svarar på Changes från klient) - minns vad som sagts med skapade DBChange rader
==== Model Additions ====
'''''Add attributes in SysSingleton'''''
* CurrentVersionNumberFromChangeTracking: Int64?


Klienten är omedveten - frågar bara efter DBChange
* LastHandledVersionNumberFromChangeTracking: Int64?


Kruxet är att "vanliga" ändringar kommer även generera changetrack som generar nya DBChange
==== Viewmodel Additions ====
Read more on [[Serverside actions]].


Det gör att vi får dubbla DBChange - vilket kommer leda till att alla kliener som sparar nått kommer vilja läsa om det
===== Create an MDrivenServer Server-side Job That Asks the SQL Server for the Current Change Track Version =====
* Create a ViewModel '''SS_GetCurrentTrackingVersion''' rooted in SysSingleton
* Add the '''CurrentVersionNumberFromChangeTracking''' attribute to the ViewModel.
* Add an action called '''GetCurrentVersion''' with this EAL.
self.CurrentVersionNumberFromChangeTracking := SysSingleton.sqlpassthrough(
'-- Get the current tracking version number
  DECLARE @CurrentVersionNumberFromChangeTracking bigint;
  SET @CurrentVersionNumberFromChangeTracking = CHANGE_TRACKING_CURRENT_VERSION();
  select @CurrentVersionNumberFromChangeTracking', Int64 )->first.Part1
Add criteria for serverside execution to the ViewModel with a PS OCL like this:
SysSingleton.allInstances
===== Create an MDrivenServer Server-side Job That Invalidates Changed Objects =====
* Create a ViewModel '''SS_InvalidateExternalChanges''' rooted in SysSingleton


(vilket i princip är rätt för att fixa fallet med en trigger som ändrar ett annat fält på samma rad som sidoeffekt)
* Add the '''CurrentVersionNumberFromChangeTracking''' and '''LastHandledVersionNumberFromChangeTracking''' attributes to the ViewModel


MEN om insert/update/Delete skickar WITH CHANGE_TRACKING_CONTEXT ("MDrivenServer") så kan vi ignorera våra egna ändringar
* Add variable vHandleToVersionNumber as Int64


Då borde vi även kunna fylla ett datablock med hela radensdata så att det skulle funka med synk
* Add an action called '''Invalidate''' with this EAL.
-- Move to variable avoiding change of version during execution
vHandleToVersionNumber := vCurrent_SS_InvalidateExternalChanges.CurrentVersionNumberFromChangeTracking;
-- Invalidate changed objects
'''selfVM.SuspectExternalUpdateInvalidate(<classname>.sqlpassthroughobjects( 'SELECT <primarykeyattributename> FROM CHANGETABLE (CHANGES <tablename>, ' + LastHandledVersionNumberFromChangeTracking.toString + ') AS C') );'''
'''selfVM.SuspectExte'''....
'''selfVM.SuspectExte'''....
'''selfVM.SuspectExte'''....
-- Update last handled change number
self.LastHandledVersionNumberFromChangeTracking := vHandleToVersionNumber
The '''selfVM.SuspectExternalUpdateInvalidate.....''' is the one you need to adapt to the tables you are tracking.


ALTER DATABASE Kubik  SET CHANGE_TRACKING = ON 
One row for each of the classes/tables you're tracking.
  (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)
* Add criteria for serverside execution to the ViewModel with a PS OCL like this
  SysSingleton.allInstances->select(ss|ss.CurrentVersionNumberFromChangeTracking <> ss.LastHandledVersionNumberFromChangeTracking)
ALTER TABLE Aktualitet ENABLE CHANGE_TRACKING  WITH (TRACK_COLUMNS_UPDATED = OFF)
This means that the job isn't even invoked if there's nothing to do.


Måste slå på ChangeTracking per tabell
=== Recommended Interval ===
DECLARE @next_baseline bigint; 
Interval has to be adjusted depending on the need and load on the server.  
SET @next_baseline = CHANGE_TRACKING_CURRENT_VERSION();
select @next_baseline DECLARE @context varbinary(14);
set @context=CAST('MDrivenServer' AS VARBINARY);
WITH CHANGE_TRACKING_CONTEXT (@context)
update Aktualitet set namn='Arbetar nu4' where AktualitetID=1
DECLARE @last_sync_version bigint; 
SET @last_sync_version = 1; 
SELECT *,CAST(Sys_Change_Context AS VARCHAR(10)) as context FROM CHANGETABLE (CHANGES Aktualitet, @last_sync_version) AS C;
Krux med att CHangeTracking bara har sista. Om främling skriver och vi skriver efter så kanske vi missar främlling.


Om vi skulle läsa om hela raden alltid vid upptäckt changetrack så får vi dublett om det var vi som skrev - inte hela värden. Det kanske blir trögt att inavlidera allt på ett objekt - men ser ingen annan råd då vi saknar precision
It can be quite low because it's a very fast operation - for example, 5 seconds, or what suits your need of discovery.  


Men om vi alltid ska läsa om alla ändringar så behöver vi inte sätta kontext för våra skriv
The detection takes about 10 ms to run and gets changes for 40 tables (without changes) around 100 ms.


Så förslaget är:
=== Generating OCL and SQL for Change Tracked Classes ===
You can use the model debugger to make it easier to keep your change-tracking OCL and SQL in sync with your model. '''Note!''' This OCL is for a database with reverse-engineered tables, having non-standard table names and primary keys.


En idé är att låta utvecklaren välja själv i databasen vilka tabeller som ska ha change tracking.
===== Extend Your Model =====
If you add a tagged value to your classes, for example, called '''ChangeTracking.''' See [[TaggedValues]] for more information.
[[File:ChangeTracking tagged value.png|none|thumb|419x419px|ChangeTracking tagged value with icon]]
[[File:Tagged value editor for ChangeTracking.png|none|thumb|138x138px|Tagged value editor]]


Sen skapa en en operator som kan användas från MDrivenServer-Serverside-jobs typ:
===== SQL for Turning Change Tracking On =====
Using this tag called '''ChangeTracking''', you can use this OCL to generate SQL for turning change tracking '''on''' for each table.
Class.allInstances->select(s | s.TaggedValue->select(tv | tv.Tag.toUpper='CHANGETRACKING')->notEmpty)->collect(c |
  String.format('ALTER TABLE {2} ENABLE CHANGE_TRACKING  -- For class {0}',
    c.Name,
    c.TaggedValue->select(tv | tv.Tag='Eco.PrimaryKey')->first.Value,
    c.TaggedValue->select(tv | tv.Tag='Eco.TableName')->first.Value
  )
)->orderBy(s|s)
This will create a list of rows like this:
ALTER TABLE [CV] ENABLE CHANGE_TRACKING  -- For class CV
Run this SQL on your SQL Server database.


selfVM.ServerSide_CheckChangeTracking(Person)
===== OCL for Invalidating Objects =====
Class.allInstances->select(s | s.TaggedValue->select(tv | tv.Tag.toUpper='CHANGETRACKING')->notEmpty)->collect(c |
  String.format('selfVM.SuspectExternalUpdateInvalidate({0}.sqlpassthroughobjects( \'SELECT {1} FROM CHANGETABLE (CHANGES {2}, \' + LastHandledVersionNumberFromChangeTracking.toString + \') AS C\') );',
    c.Name,
    c.TaggedValue->select(tv | tv.Tag='Eco.PrimaryKey')->first.Value,
    c.TaggedValue->select(tv | tv.Tag='Eco.TableName')->first.Value
  )
)->orderBy(s|s)
This will create a list of rows like this:
selfVM.SuspectExternalUpdateInvalidate(CV.sqlpassthroughobjects( 'SELECT [CV_ID] FROM CHANGETABLE (CHANGES [CV], ' + LastHandledVersionNumberFromChangeTracking.toString + ') AS C') );
Insert this OCL in the '''SS_InvalidateExternalChanges''' action.


Vad den operatorn skulle göra är att hitta pk's som är externt ändrade samt skicka dessa med en ny typ av updateRequest som gör att alla persisterade members läggs i changepacke och landar därmed i Synk-listan - som styr refresh
==== Helpful Queries for SQL Server ====
Here are a couple of helpful queries: [[Change tracking help SQL]]
[[Category:SQL]]
{{Edited|July|12|2024}}


Man skulle då helt själv med serversidejob styra hur ofta - och vad som behöver läsas om så här.
[[Category:TOC]]

Latest revision as of 14:13, 26 March 2024

Background

The MDrivenServer has built-in change tracking. It is implemented as a "dirty list" of objects that all clients can ask for. When the client gets this list, it marks all matched objects in memory as "dirty" and discards them. If any of these objects are present in a user interface (that is currently shown), these objects will be immediately reloaded from the MDrivenServer/ SQL Server.

MDrivenServer's dirty list is not part of the SQL Server and the MDrivenServer won't find out if some other client or service changes the underlying tables directly in the SQL Server.

The need for external dirty list handling (aka. change tracking) is often needed when there's legacy system reading and writing to the same SQL database. Because of this, it's not a good idea to have it on all tables in the database. You probably only want change tracking on the tables that can be affected by the non-MDriven system.

MDriven Implementation

Support for Change Tracking in SQL Server

Tracking of changes is available from SQL Server 2008 and in Azure SQL. It's available in the Express editions, so you can use it in the built-in Turnkey database.

Background About This on MS Site

https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-tracking-sql-server

Article with details: https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/work-with-change-tracking-sql-server

How We Propose You Use the MDriven Features to Do Change Tracking

We want to put as little extra load as possible on the SQL Server. We, therefore, use different jobs to detect that something changed and then what has changed.

  • Something changed: The stored procedure CHANGE_TRACKING_CURRENT_VERSION() can be used to know if ANY change has occurred. We compare this version value with the value when we last processed change information.
  • What has changed: If there's a change, we iterate change information for all tables we're interested in.

Both of these methods use sqlpassthroughobjects to directly use SQL to return information.

It then uses SuspectExternalUpdateInvalidate to "invalidate", make dirty, the changes objects.

Support With Other Database Servers

The MDriven implementation is not dependent on HOW you find out what has changed. As long as you can find the primary key of changed rows, you can use this functionality.

Applying Change Tracking

Preparing Your SQL-server

The suggested pattern to allow for MDriven Applications to effectively discover changes to data made by others is this. Turn on change tracking on the DB and on the tables you want to discover foreign changes to.

To turn change tracking on, do this:

ALTER DATABASE <database name>
SET CHANGE_TRACKING = ON  
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON) 

Turn on change tracking for each table you want to track:

ALTER TABLE <table name>
ENABLE CHANGE_TRACKING

You do NOT need column track, we track whole rows/objects.

Model Additions

Add attributes in SysSingleton

  • CurrentVersionNumberFromChangeTracking: Int64?
  • LastHandledVersionNumberFromChangeTracking: Int64?

Viewmodel Additions

Read more on Serverside actions.

Create an MDrivenServer Server-side Job That Asks the SQL Server for the Current Change Track Version
  • Create a ViewModel SS_GetCurrentTrackingVersion rooted in SysSingleton
  • Add the CurrentVersionNumberFromChangeTracking attribute to the ViewModel.
  • Add an action called GetCurrentVersion with this EAL.
self.CurrentVersionNumberFromChangeTracking := SysSingleton.sqlpassthrough( 
'-- Get the current tracking version number
  DECLARE @CurrentVersionNumberFromChangeTracking bigint;
  SET @CurrentVersionNumberFromChangeTracking = CHANGE_TRACKING_CURRENT_VERSION();
  select @CurrentVersionNumberFromChangeTracking', Int64 )->first.Part1

Add criteria for serverside execution to the ViewModel with a PS OCL like this:

SysSingleton.allInstances
Create an MDrivenServer Server-side Job That Invalidates Changed Objects
  • Create a ViewModel SS_InvalidateExternalChanges rooted in SysSingleton
  • Add the CurrentVersionNumberFromChangeTracking and LastHandledVersionNumberFromChangeTracking attributes to the ViewModel
  • Add variable vHandleToVersionNumber as Int64
  • Add an action called Invalidate with this EAL.
-- Move to variable avoiding change of version during execution
vHandleToVersionNumber := vCurrent_SS_InvalidateExternalChanges.CurrentVersionNumberFromChangeTracking;
-- Invalidate changed objects
selfVM.SuspectExternalUpdateInvalidate(<classname>.sqlpassthroughobjects( 'SELECT <primarykeyattributename> FROM CHANGETABLE (CHANGES <tablename>, ' + LastHandledVersionNumberFromChangeTracking.toString + ') AS C') );
selfVM.SuspectExte....
selfVM.SuspectExte....
selfVM.SuspectExte....
-- Update last handled change number
self.LastHandledVersionNumberFromChangeTracking := vHandleToVersionNumber

The selfVM.SuspectExternalUpdateInvalidate..... is the one you need to adapt to the tables you are tracking.

One row for each of the classes/tables you're tracking.

  • Add criteria for serverside execution to the ViewModel with a PS OCL like this
SysSingleton.allInstances->select(ss|ss.CurrentVersionNumberFromChangeTracking <> ss.LastHandledVersionNumberFromChangeTracking)

This means that the job isn't even invoked if there's nothing to do.

Recommended Interval

Interval has to be adjusted depending on the need and load on the server.

It can be quite low because it's a very fast operation - for example, 5 seconds, or what suits your need of discovery.

The detection takes about 10 ms to run and gets changes for 40 tables (without changes) around 100 ms.

Generating OCL and SQL for Change Tracked Classes

You can use the model debugger to make it easier to keep your change-tracking OCL and SQL in sync with your model. Note! This OCL is for a database with reverse-engineered tables, having non-standard table names and primary keys.

Extend Your Model

If you add a tagged value to your classes, for example, called ChangeTracking. See TaggedValues for more information.

ChangeTracking tagged value with icon
Tagged value editor
SQL for Turning Change Tracking On

Using this tag called ChangeTracking, you can use this OCL to generate SQL for turning change tracking on for each table.

Class.allInstances->select(s | s.TaggedValue->select(tv | tv.Tag.toUpper='CHANGETRACKING')->notEmpty)->collect(c |
  String.format('ALTER TABLE {2} ENABLE CHANGE_TRACKING  -- For class {0}',
    c.Name,
    c.TaggedValue->select(tv | tv.Tag='Eco.PrimaryKey')->first.Value,
    c.TaggedValue->select(tv | tv.Tag='Eco.TableName')->first.Value
  )
)->orderBy(s|s)

This will create a list of rows like this:

ALTER TABLE [CV] ENABLE CHANGE_TRACKING  -- For class CV

Run this SQL on your SQL Server database.

OCL for Invalidating Objects
Class.allInstances->select(s | s.TaggedValue->select(tv | tv.Tag.toUpper='CHANGETRACKING')->notEmpty)->collect(c |
  String.format('selfVM.SuspectExternalUpdateInvalidate({0}.sqlpassthroughobjects( \'SELECT {1} FROM CHANGETABLE (CHANGES {2}, \' + LastHandledVersionNumberFromChangeTracking.toString + \') AS C\') );',
    c.Name,
    c.TaggedValue->select(tv | tv.Tag='Eco.PrimaryKey')->first.Value,
    c.TaggedValue->select(tv | tv.Tag='Eco.TableName')->first.Value
  )
)->orderBy(s|s)

This will create a list of rows like this:

selfVM.SuspectExternalUpdateInvalidate(CV.sqlpassthroughobjects( 'SELECT [CV_ID] FROM CHANGETABLE (CHANGES [CV], ' + LastHandledVersionNumberFromChangeTracking.toString + ') AS C') );

Insert this OCL in the SS_InvalidateExternalChanges action.

Helpful Queries for SQL Server

Here are a couple of helpful queries: Change tracking help SQL

This page was edited 47 days ago on 03/26/2024. What links here