Offline
Draw applications have offline capabilities and you can enable them with the offline.enabled
config parameter.
What is a Draw Offline Application ?
When an application has enabled offline mode, it changes some of its behaviour
- when it looses connection to the orchestrator, the internal state of the app becomes
offline
and notdisconnected
- when the app is first launched, instead of loading only the first screen, the app will download the entire code as data
- with offline enabled, all the code as data is persisted in a local db in the browser (Indexed DB)
It means that, from this point onward, the app is able to be launched offline.
When the application is in a 'disconnected' state, it will attempt to reconnect to the orchestrator every 5 seconds. During this disconnected state, the application will be unable to execute any queries or transactions.
When an app is in offline
state, it will continue to work, making queries towards the offline cached database and keeping transactions for synchronising them later.
Persistance in Offline mode ?
When an app has enabled offline mode, it will persist the data that could be useful for offline usage. The way this persistance is accomplished depends of the type of data.
Static data persistance
A web app needs some files to start, such as the index.html
and the related javascript, css, or any other static resources (images, fonts, ...). All these files, considered static, are cached using the service worker and the browser Cache API. This is a standard way to enable offline in PWA applications. This is done automatically for all Draw applications.
Learn more in the PWA doc section
Code as data persistance
Draw applications require their code-as-data to be available. This code is usually reached by sending HTTP requests. This assumes network connectivity to the orchestrator. When an application has enabled offline, at launch time the runtime will request all the code as data (with standard requests to the orchestrator) and then will persist the results in a local database (on-device: Indexed DB). This database is then able to execute queries like if they were done by the orchestrator or any other data sources. It means that after the first launch of the application it will later be able to start entirely offline.
Business data persistance
By default the business data is not downloaded and therefore not available when offline. The reason is that business needs are very different and its the developer's responsibility to decide what business data must be in the cache. For large applications we will probably not want to download "everything" or for user-centric applications we won't want to download data that the user has no permission for.
In order for some business data to be persisted, one must use explicit bricks that place the downloaded data in a "Bucket" labeled with a cache_id
. Behind the scene, the runtime will persist the result of these queries and thus making this data available in the app offline.
The term bucket implies a temporary holding space (IndexedDB or local storage), suggesting that the data stored within it will eventually be synchronized when connectivity is restored.
The best practice here is to create a brick that executes a query covering all the data required when offline. As soon as the data is in the cache, it's possible to make smaller queries offline and the local database will be able to execute them.
In other words: it's not necessary to "cache" the exact same queries that will need to be executed offline. The purpose is to ensure that all required data has landed once in the local offline cache database.
The bricks to cache business data are
Offline: Execute and Cache Query
- identical thanExecute Query
but persist result in offline cacheOffline: Observe and Cache Query
- identical thanObserve Query
but persist result in offline cache
These bricks require a cache_id
input to identify the explicit intention of caching such data. Imagine that you develop an app to manage employees data and you use a brick to explicitly cache the data related to employee "1234", then you might give the name "employee_1234" as cache_id
. This will allow the app to know:
- if some data is already cached (using
Offline: Get Cache Entries List
brick) - or remove some data from the cache (using
Offline: Clear Cache Entry
brick)
A cache_id
is like a bucket that can contain unlimitted queries, and it's often the case. Many times several queries are required to download some business data with all its related objects.
The business data using the embedded graph database as data source is considered like "Code as data" and is fully downloaded and persisted at launch time
Handling the lifecycle of persisted business data
Adding data in the cache
Offline: Execute and Cache Query
Offline: Observe and Cache Query
Listing data in the cache
Offline: Get Cache Entries List
Remove data from the cache
Offline: Clear Cache Entry
Examples
Load all business data at startup
For small apps where every user has the same permission, you might want to run a query at the start of the App (in the UI App OnLoad) which gets all the business data. In fact you probably will need several queries and execute them all with the Offline: Execute and Cache Query
brick giving the same cache_id
name like Business Data
. In such a scenario you don't need to clear the cache later.
Load only the data following users permission
Instead of loading all data, if the app has some sort of permission around data, you probably want to only download data that the user has permission to see.
Load parts of the business data when offline usage will be required
If a user of the app will need to work offline on some part of the data, you can provide a "Download" button which, when triggered, execute a query (or queries) to get all the data related to the items needed offline. In such a scenario you will want to use a bucket name with the tag of the origin instance used for the queries like instance_<tag>
.
In the UI you can then show a "Download" button if no cache entry is named instance_<tag>
in the output of the Offline: Get Cache Entries List
bricks or a "Clear" button instead that, when triggered, will use the Offline: Clear Cache Entry
brick to release this data from the offline cache.
Offline changes synchronisation
When being offline, the app records changes in a "Pending Changes" store in the database. These changes must be "applied" as transactions when connectivity is back.
You can use the Offline: Has Pending Change
brick to know if the local DB contains any pending changes that must be synchronised.
When the app is back online, you can trigger the Offline: Sync Changes
brick to initiate a synchroniation. It will start sending pending transactions one by one and if no error is encountered it will trigger its output control flow once finished.
Conflict resolution during synchronisation
The runtime applies 2 rules to automatically handle conflicts in changes. In order:
- Deletion wins : if an instance is deleted either online or offline it must be considered deleted after conflict resolution
- Offline wins : if an instance has been updated both online and offline, the offline changes are considered winner
What is something goes bad ?
If the runtime is not able to synchronise the changes, the application will stay in a hybrid state. To unblock such an unlikely situation we provide two bricks:
- Offline: Get Pending Changes which creates a JSON
string
containing all pending changes, including files. This file can be used to apply the transactions by an experimented developer and analyse why it failed to be synced - Offline: Clear Pending Changes which deletes all pending changes and gets the application back to a full online state. It's important to understand that this brick wipes out any unsynched changes so it's good practice to download the changes first
Offline Configuration
Activating offline mode
The offline mode can be enabled on the full instance or on a per application basis:
offline.enabled: boolean
: enable/disable offline on the instance (default:false
)offline.<app_tag>.enabled: boolean
: enable/disable offline only on one application. This value overrides the instance value (default:offline.enabled
value)
Auto Back Online
Before we discuss this setting, we must understand that there are two ways to go offline.
- Using the brick “Offline: Go Offline” ⇒ this is considered as a manual and intentional choice of the user to go offline. It means that from this point onward, even if the app has network connection, it will not use it
- By losing network connectivity (which is triggered when losing connection to the Orchestrator) ⇒ this is considered as a non-intentional choice to go offline
If the user chooses to manually go online, the app will never try to go back online by itself. However, if the app loses connection, it might want to go online as soon as it gets connection again. This is what happens if autoBackOnline === true
. However, if autoBackOnline === false
, the app will stay offline until the user manually decides to go online again (using the “Go Online” brick).
offline.autoBackOnline: boolean
: enable/disable auto back online on the instance (default:false
)offline.<app_tag>.autoBackOnline: boolean
: enable/disable auto back online on the instance (default:offline.autoBackOnline
value)