async-task-pattern

Implements asynchronous task state machines for long-running Spring backend operations.

1.0k|109|Updated Jan 4, 2026
One-click install
npx skills add https://github.com/doccker/cc-use-exp --skill async-task-pattern
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: async-task-pattern
Source: https://github.com/doccker/cc-use-exp/tree/main/.cursor/skills/async-task-pattern
Command: npx skills add https://github.com/doccker/cc-use-exp --skill async-task-pattern

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve?

Synchronous API endpoints that run longer than 10 seconds get cut off by gateway timeouts (30s), causing 502/504 errors, duplicate user clicks triggering concurrent executions, unbounded in-memory state caches leaking memory, and lost tenant context in child threads.

Core Features & Use Cases

  • Async State Machine Template: Provides a triggerAsync() + getStatus() pattern with IDLE/RUNNING/SUCCESS/FAILED states, tenant-isolated ConcurrentHashMap caching, and TTL-based eviction to prevent memory leaks.
  • Concurrency & Context Safety: Prevents duplicate task triggering while RUNNING, restores TenantContext/SecurityContext/MDC in executor child threads, and defines frontend polling rules to avoid double useEffect timers.
  • Use Case: A 6000-row product duplicate check that would exceed the 30s gateway timeout is converted into an immediately-returning trigger endpoint plus a status polling endpoint, with progress shown to all users and results surviving page refreshes.

Quick Start

Convert this slow synchronous endpoint into an async trigger plus status polling pattern following the async-task-pattern skill.

Frequently Asked Questions about async-task-pattern

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I handle API requests that take longer than 30 seconds in Spring?

Convert the endpoint into an async pattern: a trigger endpoint that starts work on an Executor and returns immediately, plus a status endpoint the frontend polls. Store task state in a tenant-keyed ConcurrentHashMap with RUNNING/SUCCESS/FAILED statuses.

How to prevent duplicate concurrent task execution when users click a button twice?

Check the state cache before starting: if a RUNNING state already exists for the tenant, return its snapshot instead of launching a new task. On the frontend, disable the trigger button while status is RUNNING.

Why does TenantContext return null inside @Async or executor threads?

ThreadLocal values do not propagate to child threads automatically. Capture the tenant ID before submitting the task, then set it inside the child thread's runnable and clear it in a finally block. The same applies to SecurityContextHolder and MDC.

Does an in-memory ConcurrentHashMap task state cache cause memory leaks?

Yes, if finished states are never evicted. Add a TTL-based cleanup on each trigger/status call, a scheduled cleanup job, or use Caffeine with maximumSize and expireAfterWrite. Avoid storing large result sets in the state object.

When should I use a message queue instead of an in-memory async task map?

Use Redis-backed state or a queue like RocketMQ, XXL-Job, or Temporal when running multiple instances needing shared state, when tasks exceed one hour, or when retries and workflow orchestration are required. The ConcurrentHashMap approach covers single-instance tasks under an hour.