Effective scaling for apps with large datasets can reduce sync times while maintaining a convenient user interface. When an app syncs, the data is copied from the database to the device, allowing the user to easily interact with the data inside the app. By default all data associated with the app will be synced when the app is initially loaded. The default setting is effective for apps with small to medium datasets, but will result in long sync times for very large datasets.
Horizontal scaling is a technique by which the app creator can structure how and when data is synced into the app. This is accomplished by breaking the data up into buckets. Bucket #1 contains data the user needs to begin using the app. Bucket #2 contains additional data based on what the user selects in Bucket #1. This model can be scaled indefinitely with each bucket drilling down further based on the selection made in the preceding bucket.
To give an example, let’s consider an app that allows users to see a menu of desserts. To make things interesting, let’s assume our company offers a vast array of desserts totaling over 60,000 unique delicious choices. Loading the entire dataset will take some time. However, by thoughtfully breaking the data up into buckets, we can significantly reduce the sync time and improve performance.
I broke the tables up into three buckets based on the user experience I will create in my app.
When the user opens the app, they are taken to a home page where they can select the type of dessert. At this point, AppSheet has only synced the Type table. The user makes their selection and clicks to proceed.
AppSheet loads all rows from the Dessert table where Type matches the user’s selection from Bucket 1. By using the initial filter, we have significantly reduced the sync requirements from the full 60,000.
AppSheet loads only the Reviews and Recipes associated with the subset of Desserts loaded in Bucket 2. This offers another significant reduction to the number of records loaded during the sync.
User Settings are displayed as a Form view and allow users to make a selection and save the form. These values can then be accessed by formulas throughout the app
Security Filters are formulas that limit that data loaded during the sync. By creating a security filter based on the User Settings selected by the user, we are able to filter the data to only the Type(s) selected. The data that does not meet the filter criteria will not be included in the sync.
Security filter used on Dessert table
Security filter used on Review table
Variant Approach Using Slices
The implementation described above has a limitation where the user is not able to see any desserts until they select a Type. In some cases, you may want to allow your users to make their initial selection by searching on the full list of desserts.
Updated User Settings
Searching the entire dessert list presents a challenge because we want to have the full dessert list available, but we don’t want to load all the Reviews and Recipes associated with every dessert. We can solve this with Slices!
Slices are subsets of data. The subset can then be used throughout AppSheet in the same way as a data table. We can create a slice of the dessert table and apply a Row filter condition based on the Search Method chosen by the user. If the user filters by type, the slice will contain all desserts that match that type. If the user chooses to select a single dessert from the full list, the slice will contain only that single dessert.
Filter expression for Dessert Slice
The security filter for Reviews and Recipes will select only those rows where the Dessert ID matches what is in the slice.
Security Filter used on Review and Recipe tables
Updated Table Structure
Auto-select Based on User’s Email
Another variant on this concept is to auto-select a subset of the data when the application loads. A common use case is to filter based on the logged-in user’s email. This is effective in situations where the user only needs to see records with which they are associated.
Example security filter for user’s email
An additional performance improvement can be achieved by filtering child tables to only load data associated with the rows of the parent table that passed the USEREMAIL() filter. This can be done by checking if the Primary Key in the reference column of the child table matches a Primary Key of the filtered parent table. This can be done using an IN() statement such as the one below.
Example IN() statement to filter child table
Check out this article for more information about applying security filters based on user’s email.
As mentioned in the Approaches to Data Scalability article, an app based on spreadsheets will receive limited performance improvement from security filters. This is because the entire spreadsheet must be read before the security filter is applied. When working with spreadsheets, partitioning is a powerful tool for both improving performance and avoiding maximum data size restrictions.
Limitations and Future Functionality
The limitation for horizontal scaling is the interface for updating User Settings. The user must access these settings from the menu. It is possible to set the User Settings view as the home page to prompt the user for an initial selection before entering the app. However, once the initial selection has been made, the user must return to User Settings from the menu to update their selection.
Future functionality is being developed to allow Actions to update the User Settings from within the app. This will enable the app creator to display an inline action button that will update the user settings. The update will trigger AppSheet to fetch the required data so the content can be loaded on demand. With this functionality, app creators will be able to leverage the performance improvement of horizontal scaling and retain a fluid user experience.
The following apps demonstrate the processes described in this article. They are provided to highlight the formulas and structure. The dataset i