本文へ移動
cccskills
無料GitHub で公開

inngest-flow-control

Use when handling external API rate limits (e.g., OpenAI 429s, HubSpot or Stripe rate limits), preventing duplicate work from rapid event bursts (debouncing user actions), spreading load over time, ensuring per-tenant fairness, processing events in batches, limiting concurrent runs of the same operation, or assigning priority to important runs. Covers Inngest flow control: concurrency limits with keys, throttling, rate limiting, debounce, priority, singleton, and event batching.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md9.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Inngest Flow Control

Master Inngest flow control mechanisms to manage resources, prevent overloading systems, and ensure application reliability. This skill covers all flow control options with prescriptive guidance on when and how to use each.

These skills are focused on TypeScript. For Python or Go, refer to the Inngest documentation for language-specific guidance. Core concepts apply across all languages.

Quick Decision Guide

  • "Limit how many run at once" → Concurrency
  • "Spread runs over time" → Throttling
  • "Block after N runs in a period" → Rate Limiting
  • "Wait for activity to stop, then run once" → Debounce
  • "Only one run at a time for this key" → Singleton
  • "Process events in groups" → Batching
  • "Some runs are more important" → Priority

Concurrency

When to use: Limit the number of executing steps (not function runs) to manage computing resources and prevent system overwhelm.

Key insight: Concurrency limits active code execution, not function runs. A function waiting on step.sleep() or step.waitForEvent() doesn't count against the limit.

Basic Concurrency

inngest.createFunction(
  {
    id: "process-images",
    concurrency: 5,
    triggers: [{ event: "media/image.uploaded" }],
  },
  async ({ event, step }) => {
    // Only 5 steps can execute simultaneously
    await step.run("resize", () => resizeImage(event.data.imageUrl));
  },
);

Concurrency with Keys (Multi-tenant)

Use key parameter to apply limit per unique value of the key.

inngest.createFunction(
  {
    id: "user-sync",
    concurrency: [
      {
        key: "event.data.user_id",
        limit: 1,
      },
    ],
    triggers: [{ event: "user/profile.updated" }],
  },
  async ({ event, step }) => {
    // Only 1 step per user can execute at once
    // Prevents race conditions in user-specific operations
  },
);

Account-level Shared Limits

inngest.createFunction(
  {
    id: "ai-summary",
    concurrency: [
      {
        scope: "account",
        key: `"openai"`,
        limit: 60,
      },
    ],
    triggers: [{ event: "ai/summary.requested" }],
  },
  async ({ event, step }) => {
    // Share 60 concurrent OpenAI calls across all functions
  },
);

When to use each:

  • Basic: Protect databases or limit general capacity
  • Keyed: Multi-tenant fairness, prevent "noisy neighbor" issues
  • Account-level: Share quotas across multiple functions (API limits)

Throttling

When to use: Control the rate of function starts over time to work around API rate limits or smooth traffic spikes.

Key difference from concurrency: Throttling limits function run starts; concurrency limits step execution.

inngest.createFunction(
  {
    id: "sync-crm-data",
    throttle: {
      limit: 10, // 10 function starts
      period: "60s", // per minute
      burst: 5, // plus 5 immediate bursts
      key: "event.data.customer_id", // per customer
    },
    triggers: [{ event: "crm/contact.updated" }],
  },
  async ({ event, step }) => {
    // Respects CRM API rate limits: 10 calls/min per customer
    await step.run("sync", () => crmApi.updateContact(event.data));
  },
);

Configuration:

  • limit: Functions that can start per period
  • period: Time window (1s to 7d)
  • burst: Extra immediate starts allowed
  • key: Apply limits per unique key value

Rate Limiting

When to use: Hard limit to prevent abuse or skip excessive duplicate events.

Key difference from throttling: Rate limiting discards events; throttling delays them.

inngest.createFunction(
  {
    id: "webhook-processor",
    rateLimit: {
      limit: 1,
      period: "4h",
      key: "event.data.webhook_id",
    },
    triggers: [{ event: "webhook/data.received" }],
  },
  async ({ event, step }) => {
    // Process each webhook only once per 4 hours
    // Prevents duplicate webhook spam
  },
);

Use cases:

  • Prevent webhook duplicates
  • Limit expensive operations per user
  • Protection against abuse

Debounce

When to use: Wait for a series of events to stop arriving before processing the latest one.

inngest.createFunction(
  {
    id: "save-document",
    debounce: {
      period: "5m", // Wait 5min after last edit
      key: "event.data.document_id",
      timeout: "30m", // Force save after 30min max
    },
    triggers: [{ event: "document/content.changed" }],
  },
  async ({ event, step }) => {
    // Saves document only after user stops editing
    // Uses the LAST event received
    await step.run("save", () => saveDocument(event.data));
  },
);

Perfect for:

  • User input that changes rapidly (search, document editing)
  • Noisy webhook events
  • Ensuring latest data is processed

Priority

When to use: Execute some function runs ahead of others based on dynamic data.

inngest.createFunction(
  {
    id: "process-order",
    priority: {
      // VIP users get priority up to 120 seconds ahead
      run: "event.data.user_tier == 'vip' ? 120 : 0",
    },
    triggers: [{ event: "order/placed" }],
  },
  async ({ event, step }) => {
    // VIP orders jump ahead in the queue
  },
);

Advanced example:

inngest.createFunction(
  {
    id: "support-ticket",
    priority: {
      run: `
        event.data.severity == 'critical' ? 300 :
        event.data.severity == 'high' ? 120 :
        event.data.user_plan == 'enterprise' ? 60 : 0
      `,
    },
    triggers: [{ event: "support/ticket.created" }],
  },
  async ({ event, step }) => {
    // Critical tickets get highest priority (300s ahead)
    // High severity: 120s ahead
    // Enterprise users: 60s ahead
    // Everyone else: normal priority
  },
);

Singleton

When to use: Ensure only one instance of a function runs at a time.

Skip Mode (Preserve Current Run)

inngest.createFunction(
  {
    id: "data-backup",
    singleton: {
      key: "event.data.database_id",
      mode: "skip",
    },
    triggers: [{ event: "backup/requested" }],
  },
  async ({ event, step }) => {
    // Skip new backups if one is already running for this database
    await step.run("backup", () => performBackup(event.data.database_id));
  },
);

Cancel Mode (Use Latest Event)

inngest.createFunction(
  {
    id: "realtime-sync",
    singleton: {
      key: "event.data.user_id",
      mode: "cancel",
    },
    triggers: [{ event: "user/data.changed" }],
  },
  async ({ event, step }) => {
    // Cancel previous sync and start with latest data
    await step.run("sync", () => syncUserData(event.data));
  },
);

Batching

When to use: Process multiple events together for efficiency.

inngest.createFunction(
  {
    id: "bulk-email-send",
    batchEvents: {
      maxSize: 100, // Up to 100 events
      timeout: "30s", // Or 30 seconds, whichever first
      // `key` groups events into separate batches per unique value
      // This is different from expressions `if` which filters events
      key: "event.data.campaign_id", // Batch per campaign
    },
    triggers: [{ event: "email/send.queued" }],
  },
  async ({ events, step }) => {
    // Process array of events together
    const emails = events.map((evt) => ({
      to: evt.data.email,
      subject: evt.data.subject,
      body: evt.data.body,
    }));

    await step.run("send-batch", () => emailService.sendBulk(emails));
  },
);

Combining Flow Control

Example: Fair AI Processing

inngest.createFunction(
  {
    id: "ai-image-processing",
    // Global throttling for API limits
    throttle: {
      limit: 50,
      period: "60s",
      key: `"gpu-cluster"`,
    },
    // Per-user concurrency for fairness
    concurrency: [
      {
        key: "event.data.user_id",
        limit: 3,
      },
    ],
    // VIP users get priority
    priority: {
      run: "event.data.plan == 'pro' ? 60 : 0",
    },
    triggers: [{ event: "ai/image.generate" }],
  },
  async ({ event, step }) => {
    // Combines multiple flow controls for optimal resource usage
  },
);

Pro tip: Most production functions benefit from combining 1-3 flow control mechanisms for optimal reliability and performance.

This Repository

These upstream Inngest instructions are vendored for agent tooling and integration work in this monorepo.

Repository Triggers

Use this skill when inngest-flow-control matches the current Inngest task. If the right skill is unclear, start with docs/ai/skills/inngest/SKILL.md.

Repository Workflow

  1. Confirm whether the request is agent-tooling guidance or product runtime integration.
  2. Use inngest-brownfield-audit before changing existing app workflows or fragile background work.
  3. Follow this upstream guidance under OpenSpec, root AGENTS.md, repo rulebooks, framework docs, and runtime evidence.
  4. Keep runtime packages, app code, migrations, and INNGEST_* env requirements out of agent-tooling-only changes.

Repository Checklist

  • The task has explicit product-runtime scope before adding Inngest app code or dependencies.
  • Existing workflows were audited before introducing or changing durable workflow behavior.
  • Any MCP usage is backed by a running Inngest dev server on the configured port.
  • Upstream source and license attribution remain documented in docs/ai/skills/inngest/references/upstream.md.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Audit and fix accessibility in Core UI. Use when changing or reviewing buttons, links, forms, validation, dialogs, menus, tabs, tables, keyboard interactions, focus management, accessible names, announcements, contrast, touch targets, reduced motion, or WCAG/a11y behavior.

日本語の概要は準備中です。原文の説明を表示しています。

Asymmetric-al/core3812026年10月12日 更新

Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.

日本語の概要は準備中です。原文の説明を表示しています。

Asymmetric-al/core3812026年10月12日 更新

Use when building any system where email content triggers actions — AI agent inboxes, automated support handlers, email-to-task pipelines, or any workflow processing untrusted inbound email. Always use this skill when the user wants to receive emails and act on them programmatically, even if they don't mention "agent" — the skill contains critical security patterns (sender allowlists, content filtering, sandboxed processing) that prevent untrusted email from controlling your system.

日本語の概要は準備中です。原文の説明を表示しています。

Asymmetric-al/core3812026年10月12日 更新

anim

無料

Tasteful, subtle web UI animation following Emil Kowalski / animations.dev principles. Use when adding or reviewing interface motion — hover and press feedback, entrances and exits, modals, toasts, menus, loading and skeleton states, staggered reveals, page or view transitions — so motion stays refined and purposeful, not decorative. Covers CSS, Web Animations, and React timing/easing. Pair with the motion skill for motion/react API specifics.

日本語の概要は準備中です。原文の説明を表示しています。

Asymmetric-al/core3812026年10月12日 更新

animate

無料

Build an animation from scratch, making the decisions in the order that determines whether it feels right — should it animate at all, what purpose, which tool, which properties, which curve and duration, how it interrupts, how it exits. Writes the implementation. Use when asked to animate something, add motion, make a component feel alive, or build a transition. For critiquing existing motion use review-animations; for auditing a whole codebase use improve-animations. Only runs when explicitly invoked; it does not trigger on its own.

日本語の概要は準備中です。原文の説明を表示しています。

Asymmetric-al/core3812026年10月12日 更新

Build animations for Expo and React Native only, using Reanimated, Gesture Handler, Expo Router, and expo-haptics. Use when the target runtime is an Expo or React Native app. Do not use it for Core Next.js web motion; use `animate` for web. Only runs when explicitly invoked; it does not trigger on its own.

日本語の概要は準備中です。原文の説明を表示しています。

Asymmetric-al/core3812026年10月12日 更新

Asymmetric-al のスキルをすべて見る

このスキルの問題を報告する