Repository navigation
Use upsert instead of REPLACE INTO when applying synced rows - #236
khawarizmus wants to merge 1 commit into
Conversation
rkistner
left a comment
There was a problem hiding this comment.
I'm in favor of this change, and think a minor version update should be fine.
It would however be good to do a basic performance test of this. These two benchmarks should help here:
powersync-sqlite-core/dart/test/sync_local_performance_test.dart
Lines 134 to 149 in d236de9
I also think it would be good to additionally enable recursive triggers in all our SDKs, since it's quite unexpected if triggers don't fire after synced changes.
It would be good to get @simolus3's input on this as well.
|
I agree that using an upsert here makes sense, and that we can use a minor/patch version (changing the behavior of diff triggers should be okay since it's an improvement and they're not stable yet).
Yeah, judging by the fact that Dart/native enables this and Dart/web doesn't, I think the missing SDKs are an oversight. Test results:
The slight regression in the amount of reads doesn't seem that concerning to me. |
Problem
When a synced row already exists, core writes it with
REPLACE INTO. SQLite then:recursive_triggersis on (JS and Dart native turn it on; Swift, Kotlin and Dart web don't)Without the pragma, triggers on
ps_data__*tables break. Example: FTS shows outdated/duplicate results and inserts fail withconstraint failed.Reported in powersync-ja/powersync-swift#93 and thunderbird/thunderbolt#1512.
Fix
Use an upsert (
INSERT ... ON CONFLICT DO UPDATE) forps_data__*andps_untyped. Direct and raw tables already do this.Why not turn on
recursive_triggersin every SDK?recursive_triggersKeep the pragma in JS/Dart: user-defined raw table put statements may still use
REPLACE.Behavior change
A synced edit to an existing row now runs UPDATE triggers and keeps the rowid, on every SDK.
Triggers that only listen for INSERT or DELETE no longer see synced edits. This needs an upgrade note.
JS diff triggers (
trackTableDiff) are affected. A synced edit was a DELETE + INSERT pair, and is now one UPDATE withprevious_value:Apps without custom triggers on internal tables: no change.
Tests
ps_data__*andps_untyped).Open question
Used Claude to plan and execute the fix.