@@ -458,18 +458,20 @@ export class FileSystemState {
458458 // ------------------ Heartbeat methods ------------------
459459 public async sendHeartbeat ( ) {
460460 const extId = this . extId
461+ const filePath = this . makeStateFilePath ( extId )
462+ const now = this . deps . now ( )
463+
464+ let fileHandle : nodeFs . FileHandle | undefined
461465 try {
462- const now = this . deps . now ( )
463466 const func = async ( ) => {
464- const filePath = this . makeStateFilePath ( extId )
465-
466- // We were seeing weird behavior where we possibly read an old file, even though we overwrote it.
467- // So this is a sanity check.
468- await fs . delete ( filePath , { force : true } )
469-
470- await fs . writeFile ( filePath , JSON . stringify ( { ...this . ext , lastHeartbeat : now } , undefined , 4 ) )
471-
472- // Sanity check to verify the write is accessible immediately after
467+ fileHandle = await nodeFs . open ( filePath , 'w' )
468+ await fileHandle . writeFile ( JSON . stringify ( { ...this . ext , lastHeartbeat : now } , undefined , 4 ) )
469+ // Noticing that some file reads are not immediately available after write. `fsync` is known to address this.
470+ await fileHandle ! . sync ( )
471+ await fileHandle . close ( )
472+ fileHandle = undefined
473+
474+ // Sanity check to verify the latest write is accessible immediately
473475 const heartbeatData = JSON . parse ( await fs . readFileText ( filePath ) ) as ExtInstanceHeartbeat
474476 if ( heartbeatData . lastHeartbeat !== now ) {
475477 throw new CrashMonitoringError ( 'Heartbeat write validation failed' , { code : className } )
@@ -484,6 +486,8 @@ export class FileSystemState {
484486
485487 return funcWithTelemetryRun
486488 } catch ( e ) {
489+ await fileHandle ?. close ( )
490+
487491 // delete this ext from the state to avoid an incorrectly reported crash since we could not send a new heartbeat
488492 await this . deleteHeartbeatFile ( extId , 'sendHeartbeatFailureCleanup' )
489493 throw e
0 commit comments