Skip to content

OUT-3964 | Tasks App - Task(s) Created Using Template Not Saving - #1385

Merged
arpandhakal merged 5 commits into
mainfrom
OUT-3964-bound-subtask-template-fanout
Jul 10, 2026
Merged

OUT-3964 | Tasks App - Task(s) Created Using Template Not Saving#1385
arpandhakal merged 5 commits into
mainfrom
OUT-3964-bound-subtask-template-fanout

Conversation

@arpandhakal

Copy link
Copy Markdown
Collaborator

Problem

Creating a task from a template with many sub-templates left some subtasks unsaved, and opening the task detail threw:

P2024 — Timed out fetching a new connection from the connection pool (in getSubtaskCountsgetSubtaskStatus)

Root cause

Applying a template fanned out every sub-template's task creation concurrently via Promise.all. Each createSubtasksFromTemplatecreateTask is heavy (multiple queries + activity log + notifications trigger + webhook). For a large template (e.g. 90 sub-templates), ~90 concurrent createTask calls saturated the Prisma connection pool — so:

  • some subtask creations failed → not all subtasks saved
  • the subsequent single-query getSubtaskCounts on the detail page couldn't acquire a connection within pool_timeoutP2024

getSubtaskCounts was the victim, not the cause.

Fix

Bound the fan-out concurrency so it can't monopolize the pool, independent of pool size:

  • src/utils/array.ts — add chunk + runInBatches(items, size, handler) (at most size run at once, one batch after another)
  • src/constants/tasks.tssubtaskTemplateBatchSize = 5
  • tasks.service.ts & public.service.ts — both template-apply sites use runInBatches instead of Promise.all

Ordering is preserved (the per-subtask timestamp is still derived from index).

Notes

  • Pure concurrency change — no behavior/schema change.
  • subtaskTemplateBatchSize is deliberately conservative; bump it in one place if more throughput is wanted.

🤖 Generated with Claude Code

…mplate

Applying a template fanned out every sub-template's task creation at once
via Promise.all. For a template with many sub-templates (e.g. 90), the ~90
concurrent createTask calls exhausted the Prisma connection pool, so some
subtasks failed to save and the subsequent task-detail query timed out
(P2024 / "Timed out fetching a new connection from the connection pool").

Run the fan-out in bounded batches (subtaskTemplateBatchSize) so concurrency
stays small regardless of pool size. Ordering is preserved (timestamp is
still derived from index). Applied at both the internal and public API
template-apply sites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jul 10, 2026

Copy link
Copy Markdown

OUT-3964

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tasks-app Ready Ready Preview, Comment Jul 10, 2026 8:53am

Request Review

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown

Greptile Summary

This PR limits template subtask creation so large templates do not exhaust the database pool. The main changes are:

  • Adds chunk and runInBatches helpers for bounded async fan-out.
  • Adds a shared subtask-template batch-size constant.
  • Uses batched subtask creation in both private and public task creation flows.
  • Moves applied-template lookup into the shared subtask creation helper.
  • Changes subtask-template failures to delete the parent task and return an error.

Confidence Score: 4/5

This is close, but the rollback race should be fixed before merging.

  • Batched creation preserves the original subtask ordering index.
  • Import and helper changes look consistent with the project setup.
  • A failed child creation can delete the parent while sibling child creations in the same batch continue and emit events for rows that are later removed.

src/app/api/tasks/tasksShared.service.ts

Important Files Changed

Filename Overview
src/app/api/tasks/tasksShared.service.ts Refactors template subtask creation to fetch the applied template internally and roll back the parent task when a child creation fails.
src/app/api/tasks/tasks.service.ts Switches private template subtask creation from unbounded fan-out to batched execution while preserving the original index-based timestamp order.
src/app/api/tasks/public/public.service.ts Applies the same batched template subtask creation flow to public task creation.
src/utils/array.ts Adds array chunking and bounded async batch execution helpers.
src/constants/tasks.ts Adds the shared batch-size constant for template subtask creation.

Comments Outside Diff (1)

  1. src/app/api/tasks/tasksShared.service.ts, line 630-635 (link)

    P1 Rollback races siblings When one subtask fails, this handler deletes the parent while other subtasks in the same batch may still be running. runInBatches starts each batch with Promise.all, so a rejection does not cancel sibling createSubtasksFromTemplate calls. A sibling can finish createTask and dispatch task-created notifications or webhooks, then have its row removed by the parent delete cascade. The API fails loudly, but external callers can still receive events for subtasks that no longer exist. The rollback needs to be coordinated outside the concurrent child handler, or the child creations need to avoid non-transactional side effects while siblings can still fail.

Reviews (3): Last reviewed commit: "fix(templates): restore all-or-nothing r..." | Re-trigger Greptile

Comment thread src/app/api/tasks/tasks.service.ts
Comment thread src/app/api/tasks/public/public.service.ts
createSubtasksFromTemplate rolled back on failure by soft-deleting `parentId`
— which is the *parent* (main) task, not the failed subtask. So any single
subtask-creation error (e.g. the connection-pool P2024) silently deleted the
task the user had just created and verified, and Realtime made it vanish from
their screen ("task disappeared on its own" in OUT-3964). The rollback was also
wrong (it orphaned already-created sibling subtasks) and unnecessary — createTask
already cleans up a half-created subtask via its own catch.

Now each subtemplate is applied best-effort: the whole per-subtemplate unit
(getAppliedTemplateDescription + create) runs inside one try/catch that skips
and logs on failure instead of throwing. Because the handler no longer rejects,
runInBatches never aborts, so every subtemplate is attempted and one failure no
longer skips later batches or returns an error after a partial write (fixes the
two P1 review findings). The parent task is never deleted or orphaned.

Refactored createSubtasksFromTemplate to object params and folded the applied-
description fetch inside it, so both the internal and public call sites share the
same failure isolation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Deployment failed with the following error:

Deploying Serverless Functions to multiple regions is restricted to the Pro and Enterprise plans.

Learn More: https://vercel.link/multiple-function-regions

@arpandhakal

Copy link
Copy Markdown
Collaborator Author

@greptile please review again

Comment thread src/app/api/tasks/tasksShared.service.ts
Per product decision: don't apply a template partially or fail silently. If any
subtask fails to create, roll back the parent task and throw, so the caller gets
a clear error instead of a half-populated task with silently-missing subtasks.

The concurrency batching from the earlier commit is retained and is what makes
this safe: it prevents the connection-pool exhaustion (P2024) that was causing
the failures — and would otherwise trigger this rollback and make the task
"disappear". The rollback now also covers a failed getAppliedTemplateDescription
(folded inside the try), so any failure rolls back consistently.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@arpandhakal

Copy link
Copy Markdown
Collaborator Author

please re review @greptile

Comment thread src/app/api/tasks/tasksShared.service.ts
@arpandhakal
arpandhakal requested a review from priosshrsth July 10, 2026 08:47
priosshrsth
priosshrsth previously approved these changes Jul 10, 2026

@priosshrsth priosshrsth left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm

priosshrsth
priosshrsth previously approved these changes Jul 10, 2026
@arpandhakal
arpandhakal merged commit 44069a8 into main Jul 10, 2026
3 checks passed
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.

2 participants