Google App Engine JDO 使持久延迟

发布于 2024-12-15 20:25:28 字数 6360 浏览 4 评论 0原文

我的 Google App Engine JDO 实现存在问题,我无法弄清楚。文档 (http://code. google.com/intl/sv-SE/appengine/docs/java/datastore/jdo/creatinggettinganddeletingdata.html) 指出“对 makePersistent() 的调用是同步的,并且在保存对象并更新索引之前不会返回。”但我的经验有所不同。

我想将一个对象保存(makePersistent)到数据存储中。保存完成后,我希望能够立即从数据存储中获取它(查询执行)。我知道我不必获取它(因为我已经在内存中拥有该对象),但重点是我希望下一个请求能够从数据存储中检索数据。如果第二个请求足够快,则这不适用于当前的实现。

我注意到的一件奇怪的事情是,如果我尝试在循环中多次从数据存储区获取对象(下面的代码),则对象会很快返回(通常< 10 毫秒)。但是,如果我跳过循环并在 makePersistent 和查询执行之间运行 Thread.sleep(..) 5000 毫秒,则不确定是否找到该对象。这些解决方案都不是我想要的。我希望能够立即获取数据,而无需睡眠或循环。

您可以看到下面访问 DataStoreTestServlet 的代码和结果,包括“等待”找到数据的循环。再说一遍,我不想要循环。

有谁知道我错过了什么?我想这一定是某种东西。我觉得这个实现不合适:)。

我正在使用 appengine-java-sdk-1.6.0。这在本地(开发服务器)和部署在 Google 服务器上时都会出现问题。

这是访问 servlet 的结果。

Created users:
User [password=password, userName=user1321190966416] took 18ms, 2 loop(s)
User [password=password, userName=user1321190966438] took 15ms, 6 loop(s)
User [password=password, userName=user1321190966456] took 2ms, 1 loop(s)
User [password=password, userName=user1321190966460] took 10ms, 5 loop(s)
User [password=password, userName=user1321190966472] took 0ms, 1 loop(s)
User [password=password, userName=user1321190966472] took 0ms, 1 loop(s)
User [password=password, userName=user1321190966472] took 16ms, 1 loop(s)
User [password=password, userName=user1321190966488] took 0ms, 2 loop(s)
User [password=password, userName=user1321190966488] took 0ms, 1 loop(s)
User [password=password, userName=user1321190966488] took 16ms, 1 loop(s)

代码和配置。

jdoconfig.xml

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">
<persistence-manager-factory name="transactions-optional">
<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"/>
<property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" />
</persistence-manager-factory>
</jdoconfig>

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;
    }
}

User.java

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class User {
    @PrimaryKey
    @Persistent
    private String userName;

    @Persistent
    private String password;

    public User(String userName, String password) {
        super();
        this.setUserName(userName);
        this.setPassword(password);
    }

    public String getUserName() {
        return userName;
    }

    public String getPassword() {
        return password;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String toString() {
        return "User [password=" + password + ", userName=" + userName + "]";
    }
}

DataStoreTestServlet.java

import java.io.IOException;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
public class DataStoreTestServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        StringBuffer sb = new StringBuffer();
        sb.append("Created users:\n");

        for (int i = 0; i < 10; i++) {
            String uniqueName = "user" + System.currentTimeMillis();
            User user = new User(uniqueName, "password");
            save(user);

            User userFromDS = null;
            long startTime = System.currentTimeMillis();
            long loop = 0;
            while (userFromDS == null) {
                userFromDS = get(uniqueName);
                loop++;
                if (userFromDS != null) {
                    long endTime = System.currentTimeMillis();
                    sb.append(userFromDS.toString() + " took " + (endTime - startTime) + "ms, " + loop + " loop(s)\n");
                }
            }
        }
        resp.setContentType("text/plain");
        resp.getWriter().println(sb.toString());
    }

    public Object save(Object obj) {
        PersistenceManager pm = PMF.get().getPersistenceManager();
        Object savedObject = null;
        try {
            savedObject = pm.makePersistent(obj);
        } finally {
            pm.close();
        }
        return savedObject;
    }

    public User get(String userName) {
        User user = null;
        List<User> users = null;
        PersistenceManager pm = PMF.get().getPersistenceManager();
        Query query = pm.newQuery(User.class);
        query.setFilter("userName == nameParam");
        query.declareParameters("String nameParam");
        try {
            users = (List<User>) query.execute(userName);
            if (users != null && users.size() > 0) {
                user = users.get(0);
            }
        } finally {
            query.closeAll();
            pm.close();
        }
        return user;
    }
}

I have a problem with the Google App Engine JDO implementation that I cannot figure out. The documentation (http://code.google.com/intl/sv-SE/appengine/docs/java/datastore/jdo/creatinggettinganddeletingdata.html) states "The call to makePersistent() is synchronous, and doesn't return until the object is saved and indexes are updated." but my experience differs.

I want to save (makePersistent) an object into the datastore. When the save is complete I want to be able to fetch it (Query execute) from the datastore immediately. I know that I don't have to fetch it (because I already have the object in memory) but the point is that I want the next request to be able to retrieve the data from the datastore. That is not working with the current implementation if the second request is fast enough.

One strange thing I've noticed is that if I'm trying to fetch the object from the datastore a couple of times in a loop (code below) the object is returned really quick (usually < 10ms). But if I skip the loop and instead run Thread.sleep(..) for 5000 ms between makePersistent and query execute it's not certain that the object is found. None of these solutions are what I want. I want to be able to fetch the data right away without a sleep or loop in between.

The code and the result from accessing the DataStoreTestServlet below is as you can see including the loop that is "waiting" for the data to be found. Again, I don't want the loop.

Does anyone know what I'm missing? I guess it has to be something. This implementation doesn't feel right to me :).

I'm using appengine-java-sdk-1.6.0. This is an issue both locally (development server) and when deployed on the Google servers.

Here's the result from accessing the servlet.

Created users:
User [password=password, userName=user1321190966416] took 18ms, 2 loop(s)
User [password=password, userName=user1321190966438] took 15ms, 6 loop(s)
User [password=password, userName=user1321190966456] took 2ms, 1 loop(s)
User [password=password, userName=user1321190966460] took 10ms, 5 loop(s)
User [password=password, userName=user1321190966472] took 0ms, 1 loop(s)
User [password=password, userName=user1321190966472] took 0ms, 1 loop(s)
User [password=password, userName=user1321190966472] took 16ms, 1 loop(s)
User [password=password, userName=user1321190966488] took 0ms, 2 loop(s)
User [password=password, userName=user1321190966488] took 0ms, 1 loop(s)
User [password=password, userName=user1321190966488] took 16ms, 1 loop(s)

The code and configuration.

jdoconfig.xml

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">
<persistence-manager-factory name="transactions-optional">
<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"/>
<property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" />
</persistence-manager-factory>
</jdoconfig>

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;
    }
}

User.java

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class User {
    @PrimaryKey
    @Persistent
    private String userName;

    @Persistent
    private String password;

    public User(String userName, String password) {
        super();
        this.setUserName(userName);
        this.setPassword(password);
    }

    public String getUserName() {
        return userName;
    }

    public String getPassword() {
        return password;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String toString() {
        return "User [password=" + password + ", userName=" + userName + "]";
    }
}

DataStoreTestServlet.java

import java.io.IOException;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
public class DataStoreTestServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        StringBuffer sb = new StringBuffer();
        sb.append("Created users:\n");

        for (int i = 0; i < 10; i++) {
            String uniqueName = "user" + System.currentTimeMillis();
            User user = new User(uniqueName, "password");
            save(user);

            User userFromDS = null;
            long startTime = System.currentTimeMillis();
            long loop = 0;
            while (userFromDS == null) {
                userFromDS = get(uniqueName);
                loop++;
                if (userFromDS != null) {
                    long endTime = System.currentTimeMillis();
                    sb.append(userFromDS.toString() + " took " + (endTime - startTime) + "ms, " + loop + " loop(s)\n");
                }
            }
        }
        resp.setContentType("text/plain");
        resp.getWriter().println(sb.toString());
    }

    public Object save(Object obj) {
        PersistenceManager pm = PMF.get().getPersistenceManager();
        Object savedObject = null;
        try {
            savedObject = pm.makePersistent(obj);
        } finally {
            pm.close();
        }
        return savedObject;
    }

    public User get(String userName) {
        User user = null;
        List<User> users = null;
        PersistenceManager pm = PMF.get().getPersistenceManager();
        Query query = pm.newQuery(User.class);
        query.setFilter("userName == nameParam");
        query.declareParameters("String nameParam");
        try {
            users = (List<User>) query.execute(userName);
            if (users != null && users.size() > 0) {
                user = users.get(0);
            }
        } finally {
            query.closeAll();
            pm.close();
        }
        return user;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

べ繥欢鉨o。 2024-12-22 20:25:28

尝试在 jdoconfig.xml 中添加以下内容:

<property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" />

为了提高性能;应用程序引擎数据存储区“最终一致”。这意味着当您创建新对象或更改现有对象时,它不会立即显示出来;允许在查找时间内提高性能。与此相反的是“强”一致性,这意味着每个请求都是使用数据存储中的最新数据发出的。

现在,根据有关此内容的应用引擎文档,强烈一致是默认值,您必须显式设置最终一致性。但是,根据我的观察,您必须设置强一致性,并且 EVENTUAL 是默认值(也许是错误?)。因此,尝试将上述内容添加到您的 jdoconfig xml 中,如果您观察到与我所做的相同的事情,那么我可能会针对应用程序引擎打开一个错误,假设尚未针对此问题打开一个错误。

您必须记住的一件事是,如果您设置“强一致性”,那么您的性能将会受到影响。我设置它只是因为我的界面的某些部分变得混乱,因为我会使用不太新鲜的数据构建其中的一部分,并且在同一请求期间会使用新数据构建另一部分;使我的界面不一致。这可能是解决问题的一种广泛方法;但它有效:)。

Try adding this in your jdoconfig.xml:

<property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" />

To increase performance; the app engine datastore is "eventually consistent". This means that when you make new objects or alter existing ones, it doesn't show up right away; allowing for performance gains in lookup times. The opposite of this is "STRONG" consistency, meaning every request is made using the most recent data in the datastore.

Now, according to the app engine documentation for this STRONG consistent is the default, and you have to explicitly set eventual consistency. But, from what I've observed, you have to set STRONG consistency and EVENTUAL is the default (bug maybe?). So try adding the above to your jdoconfig xml and if you observe the same thing I did then I'll probably open a bug against app engine, assuming one hasn't been opened already for this issue.

The one thing you're going to have to keep in mind is that if you set STRONG consistency, you're going to take a performance hit. I only set it because there were parts of my interface that were getting messed up because I would build a piece of it with not so fresh data, and during the same request another piece would get built with fresh data; making my interface inconsistent. This might be a broad approach to fixing the problem; but it works :).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文