Offline Sync Concurrency Issue – Preventing “Last Write Wins” Overwriting Valid State

I’m facing a concurrency and offline synchronization issue in an AppSheet app, and I’d like to understand how others have handled similar scenarios, as well as validate whether my understanding of the platform’s behavior is correct.


App Context

I have an application that tracks keg movements in a brewery. The operational flow follows a well-defined sequence:

  1. Warehouse → In Transit

  2. In Transit → Customer

  3. Customer → Cleaning

  4. Cleaning → Warehouse

Each movement updates the current state of the keg in a physical column, which is updated via a Bot (I chose this approach to avoid using virtual columns due to performance concerns).


Problem

In some scenarios, logistics users perform operations with slow or offline connections. The observed behavior is:

  • User A (logistics) records movement step 3 (Customer → Cleaning), but the app does not sync immediately.

  • Then User B (at the facility) records step 4 (Cleaning → Warehouse) while online — this update reaches the server first.

  • Later, User A’s device syncs, sending the older update (step 3).

Result:

  • AppSheet overwrites the more recent state with an older one.

  • The keg ends up in an incorrect state (e.g., Cleaning instead of Warehouse).


Current Understanding

Based on my analysis, this happens because AppSheet follows a “last write wins” model, without considering:

  • logical process order

  • actual event timestamp

  • concurrency across devices

Additionally, based on observed behavior:

  • When a user performs an action and immediately switches to another app, AppSheet seems to stop syncing and does not continue in the background, delaying the data upload until the app returns to the foreground.

I would also like to confirm whether this understanding is accurate.


Attempts So Far

  • Modeled movement history in a separate table

  • Used a VC with MAXROW() to determine current state → worked, but became very slow at scale

  • Switched to a model with a physical state column updated via Bot for better performance

  • Currently evaluating a hybrid model (history + physical current state)


Questions

I’d like to understand how others are handling this type of scenario:

  1. Is there a recommended approach to handle offline concurrency in AppSheet?

  2. Has anyone implemented version control, event ordering validation, or overwrite prevention in this context?

  3. What strategies work best using:

    • app logic (Actions, Bots, validations, etc.)

    • native AppSheet configurations (e.g., sync settings, security, edit control, locking, etc.)

  4. How can we ensure state consistency without sacrificing performance (avoiding heavy VCs)?

  5. Is there a more robust pattern for handling this type of sequential workflow (e.g., state machine, workflow control)?

  6. Even if it’s not possible to fully prevent these inconsistencies, is there a way to detect and notify when they occur?

    • For example: validations inside Bots, timestamp comparisons, or some kind of automated check that identifies out-of-order operations and triggers alerts?

Notes

This issue mainly occurs in scenarios with:

  • multiple users

  • offline usage

  • concurrent edits to the same record


Any insights, architectural patterns, or real-world experiences would be greatly appreciated!

1 Like

Logbook / Log for each is my approach

A separate table can do the summary of all inventory changes made, with each users time of update.

General rule for me is only 1 user should be updating 1 unique inventory batch.

In your example each inventory status can be its own table. When an inventory row in eg. Inprogress is marked as done, that entire row is copied to the done table, and the inprogress row is deleted. That way people that need to do actions on done inventory can only work on actual done inventory.

Lots of steps, probably a better way to do it in appsheet.

I have a checkin system for locations in another app, writing to the location row by multiple agents doesnt work. So the logbook of those checkins instead is the basis if they actually checked in

Honestly if not, probably real databases with time condition resolution and reconciliation will be whats important

1 Like

Since it seems like the “steps” are order dependent, there shouldn’t be any concurrency issues. User B should NOT even have the capability to perform step 4 until the State of the keg, in the app, indicates that Step 3 has been performed.

Whether these activities are done online or offline shouldn’t matter to the order of the steps.

It sounds like you are deriving the state of the keg. I would recommend NOT doing this with automation. But instead use an action that is activated on the Form Saved so it is done immediately after the row change and BEFORE any other automations might run. In fact, you may even be able to do this with an App Formula on the column depending on the circumstances. If you need to allow for manual override of that state, you can also use “Reset on edit” property to provide a derived State that can be overridden when needed.

This is typically not a problem IF the data model is well structured and the process flow is strictly controlled. Most problems arise when related but independent data is lumped together in a single table row and two people are trying to change DIFFERENT details on the row at the same time - last one in wins. BUT if these details were segregated into different child tables, the two people would be changing info on 2 completely different table rows avoiding a clash altogether.

First, there is no “overwrite prevention”. Last edit in wins.

I’m not sure what the rest might mean except maybe you are referring to controlling the order of operations? That definitely you can do. How depends on the processing requirements but it would heavily rely on Status of the row to determine what can happen next.

This is too broad of a topic to answer here

State is best managed as a user submits updates. That is the intention of App Formulas, to derive result based on user input - as they are inserted. I recognize that apps cannot always handle all managed states in this manner. When it comes to more behind-the-scenes type derived values, you’ll need to evaluate the best approach at that time.

I will say, VC’s do not need to be completely avoided. When it comes to “heavy” VC’s there are usually ways to break up that processing or implement what the VC does in another way, typically as data is inserted, so it doesn’t impact the app performance.

Nothing physically supplied by the platform. Beyond State management, it will depend on the app needs.

To detect an inconsistency, the app has to have the data available to it to do so. I am of the opinion that AppSheet apps can do anything a person can if it has the information to do it.

Prevention is the primary line of defense - don’t allow a user to insert changes until the state says they can.

I hope this helps!

5 Likes

The simple answer is don’t record everything in a single row. Use a child table where each row captures exactly one step.

4 Likes

Thank you!

Your response gave me some insights into how to resolve/minimize this.

2 Likes

I would like to better understand how separating details into different tables changes/solves the concurrency problem.

Suppose two different devices. One is set to airplane mode right after the parent row is syncronized.

The other device, the online, creates a row in the child table referring to the parent. That child record can have its own child added (grandchild of the first) and so on.

Time later, airplane mode device, seeing the original state, adds another child to its row.

Would you control it with a COUNT([Related_children]) <= 1 at the ValidIf?

Thanks for any insight.

It doesn’t SOLVE it, it minimizes the possibility of a clash. If two people are editing the EXACT SAME ROW, all bets are off…last one in wins!

But typically two people are NOT editing the exact same information on that row. That would be inefficient. So separating the data among different tables means each is editing a row is different tables.

This is a DIFFERENT concurrency problem. If I understand correctly, there is no clash here. Instead the app would end up with duplicate rows.

Anytime you have a user who is out of sync with current data in the app, there is a risk of this happening. This is true for ANY software system.

You would either prevent this with process - users operate under certain guidelines to prevent the duplicate records from being inserted. Or, if you know this is a possibility, you identify it afterwards with automation and take action to remove or combine records.

I have this issue in one of my apps and we identify it afterwards and combine/correct records.

2 Likes