How to make database lock in AppEngine (GAE)?

In GAE, I have a table full of “one pass” - things like “last used sequence number”, etc. that really don't get into other tables. This is a simple String key with a pair of String-value.

I have code to capture a named integer and increment it, for example:

@PersistenceCapable(detachable="true") public class OneOff { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String dataKey; @Persistent private String value; public OneOff(String kk, String vv) { this.dataKey = kk; this.value = vv; } public static OneOff persistOneOff(String kk, String vv) { OneOff oneoff= new OneOff(kk, vv); PersistenceManager pm = PMF.get().getPersistenceManager(); try { pm.makePersistent(oneoff); } finally { pm.close(); } return oneoff; } // snip... @SuppressWarnings("unchecked") synchronized public static int getIntValueForKeyAndIncrement(String kk, int deFltValue) { int result = 0; OneOff oneOff = null; PersistenceManager pm = PMF.get().getPersistenceManager(); Query query = pm.newQuery(OneOff.class); query.setFilter("dataKey == kkParam"); query.declareParameters("String kkParam"); List<OneOff> oneOffs = (List<OneOff>) query.execute(kk); int count = oneOffs.size(); if (count == 1) { oneOff = oneOffs.get(0); result = Integer.parseInt(oneOff.value); } else if (count == 0) { oneOff = new OneOff(kk, "default"); result = deFltValue; } else { // Log WTF error. } // update object in DB. oneOff.value = "" + (result+1); try { pm.makePersistent(oneOff); } finally { pm.close(); } return result; } // etc... 

However, when I make these calls:

 int val1 = OneOff.getIntValueForKeyAndIncrement("someKey", 100); int val2 = OneOff.getIntValueForKeyAndIncrement("someKey", 100); int val3 = OneOff.getIntValueForKeyAndIncrement("someKey", 100); 

Sometimes I get the desired gain, and sometimes I get the same value. It seems that my access to the database works asynchronously when I would like to lock the database for this particular transaction.

I thought that

  synchronized public static 

should have done this for me, but apparently not (perhaps due to running multiple instances!)

Anyway, how do I do what I want? (I want to lock my DB while I receive and update this value to do all concurrency -safe.)

Thanks!

== EDIT ==

I accepted Robert as the correct answer, since the transactions were, indeed, what I wanted. However, for completeness, I added an updated code below. I think this is correct, although I'm not sure about the if(oneOff==null) (try-catch bit).

 public static int getIntValueForKeyAndIncrement(String kk, int defltValue) { int result = 0; Entity oneOff = null; int retries = 3; // Using Datastore Transactions DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); while (true) { com.google.appengine.api.datastore.Transaction txn = datastore.beginTransaction(); try { Key oneOffKey = KeyFactory.createKey("OneOff", kk); oneOff = datastore.get (oneOffKey); result = Integer.parseInt((String) oneOff.getProperty("value")); oneOff.setProperty("value", "" + (result+1)); datastore.put(oneOff); txn.commit(); break; } catch (EntityNotFoundException ex) { result = defltValue; } catch (ConcurrentModificationException ex) { if (--retries < 0) { throw ex; } } if (oneOff == null) { try { Key oneOffKey = KeyFactory.createKey("OneOff", kk); oneOff = new Entity(oneOffKey); oneOff.setProperty("value", "" + (defltValue+1)); datastore.put(txn, oneOff); datastore.put(oneOff); txn.commit(); break; } finally { if (txn.isActive()) { txn.rollback(); } } } else { if (txn.isActive()) { txn.rollback(); } } } return result; } 
+6
source share
1 answer

You must update your values ​​inside the transaction . Operations with App Engine will prevent overwriting two updates if your reads and writes are in the same transaction. Be sure to pay attention to the discussion of entity groups.

+4
source

Source: https://habr.com/ru/post/894423/


All Articles