fix(sync): TAM-6858: Fix patient_invoice_insurance_plan duplicate issue (HOTFIX 2.54)#10045
fix(sync): TAM-6858: Fix patient_invoice_insurance_plan duplicate issue (HOTFIX 2.54)#10045chris-bes wants to merge 9 commits into
Conversation
|
🦸 Review Hero Summary Below consensus threshold (5 unique issues not confirmed by majority)
Local fix prompt (copy to your coding agent)Fix these issues identified on the pull request. One commit per issue fixed.
|
| // Restores only originate from the central server, except for models that opt in | ||
| // to facility-originated restores (see Model.acceptsFacilityRestores) | ||
| if ( | ||
| (isCentralServer === false || model.acceptsFacilityRestores) && |
There was a problem hiding this comment.
This PR is to migrate patient_invoice_insurance_plan.id to be deterministic, hence, instead of creating a new record when a previously deleted patient_invoice_insurance_plan is re added, the data is restored instead
However, the current sync model does not allow data restore originates from facility server (see original code). This was originally done by Biao which I'm not 100% sure the reason behind it.
My guess is I can understand it coming from the idea that restoring data is more of an admin task, but I don't see any problem with allowing it globally now since patient_invoice_insurance_plan is a legit case that we should allow restore coming from facility server.
However I'm also wary that any small change in sync can causes a lot of unknown side effects, so I'm playing it safe now and only allow it for patient_invoice_insurance_plan for now.
What do you reckon @edmofro ?
There was a problem hiding this comment.
I think the reason is that our sync conflict handling is last-write-wins, but with a trump card that delete-always-wins.
Imagine that a record is on two facility servers
- Facility A deletes it, then syncs that delete up to the central server
- Before syncing down that delete, Facility B edits it, then syncs that edit up to the central server
This would create an unexpected restore.
One option is to change how we store whether an insurance plan is deleted, and use something like visibility_status, or removed_at instead, which avoids facility restores of soft deleted records.
Thoughts @chris-bes?
There was a problem hiding this comment.
Yeah I did think of that approach, but did not have a strong preference since it would mean every time we want to allow restoring originates from facility, we would have to add an extra column there... But now after some more thinking, I don't think it's going to be a common case. Combined with the unexpected restore behaviour, I'm probably fine with your suggestion.
edmofro
left a comment
There was a problem hiding this comment.
I have comments to talk through
| // Restores only originate from the central server, except for models that opt in | ||
| // to facility-originated restores (see Model.acceptsFacilityRestores) | ||
| if ( | ||
| (isCentralServer === false || model.acceptsFacilityRestores) && |
There was a problem hiding this comment.
I think the reason is that our sync conflict handling is last-write-wins, but with a trump card that delete-always-wins.
Imagine that a record is on two facility servers
- Facility A deletes it, then syncs that delete up to the central server
- Before syncing down that delete, Facility B edits it, then syncs that edit up to the central server
This would create an unexpected restore.
One option is to change how we store whether an insurance plan is deleted, and use something like visibility_status, or removed_at instead, which avoids facility restores of soft deleted records.
Thoughts @chris-bes?
| // having facility-originated restores applied on central. Note there is no tick-based | ||
| // conflict resolution on deletion state, so a live copy pushed by a facility that | ||
| // hasn't yet pulled a deletion will resurrect the record — only opt in where | ||
| // re-add-wins is acceptable. |
There was a problem hiding this comment.
Oh yep this comment (presumably by claude) nailed the tradeoff
| // (patient_id, invoice_insurance_plan_id), so each pair must have exactly one row first. | ||
| // The released insert-new-on-re-add behaviour can leave a live row plus soft-deleted | ||
| // tombstones for the same pair. Keep the live row if one exists, otherwise the | ||
| // soft-deleted row with the highest id (deterministic across servers since ids are synced). |
There was a problem hiding this comment.
If they are in sync at the time of upgrade...?
| ) | ||
| DELETE FROM logs.changes | ||
| WHERE record_id IN (SELECT id FROM deleted_duplicates) | ||
| AND table_name = 'patient_invoice_insurance_plans'; |
There was a problem hiding this comment.
Ah ok so this has come up before, but we haven't solved it. We shouldn't delete records from logs.changes, it is an immutable and sacred audit log. Instead we need a way to represent that a record in logs.changes has had its source record deleted.
@julianam-w do you have any suggestions here?
There was a problem hiding this comment.
Add a record in logs.changeswith a filled record_deleted_atand add a note in migration_context/ reason (I'm not sure which is for what)
| // the same row (rather than inserting a new record), and two facilities adding the same | ||
| // pair offline mint the same id and dedupe on sync instead of colliding. | ||
| // | ||
| // logs.changes and sync_lookup are remapped to the new ids (the preceding migration already |
There was a problem hiding this comment.
Again we are making changes to logs.changes. This is a lot less bad than removing records entirely, and we have done it before, but it's not entirely comfortable.
Thoughts @julianam-w @passcod?
There was a problem hiding this comment.
Is that what the migration_context field is for? We create a new record in logs.changes with the change? This looks similar to updating the registration_status in patient_program_registrations
There was a problem hiding this comment.
I think because we are migrating UUIDs to deterministic IDs which can be treated as deleting and recreating data, we can do both of these:
- Insert new logs.changes with record_deleted_at that reflects the old patient_invoice_insurance_plans data (data that using UUIDs for IDs) have been deleted
- Insert new logs.changes that reflects the latest patient_invoice_insurance_plans data that has got the IDs migrated to deterministic IDs (ie: patient_id;insurance_plan_id)
Changes
Add a brief description of the changes in this PR to help give the reviewer context.
Auto-Deploy
Options
Tests
Review Hero
.github/review-hero/suppressions.yml. Also runs automatically at the end of any auto-fix run.Remember to...