A tale of offline support an conflict resolution

A tale of offline support an conflict resolution

Mobile devices are not always connected to a reliable Internet connection, but that does not matter that our apps should stope working if they are offline. In this talk I'll explain some approaches the Wunderlist team decided to take in order to provide full offline support.

Ed8556863d194723dc49762b350bcda6?s=128

Antonio J. Consuegra

February 27, 2016
Tweet

Transcript

  1. A tale of offline support and conflicts resolution Antonio J.

    Consuegra
  2. Who am I? Antonio J. Consuegra Senior Software Engineer @

    Microsoft @aconsuegra +AntonioConsuegra
  3. What is this talk NOT about? • RxJava

  4. What is this talk NOT about? • RxJava • Testing

  5. What is this talk NOT about? • RxJava • Testing

    • Clean Architecture
  6. What IS this talk about? • Offline support matters, a

    lot.
  7. What IS this talk about? • Offline support matters, a

    lot. • How to synchronise offline data.
  8. What IS this talk about? • Offline support matters, a

    lot. • How to synchronise offline data. • How to resolve conflicts.
  9. Handle offline status like a Pro • Never, never block

    the user.
  10. Handle offline status like a Pro • Never, never block

    the user.
  11. Handle offline status like a Pro • Never, never block

    the user.
  12. Handle offline status like a Pro • Never, never block

    the user.
  13. Handle offline status like a Pro • Never, never block

    the user. • Only a few calls should require being online.
  14. Handle offline status like a Pro • Never, never block

    the user. • Only a few calls should require being online. • Most of user’s actions can be retried when online.
  15. How can you retry API calls? • Queue them (Priority

    Job Queue, Job Scheduler, etc.)
  16. • Queue them (Priority Job Queue, Job Scheduler, etc.) •

    Save only the last state of an entity… How can you retry API calls?
  17. • Queue them (Priority Job Queue, Job Scheduler, etc.) •

    Save only the last state of an entity… • Save the current “sync” state. & How can you retry API calls?
  18. Sync state public class Entity {
 
 private enum SyncState

    { DIRTY, SYNCING, SYNCED }
 
 private SyncState syncState = SyncState.DIRTY;
 
 public Entity() {
 …
 }
 
 public SyncState getSyncState() { … }
 
 public void setSyncState(SyncState syncState) { … }
 }
  19. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  20. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  21. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  22. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  23. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  24. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  25. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  26. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  27. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  28. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  29. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  30. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  31. Sync state Note text = “Hello” state = SYNCED Note

    text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY Online? YES Note text = “Hey Yo” state = SYNCING Some retry logic Success? Note text = “Hey Yo” state = SYNCED Note text = “Hey Yo” state = DIRTY YES NO NO ☁
  32. But in the real world… Root User List Task Note

  33. But in the real world… Root User List Task Note

  34. But in the real world… Root User List Task Note

  35. But in the real world… Root User List Task Note

  36. But in the real world… Root User List Task Note

  37. But in the real world… Root User List Task Note

  38. Local ID & Online ID • At creation time, a

    local_id is assigned to each entity. • When the entity is synced, it receives an online_id. • Children have to use local_id or online_id to identify its parent.
  39. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  40. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  41. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  42. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  43. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  44. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  45. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  46. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  47. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  48. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  49. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  50. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  51. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  52. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  53. Flush dirty entities Root User List Task Subtask Comment Reminder

    … IF entity is DIRTY ELSE REPEAT with child/sibling SET state to SYNCING PUSH entity to API IF success SET state to SYNCED IF onlineId was NULL UPDATE parentId in children REPEAT with child/sibling ELSE SET state to DIRTY REPEAT with child/sibling
  54. All right, but… • The proposed approach works fine for

    one user/device. • We have to add a revision to each entity. • Every time an entity is updated, its revision is updated by the API.
  55. Revisions ☁ Task title = “Hello” localId = 1 Task

    title = “Hello” localId = 1 onlineId = 1 revision = 1 HTTP POST 201 OK Case 1: Create a new entity
  56. Revisions ☁ Task title = “Hello” localId = 1 Task

    title = “Hello” localId = 1 onlineId = 1 revision = 1 HTTP POST 201 OK Case 1: Create a new entity
  57. Revisions ☁ Task title = “Hello” localId = 1 Task

    title = “Hello” localId = 1 onlineId = 1 revision = 1 HTTP POST 201 OK Case 1: Create a new entity
  58. Revisions ☁ Task title = “Hello” localId = 1 Task

    title = “Hello” localId = 1 onlineId = 1 revision = 1 HTTP POST 201 OK Case 1: Create a new entity
  59. Revisions ☁ Task title = “Hello” localId = 1 Task

    title = “Hello” localId = 1 onlineId = 1 revision = 1 HTTP POST 201 OK Case 1: Create a new entity
  60. Revisions ☁ Case 2: Update an entity Task title =

    “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 HTTP PATCH 200 OK
  61. Revisions ☁ Case 2: Update an entity Task title =

    “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 HTTP PATCH 200 OK
  62. Revisions ☁ Case 2: Update an entity Task title =

    “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 HTTP PATCH 200 OK
  63. Revisions ☁ Case 2: Update an entity Task title =

    “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 HTTP PATCH 200 OK
  64. Revisions ☁ Case 2: Update an entity Task title =

    “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 HTTP PATCH 200 OK
  65. Revisions ☁ Case 3: Delete an entity Task onlineId =

    1 revision = 2 HTTP DELETE 200 OK
  66. Revisions ☁ Case 3: Delete an entity Task onlineId =

    1 revision = 2 HTTP DELETE 200 OK
  67. Revisions ☁ Case 3: Delete an entity Task onlineId =

    1 revision = 2 HTTP DELETE 200 OK
  68. Revisions ☁ Case 3: Delete an entity Task onlineId =

    1 revision = 2 HTTP DELETE 200 OK
  69. Revisions ☁ Case 4: Sync new entities Task title =

    “Hello” localId = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1
  70. Revisions ☁ Case 4: Sync new entities Task title =

    “Hello” localId = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1
  71. Revisions ☁ Case 4: Sync new entities Task title =

    “Hello” localId = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1
  72. Revisions ☁ Case 4: Sync new entities Task title =

    “Hello” localId = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1
  73. Revisions ☁ Case 4: Sync new entities Task title =

    “Hello” localId = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1
  74. Revisions ☁ Case 4: Sync new entities Task title =

    “Hello” localId = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1
  75. Revisions ☁ Case 4: Sync new entities Task title =

    “Hello” localId = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1 Task title = “Hello” localId = 1 onlineId = 1 revision = 1
  76. Revisions ☁ Case 4a: Sync updated entities Task title =

    “Hello” onlineId = 1 revision = 1 Task title = “Hello” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2
  77. Revisions ☁ Case 4a: Sync updated entities Task title =

    “Hello” onlineId = 1 revision = 1 Task title = “Hello” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2
  78. Revisions ☁ Case 4a: Sync updated entities Task title =

    “Hello” onlineId = 1 revision = 1 Task title = “Hello” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2
  79. Revisions ☁ Case 4a: Sync updated entities Task title =

    “Hello” onlineId = 1 revision = 1 Task title = “Hello” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2
  80. Revisions ☁ Case 4a: Sync updated entities Task title =

    “Hello” onlineId = 1 revision = 1 Task title = “Hello” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2
  81. Revisions ☁ Case 4a: Sync updated entities Task title =

    “Hello” onlineId = 1 revision = 1 Task title = “Hello” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 1 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2
  82. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3
  83. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 ⚡
  84. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 ⚡
  85. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 ⚡
  86. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 ⚡
  87. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 ⚡
  88. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 ⚡
  89. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 ⚡
  90. Revisions ☁ Case 4b: Sync updated entities Task title =

    “Hey Yo” onlineId = 1 revision = 2 Task title = “Hey Yo” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 2 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 ⚡
  91. Conflicts ☁ Task title = “Beer!” onlineId = 1 revision

    = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  92. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  93. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  94. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  95. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  96. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  97. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  98. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  99. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  100. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  101. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  102. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  103. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  104. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  105. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  106. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  107. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  108. Conflicts ☁ ⚡ Task title = “Beer!” onlineId = 1

    revision = 3 Task title = “Beer!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 3 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Buuu!!” onlineId = 1 revision = 4 Task title = “Beer!” starred = TRUE revision = 3 409 ERROR Task title = “Buuu!!” starred = TRUE revision = 4 Task title = “Buuu!!” starred = TRUE revision = 5 Task title = “Buuu!!” starred = TRUE revision = 5
  109. Conflicts Resolution • The API returns 409 when the client

    needs to fix a conflict. • Each entity holds a reference to the modified fields. • All platforms use the same algorithm to solve conflicts, we call it ThreeWayMergeResolver.
  110. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  111. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  112. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  113. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  114. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  115. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  116. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  117. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  118. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  119. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  120. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  121. ThreeWayMergeResolver IF entity has conflict GET entity from API IF

    success FOR each field in entity IF field was modified locally UPDATE field with local value SET state to DIRTY ELSE IF error code IS 404 DELETE entity locally ELSE RETRY
  122. ? Thanks! @aconsuegra +AntonioConsuegra