CommCare is designed to deliver a subset of the cases in the database to users’ devices through a sync algorithm.
Timing
CommCare syncs after every form submission (which is generally the trigger for a user being ready to move onto their next action), or periodically every 5 minutes (which usually includes at log in) provided another sync hasn't been triggered within that time frame. Periodic syncing makes for better app performance than continuous database syncing would. Also, CommCare is designed to reconcile simultaneous usage asynchronously (after submission) rather than synchronously (during usage), which gives the user a consistent view of the data until they perform a 'write' operation.
User Scope
When a user syncs with the server - uses their CommCare application, or logs into the sever to use an online CommCare interaction - there exists a set of cases that should be "available" to that user. This (roughly) includes cases that are owned by that user, or cases that are derivative to cases that the user owns. It will not include all cases (even if, for instance, the user was connecting on a web browser to a server storing all cases). This set of cases is referred to as the user's scope.
When syncing the server and user device both determine the current user's scope. They then identify what is currently on each device, what cases should be in scope and out of scope, and what changes need to be made to reconcile the scope so that the user on any device "sees" the same set of data.
Case Groups
The first aspect of scope is "ownership". All cases have the concept of an "owner", which is either an individual user or a group of users.
Case ownership can be determined in two ways. Direct ownership or group ownership. In direct ownership, the owner of a case is specified by setting the owner_id
of the case to be the id of a registered user. In group ownership, the server defines a group which has an id, and is associated with a set of user id's. If the owner_id
is set to that of a group, each user id associated with a group id is considered an owner of the case.
The groups an individual user is a part of should be defined on the server, and sent to the user device during any data sync. The groups are sent down to the device as a fixture (data table) associated with an individual's user_id.
Indexing Relationships
Case indices are the second component of identifying a user's scope. If a child case (C) is in scope, the parent index case (P) will be in scope as well, because it (P) is required for the child case (C) to function correctly. Conversely, if a case (E) has an extension index to another case (H), this signifies that the extension case (E) is only meaningful in the context of the host case (H), and that the absence of the host case should also result in the absence of the extension case. Therefore, if case (H) is not relevant in the user's scope, (E) will be removed. If (H) is in scope and (E) is open, (E) will be included as well.
Sync Contract
Both the server and remote devices should have a consistent set of cases which are relevant to the user, which is known as the sync contract. In order to determine what cases to send over or manipulate, first the set of relevant cases for a user is defined, then it is identified which cases need to be delivered.
The first time that a device requests a sync from the server, the server identifies the full set of data to be transmitted to the device.
The set of all owned cases for a user is defined as all cases without an extension index, whose owner is the user or a group that the user is a member of. The full universe of cases which are in the user's scope is then iteratively defined as follows:
- All cases that belong to a user are in its scope.
- Child cases pull their parents into scope.
- Cases pull their extension cases into scope.
Once scope is established, it is pared down to contain only live cases. Live cases are determined through the following process.
- Initialize a marking process for each case:
- Closed are marked "dead"
- Open extension cases are marked abandoned
- Other open cases without an extension index are marked live
- Traverse all cases. For every case marked live each case it indexes with a child relationship is marked live. ("Children pull in their parents.")
- Traverse all cases. For every case marked live each case that indexes it with an extension relationship is marked as live if currently marked abandoned. ("Cases pull in their open extension cases.")
View Exact Data Sent to the Device
You can use the same API URL which is used by the mobile devices to see what data will be sent to the phone or web application during a sync.
View Restore Data for Any Mobile User in HQ | |
View All Data Associated with a User: | http://www.commcarehq.org/a/{my-project-space}/phone/restore/?as={user}@{my-project-space}.commcarehq.org&version=2.0 |
App Specific: | http://www.commcarehq.org/a/{my-project-space}/phone/restore/{app_id}/?as={user}@{my-project-space}.commcarehq.org&version=2.0 (assumes current state of the app, not particular build) |
Build Specific: | http://www.commcarehq.org/a/{my-project-space}/phone/restore/{build_id}/?as={user}@{my-project-space}.commcarehq.org&version=2.0 |
Additional Parameters | |
since={previous restore id} | Incremental response since the previous sync. |
items=true | Include the item number in the response. |
overwrite_cache=true | Don't return a cached value but rather recompute the response. Deletes the cache, so the next time you sync on the phone, it will use the newly computed result. |
raw=true | Raw restore response without the UI. |
device_id={device_id} | Fake a restore from a particular device id. WebApps uses a device ID of the form "WebAppsLogin". |
app_id={app_id} | Restore using app aware sync. This will only sync down the reports for that particular application. |
case_sync={clean_owners | livequery} | Select which case sync algorithm to use with this restore |
hide_xml=true | Shows only aggregate case counts, without loading associated XML data |