5

Today it's the first time I'm using GWT and JDO. I am running it with Eclipse in the local debug mode.

I do the following thing:

    public Collection<MyObject> add(MyObject o) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
    pm.makePersistent(o);
    Query query = pm.newQuery(MyObject.class);// fetch all objects incl. o. But o only sometimes comes...
List<MyObject> rs = (List<MyObject>) query.execute();
ArrayList<MyObject> list= new ArrayList<MyObject>();
for (MyObject r : rs) {
    list.add(r);
}
return list; 
} finally {
    pm.close();
}
}

I already set <property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" /> in my jdoconfig.xml. Do I have to set some other transaction stuff in the config? Was somebody got a working jdoconfig.xml? Or is the problem somewhere else? Some caching inbetween?

EDIT: Things I have tried:

  • Setting NontransactionalRead/Write to false
  • Using the same/a different PersistenceManager though calling PMF.get().getPersistenceManager() multiple times
  • Using transactions
  • ignoreCache = true on PersistenceManager
  • calling flush and checkConsistency

The jdoconfig:

    <persistence-manager-factory name="transactions-optional">
<property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" />
    <property name="javax.jdo.PersistenceManagerFactoryClass"
        value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
    <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
    <property name="javax.jdo.option.NontransactionalRead" value="true"/>
    <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
    <property name="javax.jdo.option.RetainValues" value="true"/>
    <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
</persistence-manager-factory>

I must be missing something central here because all approaches fail...

EDIT2: When I split the job into two transaction the log says that the write transaction fished and then the read transaction starts. But it doesn't find the just persited object. It always says Level 1 Cache of type "weak" initialised aswell. Is week bad or good?

It about 30% of requests that go wrong... Might I be some lazy query loading issue?

Franz Kafka
  • 10,623
  • 20
  • 93
  • 149
  • 1
    I also face same problem. I add some objects through my app, but they don't reflect in results, sometimes, and sometimes they do! – Vishal Biyani Dec 17 '11 at 21:53
  • and the object is in the datastore at the time of doing the query ? Obviously the log would tell you that. What this has to do with GWT is unclear ... – DataNucleus Dec 18 '11 at 09:56
  • Where do I find a log? What it has to do with GWT? Alot, because it is a GWT project. I can't really pull a clear line between GWT and the app-engine therefore I call everything GWT that comes from google. And why would the caching be so messed up that the same instance can't even read data just written? Could you post a jdoconfig.xml which will always be consistent for the same client? – Franz Kafka Dec 19 '11 at 01:58
  • So many questions ans so little documentation. I hope you can lead me on the right path... Thanks – Franz Kafka Dec 19 '11 at 02:04
  • You should delete this: http://stackoverflow.com/questions/8563654/datanucleus-app-engine-jdo-are-writes-cached/8564113 – Dave Dec 19 '11 at 19:52
  • Where do you find a log ? DataNucleus log of course, since that is what Googles plugin is using. Little documentation ? You mean the JDO spec, DataNucleus project documentation to name but two places. I already asked you to check that you had actually written the data. And why I ask about GWT is simply because you bother to narrow a problem down to limited lines of code and there is *nothing* GWT in those, hence the problem seems NOT GWT – DataNucleus Dec 19 '11 at 20:30

3 Answers3

2

Calling the makePersistent() method doesn't write to the datastore; closing the PersistenceManager or committing your changes does. Since you haven't done this when you run your query, you're getting all objects from the datastore which does not, yet, include the object you just called makePersistent on.

Read about object states here: http://db.apache.org/jdo/state_transition.html

There are two ways around this, you can put this inside a transaction since the commit writes to the datastore (keep in mind GAE 5 transaction/entity type limit on transactions) and commit before running your query; Example using transaction...

public Collection<MyObject> add(MyObject o) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    ArrayList<MyObject> list = null;
    try {
        Transaction tx=pm.currentTransaction();
        try {
            tx.begin();
            pm.makePersistent(o);
            tx.commit(); 
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }

        Query query = pm.newQuery(MyObject.class);
        List<MyObject> rs = (List<MyObject>) query.execute();
        ArrayList<MyObject> list = new ArrayList<MyObject>();
        for (MyObject r : rs) {
            list.add(r);
        }
    } finally {
        pm.close();
    }

    return list; 
}

or you could close the persistence manager after calling makePersistent on o and then open another one to run your query on.

// Note that this only works assuming the makePersistent call is successful
public Collection<MyObject> add(MyObject o) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        pm.makePersistent(o);
    } finally {
        pm.close();
    }

    pm = PMF.get().getPersistenceManager();
    ArrayList<MyObject> list = null;
    try {

        Query query = pm.newQuery(MyObject.class);
        List<MyObject> rs = (List<MyObject>) query.execute();
        list= new ArrayList<MyObject>();
        for (MyObject r : rs) {
            list.add(r);
        }

    } finally {
        pm.close();
    }

    return list; 
}

NOTE: I originally said you could just add o to the result list before returning; but that isn't a smart thing to do since in the event that there is a problem writing o to the datastore; then the returned list wouldn't reflect the actual data in the datastore. Doing what I now have (committing a transaction or closing the pm and then getting another one) should work since you have your datastoreReadPolicy set to STRONG.

Dave
  • 6,141
  • 2
  • 38
  • 65
  • 2
    I tried both approaches and wrapping read and write in two different transactions. Sometimes it works and sometimes it doesn't, this is seriously messed up. I cleaned the project, downgraded to jdk6 now I'm out of options. I even call flush and constancyCheck after committing, but it all doesn't help. Sometimes the data just doesn't get into the datastore fast enough. Your earlier idea with adding the new object to the results manually. I did that, but then I sometimes get problems one request later, because that value still isn't in the store. A browser refresh does the trick then (mostly?). – Franz Kafka Dec 20 '11 at 00:06
  • Are you doing any kind of caching; either with some kind of Map or through the Memcache api? – Dave Dec 21 '11 at 19:09
2

Franz, the Default read consistency in the JDO Config is STRONG. so if you are trying to approach it in that direction, it wont lead you anywhere

Check this out as i think it mentions something similar to the scenario which you are encountering, with the committed data not returned back in the query. It isnt concurrent as mentioned, but it explains the commit process.

http://code.google.com/appengine/articles/transaction_isolation.html

Also, another approach would be to query using Extents and find out if that solves the particular use case you are looking at, since i believe you are pulling out all the records in the table.

EDIT :

Since in the code snippet that you have mentioned, it queries the entire table. And if that is what you need, you can use an Extent... The way to use it is by calling

Extent ext = getExtent(<Entity Class name>)

on the persistenceManager singleton object. You can then iterate through the Extent

Check out the documentation and search for Extents on the page here. http://code.google.com/appengine/docs/java/datastore/jdo/queries.html

Hrishikesh
  • 2,033
  • 1
  • 16
  • 26
  • Okay it says when commit returns it is not guranteed that the changes are visible (who would invent such a stupid concept?). Now the question is: how can I force visiblity? Or can I do some tricks with my where clause, because you said something about querying the whole table? – Franz Kafka Dec 20 '11 at 01:56
  • 1
    Updated the comment about on usage of Extents. Also, here is an explanation of Caching in JDO. It might be of use. http://book.javanb.com/using-and-understanding-java-data-objects/LiB0046.html – Hrishikesh Dec 20 '11 at 02:56
  • An Extent of a class and a Query of a class are the exact same thing FWIW – DataNucleus Dec 20 '11 at 08:52
  • 1
    @Hrishikesh, I believe even though the documentation says the default is STRONG that it is not actually STRONG. See this question... http://stackoverflow.com/questions/8112331/google-app-engine-jdo-makepersistent-latency/8112392#8112392 and this issue... http://code.google.com/p/googleappengine/issues/detail?id=6326&thanks=6326&ts=1321204205 This doesn't relate to the core of your answer (transaction isolation) but is just a side note worth mentioning. – Dave Dec 20 '11 at 16:26
1

I encountered the same problem and this didn't help. Since it seems to be the top result on Google for "jdo app engine consistency in eclipse" I figured I would share the fix for me!

Turns out I was using multiple instances the PersistenceManagerFactory which led to some bizarre behaviour. The fix is to have a singleton that every piece of code accesses. This is in fact documented correctly on the GAE tutorials but I think it's importance is understated.

Getting a PersistenceManager Instance

An app interacts with JDO using an instance of the PersistenceManager class. You get this instance by instantiating and calling a method on an instance of the PersistenceManagerFactory class. The factory uses the JDO configuration to create PersistenceManager instances.

Because a PersistenceManagerFactory instance takes time to initialize, an app should reuse a single instance. An easy way to manage the PersistenceManagerFactory instance is to create a singleton wrapper class with a static instance, as follows:

PMF.java

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

    public final class PMF {
        private static final PersistenceManagerFactory pmfInstance =
            JDOHelper.getPersistenceManagerFactory("transactions-optional");

        private PMF() {}

        public static PersistenceManagerFactory get() {
            return pmfInstance;
        }
    }
RedSpikeyThing
  • 304
  • 2
  • 3
  • While this solves many other problems, this didn't resolve the issues faced by the original poster (be as well). The devserver admin interface also doesn't properly reflect data changes. If I leave everything just sit for a while, _sometimes_ I will see things persist and become available. It feels random, although I'm sure there is something else going on. – BK- Feb 19 '15 at 23:02