Jakub Łaba
Jakub Łaba

Reputation: 1

Spring boot caching + Caffeine with custom expiration logic

I am doing a sample project for managing some generic background jobs, mainly to practice how to work with cache in Spring Boot. My key problem is managing retention time for jobs, based on their state.

This is the job class

@Slf4j
@RequiredArgsConstructor
@Getter
public class Job implements Runnable {

  private final UUID uuid;
  private State state = State.READY;

  @Override
  public void run() {
    log.info("Starting job {}", uuid);
    state = State.RUNNING;
    try {
      Thread.sleep(1_000L * 60L);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      log.error(e.getMessage());
      state = State.INTERRUPTED;
      return;
    }
    state = State.COMPLETED;
  }

  public enum State {
    READY,
    RUNNING,
    COMPLETED,
    INTERRUPTED
  }
}

I have created this Caffeine<UUID, Job> bean, specifying my retention logic:

@Bean
public Caffeine<UUID, Job> caffeine() {
  return Caffeine.newBuilder()
      .expireAfter(new Expiry<>() {
        @Override
        public long expireAfterCreate(UUID uuid, Job job, long currentTime) {
          return job.getState() == State.READY ? TimeUnit.DAYS.toNanos(3) : 0;
        }

        @Override
        public long expireAfterUpdate(UUID uuid, Job job, long currentTime,
            @NonNegative long currentDuration) {
          return TimeUnit.DAYS.toNanos(
              switch (job.getState()) {
                case READY, INTERRUPTED -> 3;
                case RUNNING, COMPLETED -> 1;
              }
          );
        }

        @Override
        public long expireAfterRead(UUID uuid, Job job, long currentTime,
            @NonNegative long currentDuration) {
          return expireAfterUpdate(uuid, job, currentTime, currentDuration);
        }
      });
}

The issue arises when I want to integrate it with CaffeineCacheManager. CaffeineCacheManager#setCaffeine requires Caffeine<Object, Object>. I am not even allowed to cast it:

@Bean
public CacheManager cacheManager(@Value("${spring.cache.cache-names}") String... cacheNames) {
  var cacheManager = new CaffeineCacheManager(cacheNames);
  cacheManager.setCaffeine((Caffeine<Object, Object>) caffeine());
  return cacheManager;
}

inconvertible types; cannot cast 'com.github.benmanes.caffeine.cache.Caffeine<java.util.UUID,dev.jakublaba.backgroundprocessingapi.model.entity.Job>' to 'com.github.benmanes.caffeine.cache.Caffeine<java.lang.Object,java.lang.Object>'

The only thing I can think of right now is declaring caffeine bean as Caffeine<Object, Object> and do some absolutely atrocious casting inside Expiry implementation, but is there a more elegant and type safe way to do this?

I am using Spring Boot 3.2.5 and Caffeine 3.1.8

Upvotes: 0

Views: 222

Answers (1)

Wladimir Diskowski
Wladimir Diskowski

Reputation: 650

Not Type safe, but short:

@Bean
public CacheManager cacheManager(@Value("${spring.cache.cache-names}") String... cacheNames) {
  var cacheManager = new CaffeineCacheManager(cacheNames);
  cacheManager.setCaffeine((Caffeine) caffeine());
  return cacheManager;
}

Upvotes: 1

Related Questions