javaalgorithmdata-structures

How can I trigger an expensive computation at most once every 30 minutes?


We have a system that receives a bunch of "item was updated/created/..." events, and that is responsible for doing an expensive computation on the item after it has changed. Currently this system will perform this expensive computation every time an event of the type "item was updated/created/..." arrives. However, changes to items are often made in quick succession, which thus results in a lot of (repeated) expensive computations and load on the system in a short time frame.

We would like to delay the expensive computation for 30 minutes to aggregate the various changes mad in quick succession, and at the end of that window perform the computation only once.

Thus, when the first "item X was updated/created/..." arrives we would like to start a timer for 30 minutes. Any events related to item X during that window will NOT extend the window duration of start a new window. At the end of those 30 minutes the computation is performed. If an event arrives after that cache wipe, a new window of 30 minutes is started

A similar, unrelated window might also exist at the same time for item Y. In total we have 500,000 items.

Would it be possible to help us with some pointers/advice on how to efficiently implement such an "aggregation" window? Is there an efficient data structure that could solve our problem?

We have thought of adding all the events to a database and then performing a group by query every 30 minutes via a cron, but this comes with some drawbacks:


Solution

  • For reference, the solution we used was to create scheduled futures, and keep track if such a future already exists in a Map or Set. Pseudo code:

    private final Map<Item, ScheduledFuture<?>> scheduledTasks = new HashMap<>();
    private final ScheduledExecutorService scheduler = init();
    
    public void onItemArrival(Item item) {
        scheduledTasks.computeIfAbsent(item, item -> 
            scheduler.schedule(() -> {
                performExpensiveComputation(item);
                scheduledTasks.remove(item);
            }, 30, TimeUnit.MINUTES)
        );
    }