在我们的项目中,我们使用hazelcast 3.6.5作为hibernate(4.3.11)第二个lvl缓存。
以下是描述我们面临的问题的测试:
@Test
public void cacheTest() {
// Step 1: create account with someValue=111
Account account = account().withName("test hz account").withSomeValue(111).build();
account = restClient.upsertAccount(account);
//Step 2: create user with ref to account
User user = user().withAccount(account).build();
restClient.upsertUser(user);
//Step 3: update account someValue to 222
account.setSomeValue(222);
restClient.upsertAccount(account);
//search users by account id
List<User> users = restClient.searchUsersByAccountId(BRAND, account.getId());
assertThat(users.size(), is(1));
//assertion below fails, user.account.someValue=111
assertThat(users.get(0).getAccount().getSomeValue(), is(222));
}
测试在具有两个节点的应用程序上运行。以下是请求与不同节点的平衡方式:
node1 :
172.20.0.1 - - [12:30:46 +0200] "POST /accounts/BRAND/ HTTP/1.1" 200 321
172.20.0.1 - - [12:30:47 +0200] "POST /accounts/BRAND/ HTTP/1.1" 200 321
node2 :
172.20.0.1 - - [12:30:47 +0200] "POST /users/BRAND/ HTTP/1.1" 200 542
172.20.0.1 - - [12:30:48 +0200] "POST /users/BRAND/search?start=0&limit=1 HTTP/1.1" 200 617
我发现hazelcast 2nd lvl cache包含两个相同帐户的实例 。一个具有旧字段值111,一个具有新字段值222。
首先将实例添加到第一个帐户插入的缓存中,第二个实例添加到用户插入(我们在用户插入时调用帐户)。据我所知,缓存中的这两个帐户实例会导致不一致。
为什么会发生这种情况以及如何调试此问题?
这是数据模型:
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "accounts")
@Cache(usage = READ_WRITE)
public class Account extends BrandIdEntity {
@Column(name = "name")
private String name;
@Column(name = "some_value")
private Integer someValue;
}
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "users")
@Cache(usage = READ_WRITE)
public class User extends BrandIdEntity {
@Column(name = "account_id")
private String accountId;
@ManyToOne(cascade = ALL)
@JoinColumns({
@JoinColumn(name = "brand", nullable = false, insertable = false, updatable = false),
@JoinColumn(name = "account_id", nullable = false, insertable = false, updatable = false) })
private Account account;
}
@EqualsAndHashCode(of = "pk", callSuper = false)
@MappedSuperclass
public abstract class BrandIdEntity extends AuditedEntity<BrandIdPK> {
@EmbeddedId
private final BrandIdPK pk;
public BrandIdEntity() {
super();
pk = new BrandIdPK();
}
public BrandIdEntity(BrandType brand, String id, IdGenType idGenType) {
super();
pk = new BrandIdPK(brand, id, idGenType);
}
}
@Embeddable
@Getter
@Setter
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class BrandIdPK extends BaseObject {
@Column(name = "id")
private String id;
@Column(name = "brand")
private String brand;
public BrandIdPK(BrandType brand, String id, IdGenType idGenType) {
this.brand = brand.name();
if (id != null) {
this.id = id;
}
else {
this.id = next(idGenType);
}
}
}
这里是hazelcast map config:
{
name='com.project.data.Account',
inMemoryFormat='BINARY',
backupCount=1,
asyncBackupCount=0,
timeToLiveSeconds=300,
maxIdleSeconds=0,
evictionPolicy='LRU',
evictionPercentage=25,
minEvictionCheckMillis=100,
maxSizeConfig=MaxSizeConfig{maxSizePolicy='PER_NODE',size=100000},
readBackupData=true,
hotRestart=HotRestartConfig{enabled=false,fsync=false},
nearCacheConfig=null,
mapStoreConfig=MapStoreConfig{
enabled=false,className='null',factoryClassName='null',writeDelaySeconds=0,writeBatchSize=1,
implementation=null,factoryImplementation=null,properties={},
readOnly=null,initialLoadMode=LAZY,writeCoalescing=true
},
mergePolicyConfig='com.hazelcast.map.merge.PutIfAbsentMapMergePolicy',
wanReplicationRef=null,
entryListenerConfigs=[],
mapIndexConfigs=[],
mapAttributeConfigs=[],
quorumName=null,
queryCacheConfigs=[],
cacheDeserializedValues=INDEX_ONLY
}