Repository navigation
Optimize Cloud VM create path #3736
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
2d9bbe5
77025a9
beb7439
1082ba8
929069d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,7 @@ export async function POST( | |
| throw err; | ||
| } | ||
| }, | ||
| { includeTeams: "selected" }, | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,5 +61,6 @@ export async function POST( | |
| throw err; | ||
| } | ||
| }, | ||
| { includeTeams: "selected" }, | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,5 +29,6 @@ export async function DELETE( | |
| } | ||
| return jsonResponse({ ok: true }); | ||
| }, | ||
| { includeTeams: "selected" }, | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,5 +42,6 @@ export async function POST( | |
| throw err; | ||
| } | ||
| }, | ||
| { includeTeams: "selected" }, | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -76,6 +76,9 @@ export async function GET(request: Request): Promise<Response> { | |||||||||||||||||||||||
| })); | ||||||||||||||||||||||||
| return jsonResponse({ vms }); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| () => ({ | ||||||||||||||||||||||||
| requestedBillingTeamId: requestedVmTeamIdFromRequest(request), | ||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
@@ -252,5 +255,29 @@ export async function POST(request: Request): Promise<Response> { | |||||||||||||||||||||||
| createdAt: created.createdAt, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| vmCreateAuthOptions, | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| async function vmCreateAuthOptions(request: Request) { | ||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||
| requestedBillingTeamId: requestedVmTeamIdFromRequest(request) ?? | ||||||||||||||||||||||||
| await requestedVmTeamIdFromBody(request), | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Team ID priority in
// auth options (line 264): header wins
requestedBillingTeamId: requestedVmTeamIdFromRequest(request) ??
await requestedVmTeamIdFromBody(request)
// handler (line 189): body wins
const requestedBillingTeamId = body.billingTeamId || requestedVmTeamIdFromRequest(request);Concrete failure: a multi-team user whose
Fix: align 🐛 Proposed fix async function vmCreateAuthOptions(request: Request) {
return {
- requestedBillingTeamId: requestedVmTeamIdFromRequest(request) ??
- await requestedVmTeamIdFromBody(request),
+ requestedBillingTeamId: await requestedVmTeamIdFromBody(request) ??
+ requestedVmTeamIdFromRequest(request),
};
}🤖 Prompt for AI Agents
Comment on lines
+752
to
+796
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a client sends both
Suggested change
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| async function requestedVmTeamIdFromBody(request: Request): Promise<string | null> { | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| const rawText = await request.clone().text(); | ||||||||||||||||||||||||
| if (rawText.length === 0) return null; | ||||||||||||||||||||||||
| const raw = JSON.parse(rawText); | ||||||||||||||||||||||||
| if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null; | ||||||||||||||||||||||||
| const candidate = raw as Record<string, unknown>; | ||||||||||||||||||||||||
| const bodyBillingTeamId = candidate.billingTeamId ?? candidate.teamId; | ||||||||||||||||||||||||
| return typeof bodyBillingTeamId === "string" && bodyBillingTeamId.trim() | ||||||||||||||||||||||||
| ? bodyBillingTeamId.trim() | ||||||||||||||||||||||||
| : null; | ||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,7 @@ const CREATE_TIMEOUT_MS = 15 * 60 * 1000; | |
| const SNAPSHOT_TIMEOUT_MS = 15 * 60 * 1000; | ||
| const EXEC_OVERHEAD_TIMEOUT_MS = 15_000; | ||
| const MAX_EXEC_TIMEOUT_MS = 15 * 60 * 1000; | ||
| const FREESTYLE_ACTIVE_STATES = new Set(["starting", "running", "suspending"]); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suspended VMs excluded from active limit checkMedium Severity
Additional Locations (1)Reviewed by Cursor Bugbot for commit 77025a9. Configure here. |
||
|
|
||
| function client(timeoutMs = DEFAULT_TIMEOUT_MS): Freestyle { | ||
| const longFetch: typeof fetch = (input, init) => | ||
|
|
@@ -79,6 +80,34 @@ function mapStatus(state: string | null | undefined): VMStatus { | |
| export class FreestyleProvider implements VMProvider { | ||
| readonly id = "freestyle" as const; | ||
|
|
||
| async listActiveVmIds(): Promise<readonly string[]> { | ||
| return withVmSpan( | ||
| "cmux.vm.provider.list_active", | ||
| { | ||
| "cmux.vm.provider": "freestyle", | ||
| "cmux.vm.operation": "list_active", | ||
| "cmux.timeout_ms": DEFAULT_TIMEOUT_MS, | ||
| }, | ||
| async (span) => { | ||
| try { | ||
| const result = await client().vms.list(); | ||
| const active = result.vms | ||
| .filter((vm) => !vm.deleted && FREESTYLE_ACTIVE_STATES.has(vm.state)) | ||
| .map((vm) => vm.id); | ||
| setSpanAttributes(span, { | ||
| "cmux.vm.provider.active_count": active.length, | ||
| "cmux.vm.provider.total_count": result.totalCount, | ||
| "cmux.vm.provider.running_count": result.runningCount, | ||
| "cmux.vm.provider.starting_count": result.startingCount, | ||
| }); | ||
| return active; | ||
| } catch (err) { | ||
| throw new ProviderError("freestyle", "listActiveVmIds", err); | ||
| } | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| async create(options: CreateOptions): Promise<VMHandle> { | ||
| const image = options.image.trim(); | ||
| if (!image) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { and, count, desc, eq, inArray, isNotNull, isNull, ne, or, sql } from "drizzle-orm"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { and, desc, eq, isNotNull, isNull, ne, or, sql } from "drizzle-orm"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as Context from "effect/Context"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as Effect from "effect/Effect"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as Layer from "effect/Layer"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -39,6 +39,7 @@ export type VmRepositoryShape = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly image: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly imageVersion?: string | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly maxActiveVms: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly liveActiveProviderVmIds?: readonly string[] | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly idempotencyKey?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) => Effect.Effect<BeginCreateResult, VmDatabaseError | VmLimitExceededError>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly markCreateRunning: (input: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -217,19 +218,31 @@ export const VmRepositoryLive = Layer.succeed(VmRepository, { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (existing) return { inserted: false as const, vm: existing }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [active] = await tx | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .select({ total: count() }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const activeCandidates = await tx | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .select({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| provider: cloudVms.provider, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| providerVmId: cloudVms.providerVmId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: cloudVms.status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .from(cloudVms) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .where( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inArray(cloudVms.status, ["provisioning", "running", "paused"]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eq(cloudVms.billingTeamId, input.billingTeamId), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and(isNull(cloudVms.billingTeamId), eq(cloudVms.userId, input.userId)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eq(cloudVms.billingTeamId, input.billingTeamId), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and(isNull(cloudVms.billingTeamId), eq(cloudVms.userId, input.userId)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
cursor[bot] marked this conversation as resolved.
Resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const activeCount = Number(active?.total ?? 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const liveActiveProviderVmIds = input.liveActiveProviderVmIds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? new Set(input.liveActiveProviderVmIds) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const activeCount = activeCandidates.filter((vm) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isActiveForLimit({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| provider: vm.provider, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| providerVmId: vm.providerVmId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: vm.status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reconciledProvider: liveActiveProviderVmIds ? input.provider : null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| liveActiveProviderVmIds, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).length; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (activeCount >= input.maxActiveVms) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new VmLimitExceededError({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kind: "active_vms", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -420,3 +433,24 @@ export const VmRepositoryLive = Layer.succeed(VmRepository, { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function isActiveForLimit(input: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly provider: ProviderId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly providerVmId: string | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly status: CloudVmRow["status"]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly reconciledProvider: ProviderId | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readonly liveActiveProviderVmIds: ReadonlySet<string> | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }): boolean { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input.provider === input.reconciledProvider && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input.providerVmId && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input.liveActiveProviderVmIds?.has(input.providerVmId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (input.status === "failed" || input.status === "destroyed") return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (input.provider !== input.reconciledProvider) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return input.status === "provisioning" || input.status === "running" || input.status === "paused"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return input.status === "provisioning"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||


Uh oh!
There was an error while loading. Please reload this page.