Skip to content

Handle Chorus "undeleting" entities due to merge conflicts #55

@myieye

Description

@myieye

Current situation

In FieldWorks, if one user edits an entry and another user deletes in, then Chorus detects the conflict and "undeletes" the entity.
Chorus then notifies the user via a note, so they can make a decision.

If this happens because an entity is deleted by FieldWorks Lite, here's what happens:

  • The delete is propagated to FieldWorks
  • FieldWorks/Chorus undeletes it (due to a detected merge conflict)
  • In the next sync, it's determined to be a new entity (because it's not in the project snapshot) and is synced as a "create" back to FieldWorks Lite
  • FieldWorks Lite generates Create changes for the entity
  • When applying the create changes Harmony:
    • can find a snapshot for the entity
    • so, it just ignores the create changes (i.e. the latest snapshot is still marked as deleted)
  • FieldWorks Lite then tries to query the newly created entry out, but can't find it in the projected tables, because deleted entities are deleted from the projected tables
  • 💥 NullReferenceException()

How FieldWorks Lite should behave

We need to respect Chorus' "undeleting".

Potential approaches

1. Detect deleted entities

  • FieldWorks Lite detects that the entity has been deleted
  • Creates a change specifically to "undelete" it
  • Updates the entity

This is both complex and expensive, because we need to consider the entity's hierarchy and potentially "undelete" multiple entities. We would probably do that in a catch block, because it will be very rare.

2. Soft deletes
We could potentially rearchitect everything, so that we soft delete things in the projected tables, but...that would be a massive change with very little payoff.

3. The easy way

  • Harmony lets Change.NewEntity automatically undo deletes.
  • It just generates a brand new "undeleted" snapshot

This is nice and simple, because we can keep passing in the same change objects and they'll have the same effect that they would if the entity never existed.

Notes:
Only case 2 would really make it possible to do a diff. Not diffing means we will overwrite any other changes made to the deleted entity in FieldWorks Lite, because we don't know what the entity looked like the last time FieldWorks got it from FieldWorks Lite. (until now we've avoided reconstructing entities and their relationships at a previous point in time as it's non-trivial) But I don't think that matters, because (1) those changes are still somewhere in Harmony history, (2) this is for edge cases and (3) if it was deleted in FieldWorks Lite, it's less likely that additional changes were made to it in FieldWorks Lite.

Proposal

I think we should pick number 2 (the easy way).
That means Harmony should be adapted, so that Change.NewEntity creates a new "undeleted" snapshot if the latest snapshot is marked as deleted.
(Perhaps it should simply always create an entirely new snapshot, even if the latest snapshot is not marked as deleted, but I don't think that's an important decision to make at the moment.)

It's worth noting that it's always possible for Harmony consumers to still do "undeletes" by creating a change that explicitly sets DeletedAt to false.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions