Old DMS issues - now different? #453
-
@Mimetis as you are so close to a release, I just wanted to check off some issues that we came across in the old days of DMS. 1. after loading changes from the database, GetDMRowState was called for every row to determine what kind of change this would be for the client.For example: Even if the row was already inserted and updated (it had an update timestamp), the row was sent to the client as "insert" in case it last synchronized, when that row did not yet exist. While this may have worked with typical non-filtered sync scenarios, we had some serious issues with it. We fixed this doing two things:
2. always use UPSERT instead of distinguishing between INSERT and UPDATEThis is basically what we implemented to overcome the issue of (1). What is the reasoning behind it? Did you come to the same conclusion as we did? 3. sqliteprovider should only send local changesWe use the SQLite for our client databases. As our clients created weird sync conflicts, we had to dig through the code to find, that the SQLiteProviders
The latter caused some issues, because this would mean that a client A could, in case its timestamp got messed up, also send changes that it originally received from a client B. This, in turn, would cause sync conflicts on the server side - including potential data loss. Since DMS does not support P2P syncs, we removed the second clause (or by some other scopeid) so that clients only ever send their own changes. SELECT [side].[ProductCategoryID],
[base].[ParentProductCategoryID],
[base].[Name],
[base].[rowguid],
[base].[ModifiedDate],
[base].[Attribute With Space],
[side].[sync_row_is_tombstone],
[side].[update_scope_id]
FROM [ProductCategory_tracking] [side]
LEFT JOIN [ProductCategory] [base]
ON [base].[ProductCategoryID] = [side].[ProductCategoryID]
WHERE (
[side].[timestamp] > @sync_min_timestamp
AND
- (
- -- THIS IS BAAAD, as it may send changes of another client if the local system time is off target
- [side].[update_scope_id] <> @sync_scope_id OR
+ Only EVER send local changes!!
[side].[update_scope_id] IS NULL
- )
)
4. Add a foreign-key friendly way of applying changesDMS used to appliy changes the following way:
foreign key conflict on insert/updateNow this could cause a foreign key conflict.
Now, previously this would have failed, as the old DMS
Apparently, this case cannot happen any longer, as DMS changed the apply algorithm:
However, you can still have a FK conflict with this! foreign key conflict on update/deleteLets take the same example as before: One
When the client now sends the changes, DMS will (according to the new 2-step algorithm)
Note, that we cannot solve this by changing the order of the two tables in the sync config either! There is, however, a very simple solution: Changing the ApplyChanges algorithm to
So instead of applying all deletes/updates/inserts for each table separately, Then, we iterate over all the tables in reverse order and apply the deletes. This is to not violate any foreign key constraints when deleting rows from multiple tables that reference each other! As you can see, this is a very, very simple but effective code-change and it works like a breeze ever since in our production environment! |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 5 replies
-
Hey @gentledepp, 1. GetDMRowState & 2. Upsert
Detailed explanationsToday, there is no distinction between an The Update query uses a Here is an extract of the Sql Server statement of, for example, the ;WITH [changes] AS (
SELECT [p].[ProductID], [p].[Name], [side].[update_scope_id], [side].[timestamp], [side].[sync_row_is_tombstone]
FROM (SELECT @ProductID as [ProductID], @Name as [Name]) AS [p]
LEFT JOIN [Product_tracking] [side] ON [p].[ProductID] = [side].[ProductID]
)
MERGE [Product] AS [base]
USING [changes] on [changes].[ProductID] = [base].[ProductID]
WHEN MATCHED AND ([changes].[timestamp] <= @sync_min_timestamp OR [changes].[timestamp] IS NULL OR [changes].[update_scope_id] = @sync_scope_id OR @sync_force_write = 1) THEN
UPDATE SET [Name] = [changes].[Name]
WHEN NOT MATCHED BY TARGET AND ([changes].[timestamp] <= @sync_min_timestamp OR [changes].[timestamp] IS NULL OR @sync_force_write = 1) THEN
INSERT ([ProductID], [Name]) VALUES ([changes].[ProductID], [changes].[Name])
OUTPUT INSERTED.[ProductID]
INTO @dms_changed; -- populates the temp table with successful PKs And here is an extract of the INSERT OR REPLACE INTO [ProductCategory]
([ProductID], [Name])
SELECT [c].[ProductID], [c].[Name] FROM (SELECT @ProductID as [ProductID], @Name as [Name) as [c]
LEFT JOIN [Product_tracking] AS [side] ON [side].[ProductID] = @ProductID
LEFT JOIN [Product] AS [base] ON [c].[ProductID] = [base].[ProductID]
WHERE ([base].[ProductID] = @ProductID AND ([side].[timestamp] < @sync_min_timestamp OR [side].[update_scope_id] = @sync_scope_id))
OR ([base].[ProductID] IS NULL AND ([side].[timestamp] < @sync_min_timestamp OR [side].[timestamp] IS NULL))
OR @sync_force_write = 1; |
Beta Was this translation helpful? Give feedback.
-
3. Sending Changes, from Sqlite. Mitigation on
|
Beta Was this translation helpful? Give feedback.
-
4. Foreign-key friendly way of applying changesToday
Changing this order, as you propose, can fail at some point as well. Anyway, to mitigate the foreign keys constraints problematic, today
Disabling / Enabling constraints can be disable with the options That being said, disabling and enabling constraints can affect performances, since it locks the entire table (see #430 (comment)) If I don't find any example that can causes a fail sync with your order, I guess it's worth a try in the next version |
Beta Was this translation helpful? Give feedback.
3. Sending Changes, from Sqlite. Mitigation on
update_scope_id
I get your point on the
[side].[update_scope_id] <> @sync_scope_id
Basically this condition is mostly used on the server side.
If I want to rephrase it, It basically says to the server (from a client perspective) :
Please don't send me back the changes I just sent to you, because we know that they are already in sync, since ... I just sent these changes to you server, and you applied them correctly :)
Indeed, from a client perspective, this condition is never matched, because all the rows that have different scope id than
0000-000
(server) orNull
(local client) are coming from others scope and should not be sent back to the s…