Objectbox-java: Update opreation is not atomic, a "pure update" is wanted

Created on 28 Jul 2020  路  8Comments  路  Source: objectbox/objectbox-java

Issue Basics

  • ObjectBox version (are using the latest version?): 2.5.1
  • Reproducibility: [always]

Reproducing the bug

Description

Update Opreation:

Box<User> testBox = App.getInstance().getBoxStore().boxFor(User.class);
User user = testBox.get(1);
user.setName("newName");
testBox.put(test);

Update opreation is NOT atomic.
If I delete data in one thread and update data in another thread then the deleted data might be save again

All 8 comments

You are asking for transactions?

You are asking for transactions?

No.The transactions can not fit.

for(User user: allUsers) {
   modify(user); //modifies user may take some time,what if the user has been deleted during modifying?
}
box.put(allUsers);//this will save the deleted user again

I see two basic approaches:

  1. Wrap the loop and the put inside a write transaction. BUT, as you wrote the modification takes some time, this is probably not what you want, as write transactions should be short lived.

  2. Unwrap the put of a list to a loop, which is executed in a write transaction. In the loop check if the element still exists.

for(User user: allUsers) {
   modify(user); //modifies user may take some time
}

boxStore.runInTx(() -> {
  for(User user: allUsers) {
    User existing = box.get(user.getId()); // null if deleted
    if(existing != null) box.put(user);
  }
}

Solution 2 sounds great,thanks for your advice.
AFAIK,The transactions is used to ensure the whole wrapped block is atomic.Is it thread safe?

boxStore.runInTx(() -> {
  for(User user: allUsers) {
    User existing = box.get(user.getId()); // not null here
    //current thread got paused and this user was deleted by other thread
    //what will happen when current thread got resumed?
    if(existing != null) box.put(user);
  }
}

ObjectBox in itself is intended to be tread safe, so are transactions. There are a couple of rules, e.g.:

  • Transactions are tied to threads

  • There can be a single write transaction at any time (consider it a lock)

  • Read transactions never get blocked or block a write transaction

Thus, you could use write transactions to enforce code to run strictly sequentially.

P.S.: the entire code block passed to runInTx() runs inside a write transaction; there's no way another write transaction can interfere until it's done.

Thanks for all of this.

Update: 2.7.0 adds Box.contains(id) which is less expensive than Box.get() != null.

Anyhow, is this a bug with put(entity)? Shouldn't it do nothing or error if putting an entity with non-zero ID where that entity does not (longer) exist? (Assuming @Id is left at default auto-assigned.)

A put() always puts; so "works as intended" :smile:. However, at core and C level, we actually have put modes (e.g. update/insert) for finer control. At some time we'll bubble that up to Java...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DJafari picture DJafari  路  22Comments

livotov picture livotov  路  17Comments

GerritDeMeulder picture GerritDeMeulder  路  20Comments

LeeKingHung picture LeeKingHung  路  35Comments

greenrobot picture greenrobot  路  27Comments