Skip to content

feat: task caching#248

Closed
thalesraymond wants to merge 1 commit intomainfrom
feat/task-caching-9591858606130206977
Closed

feat: task caching#248
thalesraymond wants to merge 1 commit intomainfrom
feat/task-caching-9591858606130206977

Conversation

@thalesraymond
Copy link
Copy Markdown
Owner

Implemented the task output caching specification to skip redundant task execution.


PR created automatically by Jules for task 9591858606130206977 started by @thalesraymond

Implemented the `feat-task-caching` proposal:
- Added `TaskCacheConfig` to `TaskStep`.
- Created `ICacheProvider` and `MemoryCacheProvider`.
- Added `CachingExecutionStrategy` to wrap inner strategies and skip execution on cache hit.
- Updated `TaskRunner` and `TaskRunnerBuilder` with `withCache()` configuration.
- Archived OpenSpec proposal to `archive/2026-04-03-feat-task-caching`.

Co-authored-by: thalesraymond <32554150+thalesraymond@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 3, 2026

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a task caching mechanism to the task runner system, including a new CachingExecutionStrategy, a TaskCacheConfig interface for individual steps, and a default MemoryCacheProvider. The implementation allows for skipping task execution on cache hits while optionally restoring context state. Feedback focuses on ensuring that cached results maintain their original success status to prevent breaking dependency chains, addressing potential cache corruption from shallow-copied objects, and mitigating memory leaks in the memory cache provider through better eviction strategies.

Comment on lines +31 to +34
return {
...cachedResult,
status: "skipped", // Or potentially a new 'cached' status, but sticking to existing ones
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Overriding the status to "skipped" for cached results is problematic. In many workflow engines, a "skipped" status (often used when a task's condition is false) prevents dependent tasks from executing. Since only successful results are cached (see line 39), the cached result's status is guaranteed to be "success". Overriding it to "skipped" will likely break the dependency chain. It is better to preserve the original status from the cached result.

      return {
        ...cachedResult,
      };

Comment on lines +25 to +35
const cachedResult = await this.cacheProvider.get(cacheKey);

if (cachedResult) {
if (step.cache.restore) {
await step.cache.restore(context, cachedResult);
}
return {
...cachedResult,
status: "skipped", // Or potentially a new 'cached' status, but sticking to existing ones
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The cachedResult is returned as a shallow copy. If the data property contains an object, it will be shared between the cache and the current execution context. If subsequent tasks or the restore function modify this object, the cache will be corrupted. Consider deep-cloning the result before returning it, or documenting that cached data must be treated as immutable. Note that if deep-cloning is implemented using JSON.stringify, consider the performance impact as per repository guidelines which favor avoiding expensive operations unless benchmarked.

References
  1. Retaining separate logic or avoiding expensive operations like JSON.stringify can be justified for performance.

* An in-memory implementation of ICacheProvider.
*/
export class MemoryCacheProvider implements ICacheProvider {
private cache = new Map<string, CacheEntry>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The MemoryCacheProvider only evicts expired entries when they are explicitly accessed via get(). In long-running processes where many unique cache keys are generated but never reused, this can lead to a memory leak as expired entries will remain in the Map indefinitely. While using a Map is correct for O(1) lookups, consider implementing a periodic cleanup mechanism (e.g., using setInterval) or using a cache with a maximum size and an eviction policy (like LRU).

References
  1. Use a Map to ensure O(1) access time for frequent lookups by a specific key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant