Why Redis Cached Data Lost Fields After Async‑to‑Sync Conversion

During a performance test a query API failed on the second request because the Redis cache was written before asynchronous tasks finished, causing missing fields; the article explains the root cause, shows the original and fixed code, and details the debugging process.

FunTester
FunTester
FunTester
Why Redis Cached Data Lost Fields After Async‑to‑Sync Conversion

In a recent performance test a query interface did not meet its metric, leading developers to apply several optimizations and finally adopt an asynchronous query converted to synchronous execution with a Redis cache.

During testing a bug was discovered: the first request returned a complete result, but the second request (which should have hit the cache) was missing some fields.

The simplified backend test code highlights the key parts:

@Override
public void doExecute(Map<String, Object> dataMap) {
    String cache = defaultRedisUtil.getString(RedisKeyConfig.COURSE_PKG_DETAIL_KEY + id);
    if (StringUtils.isNotBlank(cache)) {
        dataMap = JSON.parseObject(cache, Map.class);
        return;
    }
    doSomething(dataMap);
    CountDownLatch countDownLatch = new CountDownLatch(3);
    String traceKey = TraceKeyHolder.getTraceKey();
    teacherPadAsyncService.doExcuteLikeSateAsync(dataMap, coursePackage.getId(), ResourceTypeEnum.COURSE_PACKAGE.value, currentUser.getSystemId(), countDownLatch, traceKey);
    teacherPadAsyncService.doExcuteAccuracyAsync(dataMap, coursePackage.getId(), countDownLatch, traceKey);
    teacherPadAsyncService.doExcuteTeacherTagAsync(dataMap, coursePackage, countDownLatch, traceKey);
    doSomething(dataMap);
    defaultRedisUtil.setString(RedisKeyConfig.COURSE_PKG_DETAIL_KEY + id, JSON.toJSONString(dataMap), RedisKeyConfig.COURSE_PKG_DETAIL_EXPIRE_TIME);
    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        logger.error("异步处理线程异常", e);
    }
}

The method teacherPadAsyncService.doExcuteLikeSateAsync() adds a state field to dataMap. Because the Redis write occurs before the asynchronous tasks complete, the cached entry may lack this field, leading to the observed bug.

Fixing the timing by moving the Redis write after the latch wait ensures that all async tasks have finished before the data is cached:

@Override
public void doExecute(Map<String, Object> dataMap) {
    String cache = defaultRedisUtil.getString(RedisKeyConfig.COURSE_PKG_DETAIL_KEY + id);
    if (StringUtils.isNotBlank(cache)) {
        dataMap = JSON.parseObject(cache, Map.class);
        return;
    }
    doSomething(dataMap);
    CountDownLatch countDownLatch = new CountDownLatch(3);
    String traceKey = TraceKeyHolder.getTraceKey();
    teacherPadAsyncService.doExcuteLikeSateAsync(dataMap, coursePackage.getId(), ResourceTypeEnum.COURSE_PACKAGE.value, currentUser.getSystemId(), countDownLatch, traceKey);
    teacherPadAsyncService.doExcuteAccuracyAsync(dataMap, coursePackage.getId(), countDownLatch, traceKey);
    teacherPadAsyncService.doExcuteTeacherTagAsync(dataMap, coursePackage, countDownLatch, traceKey);
    doSomething(dataMap);
    try {
        countDownLatch.await();
        defaultRedisUtil.setString(RedisKeyConfig.COURSE_PKG_DETAIL_KEY + id, JSON.toJSONString(dataMap), RedisKeyConfig.COURSE_PKG_DETAIL_EXPIRE_TIME);
    } catch (InterruptedException e) {
        logger.error("异步处理线程异常", e);
    }
}

The root cause is that on the first request the Redis entry is empty, so the service queries the database, populates dataMap, and writes it to Redis before the asynchronous tasks have added their fields. Consequently the cached data is incomplete, and any subsequent request within the cache TTL receives the wrong result.

The author notes that this implementation has additional bugs not covered in the article.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendDebuggingJavaperformanceCacheredisAsync
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.