Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cache inconsistency - Entity not always persisted in cached Collection

I'm having an issue where a Validation instance is added to a Collection on a Step instance. Declaration is as follows:

Step class:

@Entity
@Table
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Step extends AbstractEntity implements ValidatableStep {

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "step_id", nullable = false)
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private Set<Validation> validations = new HashSet<>();

    @Override
    public void addValidation(Validation validation) {
      // do some stuff
      ...
      // add validation instance to collection
      getValidations().add(validation);
    }

}

Validation class:

@Entity
@Table
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Validation extends AbstractEntity {
  //some properties
}

Both classes are Cacheable with a READ_WRITE strategy applied. The unidirectional Collection of Validations are also cached with same strategy.

One would expect when a read-write transaction that invokes addValidation(new Validation('userName')); commits, the new Validation would be visible in a subsequent read-only transaction. The weird thing is that sometimes it does work and sometimes it doesn't work...

The first transaction always succeeds; we see the new validation being persisted in database and Step's version property (for optimistic locking puposes) getting incremented. But sometimes, the 2nd read transaction contains a Step instance with an empty Validation Collection...

Our Hibernate caching config is as follows:

hibernate.cache.use_second_level_cache = true
hibernate.cache.use_query_cache = true
hibernate.cache.region.factory_class = org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
hibernate.cache.provider_configuration_file_resource_path = classpath:ehcache.xml
net.sf.ehcache.hibernate.cache_lock_timeout = 10000

Any idea what's causing this weird (and random) behavior?

like image 288
user2054927 Avatar asked Jun 19 '15 13:06

user2054927


1 Answers

The Hibernate Collection Cache always invalidates existing entries and both the Entity and the Collection caches are sharing the same AbstractReadWriteEhcacheAccessStrategy, so a soft-lock is acquired when updating data.

Because you are using a unidirectional one-to-many association, you will end up with a Validation table and a Step_validation link table too. Whenever you add/remove a Validation you have to hit two tables and that's less efficient.

I suggest you adding the @ManyToOne side in the Validation entity and turn the @OneToMany side into a mapped-by collection:

@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "step")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Validation> validations = new HashSet<>();
like image 198
Vlad Mihalcea Avatar answered Sep 17 '22 13:09

Vlad Mihalcea