Make the pedigree id sticky across profile sync
The dog id in the synced profile shares one last-write-wins timestamp with name and birthday, and that timestamp comes from each device's own clock. A device whose profile was stamped later (clock skew, or a more recent name/birthday edit) would refuse to adopt a newer server profile that had just gained an id, or even push its empty id back over the stored one — so the pedigree id set on one device never reached the others. Make the id sticky on both sides of sync: an empty value never clears a set one, and a device/server adopts an id it is missing regardless of the blob timestamp. When both sides have an id, the newer profile still wins, so the dog can still be changed. Name and birthday keep plain last-write-wins.
This commit is contained in:
+16
-3
@@ -92,19 +92,32 @@ func (cs *ConfigStore) get(userID string) Config {
|
||||
// merge applies an incoming config for one user with last-write-wins by
|
||||
// UpdatedAt and returns the resulting stored config (which the caller sends back).
|
||||
func (cs *ConfigStore) merge(userID string, in Config) (Config, error) {
|
||||
// The upsert's WHERE clause enforces last-write-wins: the incoming row only
|
||||
// replaces the stored one when it is strictly newer.
|
||||
// Name/birthday/updated are last-write-wins: the incoming row replaces the
|
||||
// stored one only when strictly newer. The pedigree id is stickier — an empty
|
||||
// incoming value never clears a stored one, so a clock race between devices
|
||||
// can't drop it; when both are set, the newer profile's id wins with the rest.
|
||||
_, err := cs.db.Exec(`
|
||||
INSERT INTO config (user_id, name, birthday, pedigree_id, updated)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET
|
||||
name = excluded.name, birthday = excluded.birthday,
|
||||
pedigree_id = excluded.pedigree_id, updated = excluded.updated
|
||||
pedigree_id = CASE WHEN excluded.pedigree_id != '' THEN excluded.pedigree_id ELSE config.pedigree_id END,
|
||||
updated = excluded.updated
|
||||
WHERE excluded.updated > config.updated`,
|
||||
userID, in.Name, in.Birthday, in.PedigreeID, in.UpdatedAt)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
// Adopt a pedigree id the server is missing even from an older-stamped profile,
|
||||
// so a device that set it isn't blocked by another device's newer name/birthday
|
||||
// edit. (A set id is only ever changed by a newer profile that also sets one.)
|
||||
if in.PedigreeID != "" {
|
||||
if _, err := cs.db.Exec(
|
||||
`UPDATE config SET pedigree_id = ? WHERE user_id = ? AND pedigree_id = ''`,
|
||||
in.PedigreeID, userID); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
}
|
||||
return cs.get(userID), nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user