1
0
Fork 0

Add abort of stalled download

There is a known issue with Azure Blob Storage which sometimes causes downloads to stall. This commit adds logic to abort any download that is hanging for more than 2 minutes
pull/1438/head
Cedric Wille 2023-06-19 16:21:37 -04:00
parent a6bf8726aa
commit 8cf7e029de
1 changed files with 35 additions and 1 deletions

View File

@ -37,9 +37,12 @@ export class DownloadProgress {
segmentSize: number segmentSize: number
segmentOffset: number segmentOffset: number
receivedBytes: number receivedBytes: number
previouslyReceivedBytes: number
lastTimeOfNewBytes?: number
startTime: number startTime: number
displayedComplete: boolean displayedComplete: boolean
timeoutHandle?: ReturnType<typeof setTimeout> timeoutHandle?: ReturnType<typeof setTimeout>
abortController?: AbortController
constructor(contentLength: number) { constructor(contentLength: number) {
this.contentLength = contentLength this.contentLength = contentLength
@ -47,8 +50,11 @@ export class DownloadProgress {
this.segmentSize = 0 this.segmentSize = 0
this.segmentOffset = 0 this.segmentOffset = 0
this.receivedBytes = 0 this.receivedBytes = 0
this.previouslyReceivedBytes = 0
this.lastTimeOfNewBytes = undefined
this.displayedComplete = false this.displayedComplete = false
this.startTime = Date.now() this.startTime = Date.now()
this.abortController = undefined
} }
/** /**
@ -62,6 +68,7 @@ export class DownloadProgress {
this.segmentIndex = this.segmentIndex + 1 this.segmentIndex = this.segmentIndex + 1
this.segmentSize = segmentSize this.segmentSize = segmentSize
this.receivedBytes = 0 this.receivedBytes = 0
this.previouslyReceivedBytes = 0
core.debug( core.debug(
`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...` `Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`
@ -84,6 +91,22 @@ export class DownloadProgress {
return this.segmentOffset + this.receivedBytes return this.segmentOffset + this.receivedBytes
} }
setLastTimeOfNewBytes(): void {
this.lastTimeOfNewBytes = Date.now();
}
getLastTimeOfNewBytes(): number | undefined {
return this.lastTimeOfNewBytes;
}
setAbortController(abortReference: AbortController): void {
this.abortController = abortReference;
}
triggerAbortController(): void {
this.abortController?.abort();
}
/** /**
* Returns true if the download is complete. * Returns true if the download is complete.
*/ */
@ -125,7 +148,17 @@ export class DownloadProgress {
*/ */
onProgress(): (progress: TransferProgressEvent) => void { onProgress(): (progress: TransferProgressEvent) => void {
return (progress: TransferProgressEvent) => { return (progress: TransferProgressEvent) => {
this.setReceivedBytes(progress.loadedBytes) if (progress.loadedBytes > this.getTransferredBytes()) {
this.setReceivedBytes(progress.loadedBytes);
this.setLastTimeOfNewBytes();
} else {
if (this.getLastTimeOfNewBytes !== undefined && Date.now() - this.getLastTimeOfNewBytes()! > 120000) { // if download hanging for more than 2 minutes
this.triggerAbortController();
throw new Error(
'Aborting cache download as the download has stalled for more than 2 minutes.'
)
}
}
} }
} }
@ -252,6 +285,7 @@ export async function downloadCacheStorageSDK(
try { try {
downloadProgress.startDisplayTimer() downloadProgress.startDisplayTimer()
const controller = new AbortController() const controller = new AbortController()
downloadProgress.setAbortController(controller)
const abortSignal = controller.signal const abortSignal = controller.signal
while (!downloadProgress.isDone()) { while (!downloadProgress.isDone()) {
const segmentStart = const segmentStart =