【问题标题】:Does setDefaultHighRepJobPolicyUnappliedJobPercentage(100) really work?setDefaultHighRepJobPolicyUnappliedJobPercentage(100) 真的有效吗?
【发布时间】:2015-01-01 15:54:23
【问题描述】:

根据https://cloud.google.com/appengine/docs/java/tools/localunittesting#Writing_HRD_Datastore_Tests,“如果您的应用程序使用高复制数据存储 (HRD),您可能需要编写测试来验证您的应用程序在最终一致性方面的行为。LocalDatastoreServiceTestConfig 公开了使这变得容易的选项。”您应该设置 setDefaultHighRepJobPolicyUnappliedJobPercentage(100) 然后,“通过将未应用的作业百分比设置为 100,我们指示本地数据存储以最大数量的最终一致性运行。最大最终一致性意味着写入将提交但始终无法应用,因此全局(非祖先)查询将始终无法看到更改。”

但是,我认为setDefaultHighRepJobPolicyUnappliedJobPercentage(100) 不起作用。

如果是这样,那么我下面的测试用例 testEventualConsistency() 应该通过,但它在第二个断言上失败。在第一个断言中,我读回了使用 Objectify 祖先()查询保存的对象。它按记录工作,因为检索了对象。但是,第二个断言失败了。在那个断言中,我还读回了我保存的对象,但我没有使用 Objectify 祖先()查询,所以它不应该检索任何东西,因为我已经指定不应该完成任何作业(即 setDefaultHighRepJobPolicyUnappliedJobPercentage(100)设置)。

EventualConsistencyTest 测试用例

import static com.googlecode.objectify.ObjectifyService.begin;
import static com.googlecode.objectify.ObjectifyService.ofy;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import java.util.List;

import org.junit.Test;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Ref;
import com.googlecode.objectify.util.Closeable;
import com.netbase.followerdownloader.model.DownloadTask;
import com.netbase.followerdownloader.model.User;

public class EventualConsistencyTest {
    private final LocalServiceTestHelper helper =
            new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
                .setDefaultHighRepJobPolicyUnappliedJobPercentage(100));    

    @Test
    public void testEventualConsistency() {
        helper.setUp();
        ObjectifyRegistrar.registerDataModel();

        User user = new User();
        user.id = 1L;
        Closeable closeable1 = begin();
        ofy().save().entity(user);
        closeable1.close();

        Closeable closeable2 = begin();
        DownloadTask downloadTask = new DownloadTask();
        downloadTask.owner = Ref.create(user);
        ofy().save().entity(downloadTask);
        closeable2.close();

        Closeable closeable3 = ObjectifyService.begin();
        List<DownloadTask> downloadTasks1 = ofy().load().type(DownloadTask.class).ancestor(user).list();
        assertThat(downloadTasks1.size(), equalTo(1));
        closeable3.close();

        Closeable closeable4 = ObjectifyService.begin();
        List<DownloadTask> downloadTasks2 = ofy().load().type(DownloadTask.class).list();
        assertThat(downloadTasks2.size(), equalTo(0));  //  THIS SHOULD PASS IF setDefaultHighRepJobPolicyUnappliedJobPercentage(100) WORKED
        closeable4.close();

        helper.tearDown();
    }

}

用户定义

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;

@Entity
public class User {
    @Id public Long id;

    public User () {

    }
}

下载任务定义

import com.googlecode.objectify.Ref;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Parent;

@Entity
public class DownloadTask {
    @Id public Long id;

    @Parent public Ref<User> owner;

    public DownloadTask() {

    }
}

环境:

  • appengine-api-1.0-sdk-1.9.17.jar
  • appengine-testing-1.9.17.jar
  • appengine-api-stubs-1.9.17.jar
  • junit-4.11.jar
  • objectify-5.1.3.jar

如果我错过了其他重要的事情,这里有一个更详尽的清单:

我的问题是:

  1. setDefaultHighRepJobPolicyUnappliedJobPercentage(100) 坏了吗?

  2. setDefaultHighRepJobPolicyUnappliedJobPercentage(100) 真的不像文档中那样工作吗?即使文档上说不应该这样做,它实际上是否应用了这项工作?

  3. 传递给setDefaultHighRepJobPolicyUnappliedJobPercentage() 的值是否真的应该是100 而不是1.0f

  4. Objectify 祖先查询是否真的不像记录的那样工作?

【问题讨论】:

  • 您可能已经违反了观察“请记住,本地 High Replication 读取一致性模型是生产 High Replication 读取一致性模型的近似值,而不是精确的副本。在本地环境中,对属于具有未应用写入的实体组的实体执行 get() 将始终使未应用写入的结果对后续全局查询可见。在生产中,情况并非如此。在cloud.google.com/appengine/docs/java/tools/…
  • 您可以通过使用单独的测试方法而不是单个测试方法中的连续操作来避免测试相互影响,尤其是相互干扰。
  • 谢谢@AlexMartelli。如果我明白你在说什么,干扰是我的 Objectify 祖先查询导致我的非祖先查询看到一个我认为它不应该看到的对象。这是否意味着 Objectify 祖先查询与您提供的链接中提到的“属于实体组的实体的 get()”相同,而非祖先查询是“全局查询”。
  • 我不是 Objectify 专家,但在我看来,不可避免地,它(或 gae 数据存储周围的任何其他包装器)必须发出低级 get 请求——有祖先(==实体组)或没有(==全局)。
  • @AlexMartelli,谢谢。我尝试了一些支持你理论的东西。我删除了祖先查询(==实体组获取请求)并且非祖先查询(==全局查询)按预期行事 - 即assertThat(downloadTasks2.size(), equalTo(0)); 已通过。如果您想编写一个引用“在本地环境中,对属于具有未应用写入的实体组的实体的 get() 执行 get() 将始终使未应用写入的结果对后续全局查询可见”的答案。我会接受你的回答。

标签: java google-app-engine google-cloud-datastore objectify


【解决方案1】:

https://cloud.google.com/appengine/docs/java/tools/localunittesting#Java_Writing_High_Replication_Datastore_tests 的观察解释了该问题: “在本地环境中,对属于具有未应用写入的实体组的实体执行 get() 将始终使未应用写入的结果对后续全局查询可见。”

在这种情况下,这意味着祖先查询:

List<DownloadTask> downloadTasks1 = ofy().load().type(DownloadTask.class).ancestor(user).list();

在内部“执行属于具有未应用写入的实体组的Entityget()”会影响紧随其后的全局查询的行为:

List<DownloadTask> downloadTasks2 = ofy().load().type(DownloadTask.class).list();

为避免您的测试相互影响,尤其是以这种方式相互干扰,最好对每个被测操作使用单独的方法(每个操作都包含所有需要的设置和拆卸部分),而不是让单个测试方法中的连续被测操作。

【讨论】:

  • 再次感谢@AlexMartelli。新年快乐。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-16
  • 1970-01-01
  • 1970-01-01
  • 2011-04-12
相关资源
最近更新 更多