In our project we are using hazelcast 3.6.5 as hibernate (4.3.11) second lvl cache.
Here is test which describes problem we are facing:
@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));
}
test is run on application with two nodes. Here is how requests are balanced to different nodes:
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 321node2:
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 617I have found that hazelcast 2nd lvl cache contains two instances of same account. One with old field value 111 and one with new one 222.
First instance is added to cache on node1 on first account insert, and second on user insert on node2 (we have account get call on user insert). As I understand these two account instances in cache causes inconsistency. I don't understand why on second IMapRegionCache.put no entries were found.
Any ideas why this could happen and how to debug this issue?
@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);
}
}
}
private final Serializable key;
private final Type type;
private final String entityOrRoleName;
private final String tenantId;
private final int hashCode;
Problem seems to be in type serialization, which contains session factory ref. private final Serializable key;
private final Type type;
If you need further help , please provide the code for Serializable and Type.
regards
@Override
public Object generateCacheKey(final Object id, final EntityPersister persister,
final SessionFactoryImplementor session, final String tenantIdentifier) {
return id;
}
private final Type type;
field, don't remember exact hibernate implementation.