views:

60

answers:

2

I'm writing a synchronizer software which will take all changes in one DB and synchronize them to another DB. To this end I've added in my table T two columns:

alter table T add LastUpdate rowversion, LastSync binary(8) not null default 0

Now I can easily select all rows that have changed since the last synchronization:

select * from T where LastUpdate > LastSync

However after performing the synchronization I should make the two fields equal. But updating the row also updates the timestamp, so I must do this:

update T set LastSync=@@DBTS+1 where ID=@syncedId

But I'm wondering - will this always work? What if I read the value of @@DBTS and then another user manages to insert/update a row somewhere before my row is committed? Is this risky code? And if yes - how could it be made better?

A: 

If you run this in a Serializable transaction then no other reads/writes will be able to affect these tables.

RepeateableRead may also do the job...

ck
Tables - yes. But what about the value of `@@DBTS`? That isn't stored in any table!
Vilx-
@Vilx If you take an exclusive table lock for the duration of the synchronisation then presumably it doesn't matter if `@@DBTS` gets incremented by an event in another table though.
Martin Smith
@Martin Smith - but @@DBTS is global for the whole DB. Why doesn't it matter? If I get one (older) value for `LastSync` field, and a newer for the `LastUpdate` field, my synchronization will be broken.
Vilx-
@Vilx Well if you update the table and the value of `@@DBTS` is 10 then another table gets a couple of rows updated making the value of `@@DBTS` equal to 12 and you end up storing 12 in the `LastSync` field why is that a problem? Next time you come to synchronise you are still looking for rows where `LastUpdate > LastSync`.
Martin Smith
@Martin Smith - the problem is that it's the other way round. `LastUpdate` gets the value **after** `LastSync`. Otherwise I wouldn't need that `+1` in the query in the first place.
Vilx-
+4  A: 

Storing "LastSync" in the same table as the real data is maybe not a good idea at all. Try storing it in another Table that doesn't have a rowversion. That way you avoid the "updating the row also updates the timestamp"-problem.

Your synchronizer software can then work this way:

  • Get the @LastSync value from the additional table
  • "Select @ThisSync = max(LastUpdate) from T where LastUpdate > @LastSync"
  • "Select * from T where LastUpdate > @LastSync and LastUpdate <= @ThisSync" are your rows for sync
  • Store @ThisSync as the new "LastSync" in the additional table.

Entries that are modified while the synchronization is running will have a higher rowversion value than the max() query. They will be synchronized the next time your synchronizer is called.

Oliver
That's an idea. I'll keep it in mind, in case nothing better comes up.
Vilx-