-
-
Notifications
You must be signed in to change notification settings - Fork 883
feat(queues): Override queue concurrency limits from the dashboard or API #2609
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
WalkthroughThis change set adds queue concurrency override capabilities end-to-end. It introduces DB columns (concurrencyLimitBase, concurrencyLimitOverriddenAt, concurrencyLimitOverriddenBy) via Prisma schema and migrations. New server services (ConcurrencySystem and a singleton instance) implement override/reset logic, engine sync, and stats retrieval. Presenters and pause service are updated to include new concurrency fields and join overriddenBy user data. Two Remix API routes are added for override and reset. Core ApiClient and Trigger SDK gain corresponding methods. UI updates include queue page actions and indicators for overrides, column adjustments, and a new PopoverMenuItem component with forwardRef. A minor tooltip text style change is included. Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (11)
apps/webapp/app/presenters/v3/QueueListPresenter.server.ts (1)
141-165: LGTM! Well-implemented manual join with clear documentation.The manual join logic is well-documented and correctly handles the relationship without a foreign key constraint. The implementation efficiently uses a Map for O(1) lookups and properly handles null cases.
Optional: Consider pagination impact on user fetches.
If the queue list grows significantly, you might want to verify that fetching all overriddenBy users in a single query doesn't cause performance issues. The current implementation should be fine for typical queue counts, but for very large installations, you could consider limiting or batching the user fetches.
packages/core/src/v3/schemas/queues.ts (1)
33-49: Clarify JSDoc and avoid duplicate wording; mark legacy field for clients
- The comments duplicate/mislabel meanings (“The concurrency limit of the queue” appears twice; “override” says “effective/current” like “current”).
- Recommend clarifying docs and hinting clients to prefer concurrency.current over the legacy flat field.
Apply doc-only changes:
- /** The concurrency limit of the queue */ + /** Effective concurrency limit (legacy; prefer concurrency.current) */ concurrencyLimit: z.number().nullable(), - /** The concurrency limit of the queue */ + /** Concurrency metadata */ concurrency: z .object({ /** The effective/current concurrency limit */ current: z.number().nullable(), /** The base concurrency limit (default) */ base: z.number().nullable(), - /** The effective/current concurrency limit */ + /** The overridden concurrency limit (if applied) */ override: z.number().nullable(), /** When the override was applied */ overriddenAt: z.coerce.date().nullable(), /** Who overrode the concurrency limit (will be null if overridden via the API) */ overriddenBy: z.string().nullable(), }) .optional(),Also, confirm all current presenters/routes always include concurrency; if yes, consider making it non-optional in a future major. As per coding guidelines.
apps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.ts (1)
12-26: Deduplicate queueParam normalizationYou both transform queueParam in params (replace %2F) and decode/replace again in the handler. Consider centralizing into a small helper used by override/reset/retrieve routes to avoid drift.
packages/trigger-sdk/src/v3/queues.ts (1)
142-171: Guard and coerce concurrencyLimit at the SDK boundaryTo fail fast on invalid inputs and align with server expectations (non‑negative integers), coerce and validate before calling the API.
export function overrideConcurrencyLimit( queue: RetrieveQueueParam, - concurrencyLimit: number, + concurrencyLimit: number, requestOptions?: ApiRequestOptions ): ApiPromise<QueueItem> { const apiClient = apiClientManager.clientOrThrow(); + // Normalize and validate + const limit = Math.floor(concurrencyLimit); + if (!Number.isFinite(limit) || limit < 0) { + throw new Error("concurrencyLimit must be a non-negative integer"); + } + const $requestOptions = mergeRequestOptions( @@ - return apiClient.overrideQueueConcurrencyLimit(queue, concurrencyLimit, $requestOptions); + return apiClient.overrideQueueConcurrencyLimit(queue, limit, $requestOptions); }Optional: extract the repeated accessory attributes for queue into a small helper to keep wrappers consistent and DRY.
Also applies to: 179-207
packages/core/src/v3/apiClient/index.ts (1)
902-947: Factor out queue identifier encoding to a private helperThe “replace slashes then encode” sequence is duplicated across retrieve/pause/override/reset. Extract to a single helper to avoid future drift.
Apply within this method:
- // Explicitly encode slashes before encoding the rest of the string - const encodedValue = encodeURIComponent(value.replace(/\//g, "%2F")); + const encodedValue = this.#encodeQueueParamValue(value);Do the same in resetQueueConcurrencyLimit (and existing retrieveQueue/pauseQueue).
Add this helper to the class:
// Inside ApiClient class #encodeQueueParamValue(value: string) { // Explicitly encode slashes before encoding the rest of the string return encodeURIComponent(value.replace(/\//g, "%2F")); }apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx (1)
994-1003: Constrain the input to integers in the UIGuide correct input on the client side.
- <Input - type="number" + <Input + type="number" name="concurrencyLimit" id="concurrencyLimit" - min="0" + min="0" + step="1" + inputMode="numeric"apps/webapp/app/v3/services/concurrencySystem.server.ts (3)
13-14: Unify input type with shared RetrieveQueueParam.Avoid a parallel QueueInput. Reuse the core type for cohesion and fewer drift points.
-export type QueueInput = string | { type: "task" | "custom"; name: string }; +import { type RetrieveQueueParam as QueueInput } from "@trigger.dev/core/v3";
123-131: Harden clamping against non-finite maxima.If environment.maximumConcurrencyLimit or organization.maximumConcurrencyLimit isn’t a finite number, Math.min can yield NaN. Guard before clamping.
-const newConcurrencyLimit = Math.max( - Math.min( - concurrencyLimit, - environment.maximumConcurrencyLimit, - environment.organization.maximumConcurrencyLimit - ), - 0 -); +const caps = [ + environment.maximumConcurrencyLimit, + environment.organization.maximumConcurrencyLimit, +].filter((n): n is number => Number.isFinite(n as number)); +const upperBound = caps.length ? Math.min(concurrencyLimit, ...caps) : concurrencyLimit; +const newConcurrencyLimit = Math.max(0, upperBound);
179-193: DB updated before engine sync → possible drift on sync failure.If engine update/remove fails, DB remains changed. Consider a compensating update, an outbox/event to retry sync, or a transactional pattern around state + sync intent.
- Option A: Write desired state + “pending_sync” flag; background worker performs engine sync with retries, then clears flag.
- Option B: On sync failure, best-effort revert DB to prior values and surface an error.
- At minimum, emit structured logs/metrics for failed syncs and expose health to ops.
apps/webapp/app/presenters/v3/QueueRetrievePresenter.server.ts (1)
49-69: Reduce DB roundtrip by joining user in a single query.Instead of fetching the queue then a separate user query, include the user relation (by ID) in one call.
Example:
-const user = await prismaClient.user.findFirst({ - where: { id: queue.concurrencyLimitOverriddenBy }, -}); - -return { - ...queue, - concurrencyLimitOverriddenBy: user, -}; +const found = await prismaClient.taskQueue.findFirst({ + where: { id: queue.id }, + include: { concurrencyLimitOverriddenByUser: true }, // adjust relation name +}); +return found + ? { + ...(found as any), + concurrencyLimitOverriddenBy: found.concurrencyLimitOverriddenByUser, + } + : undefined;Note: Use the actual Prisma relation name for the User; rename accordingly.
apps/webapp/app/components/primitives/Popover.tsx (1)
56-96: Tighten PopoverMenuItem prop types (variant/onClick) and remove casts.
- variant should be the variant string, not ButtonContentPropsType.
- Type onClick for both anchor and button; remove any casts.
-const PopoverMenuItem = React.forwardRef< - HTMLButtonElement | HTMLAnchorElement, - { - to?: string; - icon?: RenderIcon; - title: React.ReactNode; - isSelected?: boolean; - variant?: ButtonContentPropsType; - leadingIconClassName?: string; - className?: string; - onClick?: React.MouseEventHandler; - disabled?: boolean; - } ->( +const PopoverMenuItem = React.forwardRef< + HTMLButtonElement | HTMLAnchorElement, + { + to?: string; + icon?: RenderIcon; + title: React.ReactNode; + isSelected?: boolean; + variant?: ButtonContentPropsType["variant"]; + leadingIconClassName?: string; + className?: string; + onClick?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>; + disabled?: boolean; + } +>( ( { to, icon, title, isSelected, - variant = { variant: "small-menu-item" }, + variant = "small-menu-item", leadingIconClassName, className, onClick, disabled, }, ref ) => { const contentProps = { - variant: variant.variant, + variant, LeadingIcon: icon, leadingIconClassName, fullWidth: true, textAlignLeft: true, TrailingIcon: isSelected ? CheckIcon : undefined, className: cn( "group-hover:bg-charcoal-700", isSelected ? "bg-charcoal-750 group-hover:bg-charcoal-600/50" : undefined, className ), } as const; if (to) { return ( <Link to={to} - ref={ref as React.Ref<HTMLAnchorElement>} + ref={ref as React.Ref<HTMLAnchorElement>} className={cn("group/button focus-custom", contentProps.fullWidth ? "w-full" : "")} - onClick={onClick as any} + onClick={onClick as React.MouseEventHandler<HTMLAnchorElement>} > <ButtonContent {...contentProps}>{title}</ButtonContent> </Link> ); } return ( <button type="button" - ref={ref as React.Ref<HTMLButtonElement>} - onClick={onClick} + ref={ref as React.Ref<HTMLButtonElement>} + onClick={onClick as React.MouseEventHandler<HTMLButtonElement>} disabled={disabled} className={cn( "group/button outline-none focus-custom", contentProps.fullWidth ? "w-full" : "" )} > <ButtonContent {...contentProps}>{title}</ButtonContent> </button> ); } );Optional: for Link, consider aria-disabled and preventing navigation when disabled:
<Link to={to} aria-disabled={disabled || undefined} onClick={disabled ? (e) => e.preventDefault() : onClick} />Also applies to: 98-108, 111-124
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
references/hello-world/src/trigger/queues.tsis excluded by!references/**
📒 Files selected for processing (17)
apps/webapp/app/components/primitives/Buttons.tsx(1 hunks)apps/webapp/app/components/primitives/Popover.tsx(2 hunks)apps/webapp/app/presenters/v3/QueueListPresenter.server.ts(3 hunks)apps/webapp/app/presenters/v3/QueueRetrievePresenter.server.ts(5 hunks)apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx(17 hunks)apps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.override.ts(1 hunks)apps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.ts(1 hunks)apps/webapp/app/v3/services/concurrencySystem.server.ts(1 hunks)apps/webapp/app/v3/services/concurrencySystemInstance.server.ts(1 hunks)apps/webapp/app/v3/services/pauseQueue.server.ts(1 hunks)internal-packages/database/prisma/migrations/20251009140053_add_task_queue_concurrency_limit_base_column/migration.sql(1 hunks)internal-packages/database/prisma/migrations/20251010102348_add_concurrency_limit_overriden_at_to_task_queue/migration.sql(1 hunks)internal-packages/database/prisma/migrations/20251010114444_add_concurrency_limit_overridden_by_to_task_queue/migration.sql(1 hunks)internal-packages/database/prisma/schema.prisma(1 hunks)packages/core/src/v3/apiClient/index.ts(1 hunks)packages/core/src/v3/schemas/queues.ts(1 hunks)packages/trigger-sdk/src/v3/queues.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations
Files:
apps/webapp/app/presenters/v3/QueueListPresenter.server.tsapps/webapp/app/v3/services/pauseQueue.server.tsapps/webapp/app/v3/services/concurrencySystemInstance.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.tspackages/trigger-sdk/src/v3/queues.tspackages/core/src/v3/schemas/queues.tspackages/core/src/v3/apiClient/index.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.override.tsapps/webapp/app/components/primitives/Popover.tsxapps/webapp/app/components/primitives/Buttons.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsxapps/webapp/app/v3/services/concurrencySystem.server.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
We use zod a lot in packages/core and in the webapp
Files:
apps/webapp/app/presenters/v3/QueueListPresenter.server.tsapps/webapp/app/v3/services/pauseQueue.server.tsapps/webapp/app/v3/services/concurrencySystemInstance.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.tspackages/core/src/v3/schemas/queues.tspackages/core/src/v3/apiClient/index.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.override.tsapps/webapp/app/components/primitives/Popover.tsxapps/webapp/app/components/primitives/Buttons.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsxapps/webapp/app/v3/services/concurrencySystem.server.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
When importing from @trigger.dev/core in the webapp, never import the root package path; always use one of the documented subpath exports from @trigger.dev/core’s package.json
Files:
apps/webapp/app/presenters/v3/QueueListPresenter.server.tsapps/webapp/app/v3/services/pauseQueue.server.tsapps/webapp/app/v3/services/concurrencySystemInstance.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.override.tsapps/webapp/app/components/primitives/Popover.tsxapps/webapp/app/components/primitives/Buttons.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsxapps/webapp/app/v3/services/concurrencySystem.server.ts
{apps/webapp/app/**/*.server.{ts,tsx},apps/webapp/app/routes/**/*.ts}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access environment variables only via the env export from app/env.server.ts; do not reference process.env directly
Files:
apps/webapp/app/presenters/v3/QueueListPresenter.server.tsapps/webapp/app/v3/services/pauseQueue.server.tsapps/webapp/app/v3/services/concurrencySystemInstance.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.override.tsapps/webapp/app/v3/services/concurrencySystem.server.ts
apps/webapp/app/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Modules intended for test consumption under apps/webapp/app/**/*.ts must not read environment variables; accept configuration via options instead
Files:
apps/webapp/app/presenters/v3/QueueListPresenter.server.tsapps/webapp/app/v3/services/pauseQueue.server.tsapps/webapp/app/v3/services/concurrencySystemInstance.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.tsapps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.override.tsapps/webapp/app/v3/services/concurrencySystem.server.ts
🧬 Code graph analysis (10)
apps/webapp/app/v3/services/concurrencySystemInstance.server.ts (2)
apps/webapp/app/v3/services/concurrencySystem.server.ts (1)
ConcurrencySystem(15-51)apps/webapp/app/db.server.ts (1)
$replica(103-106)
apps/webapp/app/presenters/v3/QueueRetrievePresenter.server.ts (2)
apps/webapp/app/lib.es5.d.ts (1)
Prettify(13-15)internal-packages/database/src/transaction.ts (1)
PrismaClientOrTransaction(17-17)
apps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.ts (4)
packages/core/src/v3/schemas/queues.ts (4)
RetrieveQueueType(13-13)RetrieveQueueType(14-14)RetrieveQueueParam(92-92)RetrieveQueueParam(94-94)apps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.override.ts (1)
createActionApiRoute(13-75)apps/webapp/app/v3/services/concurrencySystemInstance.server.ts (1)
concurrencySystem(5-8)apps/webapp/app/presenters/v3/QueueRetrievePresenter.server.ts (1)
toQueueItem(137-168)
packages/trigger-sdk/src/v3/queues.ts (4)
packages/core/src/v3/schemas/queues.ts (4)
RetrieveQueueParam(92-92)RetrieveQueueParam(94-94)QueueItem(16-50)QueueItem(52-52)packages/core/src/v3/apiClient/index.ts (2)
ApiRequestOptions(131-131)mergeRequestOptions(1324-1340)packages/core/src/v3/apiClientManager-api.ts (1)
apiClientManager(5-5)packages/core/src/v3/index.ts (1)
accessoryAttributes(57-57)
packages/core/src/v3/apiClient/index.ts (2)
packages/core/src/v3/schemas/queues.ts (4)
RetrieveQueueParam(92-92)RetrieveQueueParam(94-94)QueueItem(16-50)QueueItem(52-52)packages/core/src/v3/apiClient/core.ts (2)
ZodFetchOptions(31-39)zodfetch(71-78)
apps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.override.ts (4)
packages/core/src/v3/schemas/queues.ts (4)
RetrieveQueueType(13-13)RetrieveQueueType(14-14)RetrieveQueueParam(92-92)RetrieveQueueParam(94-94)apps/webapp/app/routes/api.v1.queues.$queueParam.concurrency.reset.ts (1)
createActionApiRoute(12-75)apps/webapp/app/v3/services/concurrencySystemInstance.server.ts (1)
concurrencySystem(5-8)apps/webapp/app/presenters/v3/QueueRetrievePresenter.server.ts (1)
toQueueItem(137-168)
apps/webapp/app/components/primitives/Popover.tsx (3)
apps/webapp/app/components/primitives/Icon.tsx (1)
RenderIcon(4-4)apps/webapp/app/components/primitives/Buttons.tsx (2)
ButtonContentPropsType(168-183)ButtonContent(185-288)apps/webapp/app/utils/cn.ts (1)
cn(77-79)
apps/webapp/app/components/primitives/Buttons.tsx (1)
apps/webapp/app/components/primitives/Tooltip.tsx (1)
TooltipContent(128-128)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx (9)
apps/webapp/app/models/message.server.ts (2)
redirectWithErrorMessage(181-198)redirectWithSuccessMessage(162-179)apps/webapp/app/models/user.server.ts (1)
getUserById(169-182)apps/webapp/app/v3/services/concurrencySystemInstance.server.ts (1)
concurrencySystem(5-8)apps/webapp/app/env.server.ts (1)
env(1203-1203)apps/webapp/app/assets/icons/RunsIcon.tsx (1)
RunsIcon(1-18)apps/webapp/app/components/primitives/Headers.tsx (1)
Header3(72-90)apps/webapp/app/components/primitives/Spinner.tsx (1)
Spinner(8-62)apps/webapp/app/components/primitives/Input.tsx (1)
Input(107-107)apps/webapp/app/components/primitives/FormButtons.tsx (1)
FormButtons(3-22)
apps/webapp/app/v3/services/concurrencySystem.server.ts (2)
packages/core/src/v3/apiClient/index.ts (2)
overrideQueueConcurrencyLimit(902-926)resetQueueConcurrencyLimit(928-947)apps/webapp/app/v3/runEngine.server.ts (1)
engine(9-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: typecheck / typecheck
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
apps/webapp/app/components/primitives/Buttons.tsx (1)
279-279: LGTM! Improved tooltip text visibility.The change from
text-dimmedtotext-text-brightimproves tooltip readability and aligns with other bright text usage in the component.internal-packages/database/prisma/migrations/20251010102348_add_concurrency_limit_overriden_at_to_task_queue/migration.sql (1)
1-2: LGTM! Clean migration for timestamp tracking.The migration correctly adds a nullable timestamp column to track when concurrency limits are overridden. Aligns with the Prisma schema changes.
internal-packages/database/prisma/migrations/20251010114444_add_concurrency_limit_overridden_by_to_task_queue/migration.sql (1)
1-2: LGTM! Correctly adds user tracking column.The migration adds a nullable TEXT column to store the user ID who overrode the concurrency limit. The lack of a foreign key constraint is intentional, as evidenced by the manual join logic in the presenters.
internal-packages/database/prisma/migrations/20251009140053_add_task_queue_concurrency_limit_base_column/migration.sql (1)
1-2: LGTM! Migration correctly adds base limit tracking.The migration adds a nullable integer column to store the base concurrency limit value before any overrides are applied. Aligns with the Prisma schema.
apps/webapp/app/v3/services/pauseQueue.server.ts (1)
91-93: LGTM! Correctly propagates concurrency override metadata.The implementation correctly uses
updatedQueuefor the base fields andqueue.concurrencyLimitOverriddenByfor the user reference. This is intentional becausequeue(fromgetQueue) includes the joined user data, whileupdatedQueueis the raw TaskQueue from the Prisma update operation.apps/webapp/app/v3/services/concurrencySystemInstance.server.ts (1)
11-14: LGTM! Proper singleton initialization with read replica support.The singleton correctly provides both write (prisma) and read ($replica) database instances to the ConcurrencySystem, following the established pattern in the codebase.
apps/webapp/app/presenters/v3/QueueListPresenter.server.ts (1)
117-119: LGTM! Correctly extends queue selection with concurrency override fields.The new fields align with the Prisma schema and support the concurrency override feature.
internal-packages/database/prisma/schema.prisma (1)
1485-1493: LGTM! Well-documented schema additions for concurrency override tracking.The new fields are correctly defined as nullable and include clear inline documentation. The decision to use
String?forconcurrencyLimitOverriddenBywithout a foreign key relation is appropriate given the manual join approach used in the presenters, avoiding potential cascading deletion issues.apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx (1)
241-261: LGTM: Server action wires override flow correctlyThe override case validates inputs, resolves the user, calls concurrencySystem, and redirects with toast feedback. Matches the reset case pattern.
Dashboard UI updates
Set and update override limits


Dropdown menu items update to reflect the rest of the app

Queues are highlighted and include a badge when concurrency is manually overridden
