@@ -13,9 +13,26 @@ import { isOrgMigrated } from "~/runEngine/concerns/computeMigration.server";
1313import { backingForQueue , workerRegionRegistry } from "~/v3/workerRegions.server" ;
1414import { globalFlagsRegistry } from "~/v3/globalFlagsRegistry.server" ;
1515import { getEntitlement } from "~/services/platform.v3.server" ;
16+ import { startActiveSpan , attributesFromAuthenticatedEnv } from "~/v3/tracer.server" ;
1617
1718type TemplateCreationMode = "required" | "shadow" | "skip" ;
1819
20+ // Why the mode was chosen — slices the compute.template.create span by path.
21+ type TemplateModeReason =
22+ | "no-client"
23+ | "no-project"
24+ | "microvm-native"
25+ | "migrated"
26+ | "compute-access"
27+ | "rollout"
28+ | "none" ;
29+
30+ type ResolvedTemplateMode = {
31+ mode : TemplateCreationMode ;
32+ migrated : boolean ;
33+ reason : TemplateModeReason ;
34+ } ;
35+
1936type ResolvedPreset = {
2037 name : MachinePresetName ;
2138 cpu : number ;
@@ -60,89 +77,116 @@ export class ComputeTemplateCreationService {
6077 prisma : PrismaClientOrTransaction ;
6178 writer ?: WritableStreamDefaultWriter ;
6279 } ) : Promise < void > {
63- const mode = await this . resolveMode ( options . projectId , options . prisma ) ;
80+ return startActiveSpan ( "compute.template.create" , async ( span ) => {
81+ const { mode, migrated, reason } = await this . resolveMode (
82+ options . projectId ,
83+ options . prisma
84+ ) ;
6485
65- if ( mode === "skip" ) {
66- return ;
67- }
86+ span . setAttributes ( {
87+ ...attributesFromAuthenticatedEnv ( options . authenticatedEnv ) ,
88+ "compute.template.mode" : mode ,
89+ "compute.template.migrated" : migrated ,
90+ "compute.template.reason" : reason ,
91+ "compute.template.deployment_id" : options . deploymentFriendlyId ,
92+ "compute.template.presets_total" : this . presets . length ,
93+ "compute.template.presets_required" : this . requiredPresets . size ,
94+ } ) ;
6895
69- if ( mode === "shadow" ) {
70- this . createTemplate ( options . imageReference , { background : true } )
71- . then ( ( outcome ) => {
72- if ( outcome . error ) {
73- logger . error ( "Shadow template creation failed" , {
96+ if ( mode === "skip" ) {
97+ span . setAttribute ( "compute.template.result" , "skipped" ) ;
98+ return ;
99+ }
100+
101+ if ( mode === "shadow" ) {
102+ // Shadow is fire-and-forget (background build), so the span only records
103+ // that it was dispatched — the build outcome lands server-side later.
104+ span . setAttribute ( "compute.template.result" , "shadow_dispatched" ) ;
105+ this . createTemplate ( options . imageReference , { background : true } )
106+ . then ( ( outcome ) => {
107+ if ( outcome . error ) {
108+ logger . error ( "Shadow template creation failed" , {
109+ id : options . deploymentFriendlyId ,
110+ imageReference : options . imageReference ,
111+ error : outcome . error ,
112+ } ) ;
113+ }
114+ } )
115+ . catch ( ( error ) => {
116+ logger . error ( "Shadow template creation threw unexpectedly" , {
74117 id : options . deploymentFriendlyId ,
75118 imageReference : options . imageReference ,
76- error : outcome . error ,
119+ error : error instanceof Error ? error . message : String ( error ) ,
77120 } ) ;
78- }
79- } )
80- . catch ( ( error ) => {
81- logger . error ( "Shadow template creation threw unexpectedly" , {
82- id : options . deploymentFriendlyId ,
83- imageReference : options . imageReference ,
84- error : error instanceof Error ? error . message : String ( error ) ,
85121 } ) ;
86- } ) ;
87- return ;
88- }
89-
90- // Required mode
91- if ( options . writer ) {
92- try {
93- await options . writer . write (
94- `event: log\ndata: ${ JSON . stringify ( { message : "Building compute template..." } ) } \n\n`
95- ) ;
96- } catch {
97- // Stream may be closed if client disconnected - continue with template creation
122+ return ;
98123 }
99- }
100-
101- logger . info ( "Creating compute template (required mode)" , {
102- id : options . deploymentFriendlyId ,
103- imageReference : options . imageReference ,
104- presets : this . presets . map ( ( p ) => p . name ) ,
105- requiredPresets : [ ...this . requiredPresets ] ,
106- } ) ;
107124
108- const outcome = await this . createTemplate ( options . imageReference ) ;
109- const failureMessage = this . failureMessageForRequiredMode (
110- outcome ,
111- options . deploymentFriendlyId ,
112- options . imageReference
113- ) ;
125+ // Required mode
126+ if ( options . writer ) {
127+ try {
128+ await options . writer . write (
129+ `event: log\ndata: ${ JSON . stringify ( { message : "Building compute template..." } ) } \n\n`
130+ ) ;
131+ } catch {
132+ // Stream may be closed if client disconnected - continue with template creation
133+ }
134+ }
114135
115- if ( failureMessage ) {
116- logger . error ( "Compute template creation failed" , {
136+ logger . info ( "Creating compute template (required mode)" , {
117137 id : options . deploymentFriendlyId ,
118138 imageReference : options . imageReference ,
119- error : failureMessage ,
139+ presets : this . presets . map ( ( p ) => p . name ) ,
140+ requiredPresets : [ ...this . requiredPresets ] ,
120141 } ) ;
121142
122- const failService = new FailDeploymentService ( ) ;
123- await failService . call ( options . authenticatedEnv , options . deploymentFriendlyId , {
124- error : {
125- name : "TemplateCreationFailed" ,
126- message : `Failed to create compute template: ${ failureMessage } ` ,
127- } ,
128- } ) ;
143+ const outcome = await this . createTemplate ( options . imageReference ) ;
144+ span . setAttribute ( "compute.template.presets_built" , outcome . results . length ) ;
129145
130- throw new ServiceValidationError ( `Compute template creation failed: ${ failureMessage } ` ) ;
131- }
146+ const failureMessage = this . failureMessageForRequiredMode (
147+ outcome ,
148+ options . deploymentFriendlyId ,
149+ options . imageReference
150+ ) ;
151+
152+ if ( failureMessage ) {
153+ span . setAttributes ( {
154+ "compute.template.result" : "failed" ,
155+ "compute.template.failure" : failureMessage ,
156+ } ) ;
132157
133- logger . info ( "Compute template created" , {
134- id : options . deploymentFriendlyId ,
135- imageReference : options . imageReference ,
136- results : outcome . results . length ,
158+ logger . error ( "Compute template creation failed" , {
159+ id : options . deploymentFriendlyId ,
160+ imageReference : options . imageReference ,
161+ error : failureMessage ,
162+ } ) ;
163+
164+ const failService = new FailDeploymentService ( ) ;
165+ await failService . call ( options . authenticatedEnv , options . deploymentFriendlyId , {
166+ error : {
167+ name : "TemplateCreationFailed" ,
168+ message : `Failed to create compute template: ${ failureMessage } ` ,
169+ } ,
170+ } ) ;
171+
172+ throw new ServiceValidationError ( `Compute template creation failed: ${ failureMessage } ` ) ;
173+ }
174+
175+ span . setAttribute ( "compute.template.result" , "created" ) ;
176+ logger . info ( "Compute template created" , {
177+ id : options . deploymentFriendlyId ,
178+ imageReference : options . imageReference ,
179+ results : outcome . results . length ,
180+ } ) ;
137181 } ) ;
138182 }
139183
140184 async resolveMode (
141185 projectId : string ,
142186 prisma : PrismaClientOrTransaction
143- ) : Promise < TemplateCreationMode > {
187+ ) : Promise < ResolvedTemplateMode > {
144188 if ( ! this . client ) {
145- return "skip" ;
189+ return { mode : "skip" , migrated : false , reason : "no-client" } ;
146190 }
147191
148192 const project = await prisma . project . findFirst ( {
@@ -158,11 +202,11 @@ export class ComputeTemplateCreationService {
158202 } ) ;
159203
160204 if ( ! project ) {
161- return "skip" ;
205+ return { mode : "skip" , migrated : false , reason : "no-project" } ;
162206 }
163207
164208 if ( project . defaultWorkerGroup ?. workloadType === "MICROVM" ) {
165- return "required" ;
209+ return { mode : "required" , migrated : false , reason : "microvm-native" } ;
166210 }
167211
168212 // Migrated orgs route runs to the compute backing even though their stored
@@ -194,22 +238,26 @@ export class ComputeTemplateCreationService {
194238 }
195239 if ( migrated ) {
196240 // required => template built at deploy (deploy fails on error); off => shadow.
197- return decision . flags ?. computeMigrationRequireTemplate ? "required" : "shadow" ;
241+ return {
242+ mode : decision . flags ?. computeMigrationRequireTemplate ? "required" : "shadow" ,
243+ migrated : true ,
244+ reason : "migrated" ,
245+ } ;
198246 }
199247 }
200248
201249 const hasComputeAccess = await resolveComputeAccess ( prisma , project . organization . featureFlags ) ;
202250
203251 if ( hasComputeAccess ) {
204- return "shadow" ;
252+ return { mode : "shadow" , migrated : false , reason : "compute-access" } ;
205253 }
206254
207255 const rolloutPct = Number ( env . COMPUTE_TEMPLATE_SHADOW_ROLLOUT_PCT ?? "0" ) ;
208256 if ( rolloutPct > 0 && Math . random ( ) * 100 < rolloutPct ) {
209- return "shadow" ;
257+ return { mode : "shadow" , migrated : false , reason : "rollout" } ;
210258 }
211259
212- return "skip" ;
260+ return { mode : "skip" , migrated : false , reason : "none" } ;
213261 }
214262
215263 async createTemplate (
0 commit comments