How to Implement Real-Time Overdue Task Alerts with Redis Key Expiration and Strategy Pattern

This article explains how to use Redis key expiration notifications combined with a strategy pattern to create real‑time overdue reminders for workflow tasks, covering configuration, listener implementation, distributed locking, message handling, and practical usage in a SpringBoot project.

Java Backend Technology
Java Backend Technology
Java Backend Technology
How to Implement Real-Time Overdue Task Alerts with Redis Key Expiration and Strategy Pattern

Requirement Background

During the project a new requirement emerged: when a workflow task reaches a certain node and exceeds a predefined time without being processed, trigger an overdue reminder to the task handler, e.g., "Your task has overdue, please handle promptly".

Initially considered scheduled tasks, but determining execution frequency and achieving real‑time response proved difficult.

Introducing a message queue was deemed too heavyweight for a simple reminder feature.

There is also a perpetual‑machine solution, but colleagues were unsure whether it had been considered; Redis key expiration listening is often preferred.

Implementation Idea

The project already uses Redis for caching; colleagues thought of leveraging Redis key expiration events to trigger overdue logic, satisfying real‑time requirements.

Different workflows have different overdue logic; a strategy pattern can handle these variations.

Draw a business flow diagram.

Enable Redis Key Expiration Notifications

Modify the Redis configuration file and set the notify-keyspace-events option to Ex to enable expired‑key listening.

Project Implementation of Expiration Listener

public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    @Autowired
    private MessageHandlerManager handlerManager;

    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        // User handles their own business logic; message.toString() gets the expired key
        String expiredKey = message.toString();
        log.info("onMessage --> redis expired key: {}", expiredKey);
        try {
            handlerManager.processingHandler(expiredKey);
            log.info("Expired key processing completed: {}", expiredKey);
        } catch (Exception e) {
            log.error("Exception handling redis expired key: {}", expiredKey, e);
        }
    }
}

Strategy Pattern for Overdue Logic

Define a strategy interface that provides a method to handle the key; it extends InitializingBean so each implementation must register itself after construction.

public interface IMessageHandler extends InitializingBean {
    /**
     * Message execution method
     * @param expiredKey message key
     */
    void handle(String expiredKey);
}

A simple strategy implementation class.

public class ExpressReturnOrderReturnChief implements IMessageHandler {
    private final BaseMessListenService messListenService;

    @Override
    public void handle(String expiredKey) {
        log.info("Processing express return order chief notification");
        String[] split = expiredKey.replace(RedisConstant.KAIYI_QMS + RedisConstant.FLOW_EXPRESS_RETURN_ORDER_FANJIANFENXI_KEZHANG, "").split("@");
        messListenService.checkFlowWithSendMessage(split,
                "Express order (%s) %s is overdue, please urge, monitor, see: https://xxxxxx/",
                false, expiredKey, "WeChat", null);
    }

    @Override
    public void afterPropertiesSet() {
        // Auto‑register
        MessageHandlerManager.register(RedisConstant.KAIYI_QMS + RedisConstant.FLOW_EXPRESS_RETURN_ORDER_FANJIANFENXI_KEZHANG, this);
    }
}

The strategy manager registers implementations so that when a key expires the corresponding handler is invoked.

If no matching strategy is found, the key is ignored.

Before invoking the handler, a temporary Redis key is set as a distributed lock (10 seconds) to avoid duplicate processing.

public class MessageHandlerManager {
    private static final Map<String, IMessageHandler> MESSAGE_HANDLERS = new ConcurrentHashMap<>();
    private final RedisTemplate<String, Object> redisTemplate;

    public static void register(String type, IMessageHandler handler) {
        MESSAGE_HANDLERS.put(type, handler);
    }

    public void processingHandler(String expiredKey) {
        log.info("Message listener triggered: {}", expiredKey);
        Optional<String> first = MESSAGE_HANDLERS.keySet().stream()
                .filter(expiredKey::startsWith).findFirst();
        first.ifPresent(s -> doTempAbsent(expiredKey, MESSAGE_HANDLERS.get(s)));
    }

    private void doTempAbsent(String key, IMessageHandler handler) {
        String tempKey = RedisUtil.md5(key, "UTF-8");
        Boolean exist = redisTemplate.opsForValue()
                .setIfAbsent(tempKey, "1", 10, TimeUnit.SECONDS);
        if (Boolean.FALSE.equals(exist)) {
            log.error("Another service is processing");
            return;
        }
        handler.handle(key);
    }
}

Common Method for Overdue Processing

The method checks whether the workflow is still at the node, then sends a reminder via WeChat or email. It can optionally re‑set the Redis key to trigger repeated reminders (default 24 hours).

public void checkFlowWithSendMessage(String[] split, String format, boolean isSend,
                                     String expiredKey, String sendType, String title) {
    // Query workflow instance, find current running nodes, match taskId, send message, etc.
    // If isSend is true, re‑set the key with 1‑day expiration for repeated alerts.
}

Using the Overdue Logic

public Boolean submit(Long id) {
    // After workflow reaches a node, set Redis key:
    String key = "flow:expressReturnOrder:fanjianfenxi:kezhang:" + taskId + "@" + piid + "@" + manager;
    JinhuiRedisUtil.setString(key, "1");
    JinhuiRedisUtil.expire(key, 1, TimeUnit.DAYS);
    // ...
    return true;
}

Conclusion

Other solutions exist, but this approach fits the legacy project; after switching to a perpetual‑machine scheme it has been stable for a month, though occasional message loss can occur due to Redis expiration event timing.

Problem

After running stably for a period, the project occasionally loses messages.

Colleagues could not locate the issue; after investigation, the cause was identified.

Redis Expired‑Key Message Loss

Redis may drop expired events or delay them, especially under high load or in a cluster where not all nodes are subscribed. The timing of expired events is not guaranteed, and expiration does not equal deletion.

Large data volumes can cause 100 % delay, possibly hours.

Expiration ≠ deletion; a periodic job can query keys to trigger immediate deletion events.

In a cluster, missing events are more likely if clients do not subscribe to every node.

Therefore, this solution requires careful attention.

After switching to a perpetual‑machine approach, the system has been stable for a month with no recurrence.

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.

Strategy Patternworkflowdistributed-lockSpringBootKey Expiration
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

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.