【问题标题】:Google App Engine JDO makePersistent latencyGoogle App Engine JDO makePersistent 延迟
【发布时间】:2011-12-28 01:54:56
【问题描述】:

我无法弄清楚 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;
    }
}

用户.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;
    }
}

【问题讨论】:

    标签: google-app-engine persistence jdo latency


    【解决方案1】:

    尝试在你的 jdoconfig.xml 中添加这个:

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

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

    现在,根据app engine documentation for thisSTRONG 一致是默认值,您必须明确设置最终一致性。但是,据我观察,您必须设置 STRONG 一致性,而 EVENTUAL 是默认设置(可能是错误?)。因此,尝试将上述内容添加到您的 jdoconfig xml 中,如果您观察到与我所做的相同的事情,那么我可能会针对应用引擎打开一个错误,假设尚未针对此问题打开一个错误。

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

    【讨论】:

    • 感谢您的回复。我添加了属性,但没有任何变化。我仍然需要循环几次尝试获取数据才能成功:(。很奇怪。
    • 当我第一次发布这个答案时,我不小心发布了&lt;property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /&gt;,但它应该是&lt;property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" /&gt;(注意STRONG,而不是EVENTUAL)。这是我的错;但是您确定您尝试过使用STRONG 设置吗?
    • 呵呵,是的,我用 STRONG 设置试过了。我还在查询中尝试了以下内容。 query.addExtension("datanucleus.appengine.datastoreReadConsistency", "STRONG");还是同样的问题。
    • 我唯一的另一个猜测是,因为你在同一个请求上做这一切,持久性管理器正在做一些事情(即 - 实际上直到 http 请求通过一些内部应用程序引擎 servlet 过滤器)。所以,也许试试你的实际应用;或重写您的测试 servlet 以保存一次,然后重定向到自身并检查您的项目是否保持正常。
    • 感谢提示,它可能基于请求。好像是。在 Google 服务器上部署时 有效。它仍然无法在本地工作,但这不是一个大问题。就像您说的那样,必须将属性设置为 STRONG 似乎是一个错误。文档说明默认情况下它应该是 STRONG,但显然不是。
    猜你喜欢
    • 2012-01-25
    • 1970-01-01
    • 2012-02-21
    • 2017-04-29
    • 1970-01-01
    • 2010-12-11
    • 2011-06-19
    • 1970-01-01
    • 2012-04-13
    相关资源
    最近更新 更多