mirror of https://github.com/actions/toolkit
More detailed status information during large uploads
parent
4d5d3f1362
commit
06beaf91d4
|
@ -11,10 +11,7 @@ export function getUploadRetryCount(): number {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRetryWaitTime(): number {
|
export function getRetryWaitTime(): number {
|
||||||
/*
|
return 10000
|
||||||
Timeout errors can happen when uploading large amounts of files due to the huge number of http calls being made
|
|
||||||
*/
|
|
||||||
return 30000
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDownloadFileConcurrency(): number {
|
export function getDownloadFileConcurrency(): number {
|
||||||
|
|
|
@ -132,7 +132,6 @@ export class UploadHttpClient {
|
||||||
parallelUploads.map(async index => {
|
parallelUploads.map(async index => {
|
||||||
while (currentFile < filesToUpload.length) {
|
while (currentFile < filesToUpload.length) {
|
||||||
const currentFileParameters = parameters[currentFile]
|
const currentFileParameters = parameters[currentFile]
|
||||||
this.statusReporter.incrementProcessedCount()
|
|
||||||
currentFile += 1
|
currentFile += 1
|
||||||
if (abortPendingFileUploads) {
|
if (abortPendingFileUploads) {
|
||||||
failedItemsToReport.push(currentFileParameters.file)
|
failedItemsToReport.push(currentFileParameters.file)
|
||||||
|
@ -161,6 +160,7 @@ export class UploadHttpClient {
|
||||||
abortPendingFileUploads = true
|
abortPendingFileUploads = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.statusReporter.incrementProcessedCount()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -217,9 +217,7 @@ export class UploadHttpClient {
|
||||||
|
|
||||||
// the entire file should be uploaded with a single call
|
// the entire file should be uploaded with a single call
|
||||||
if (uploadFileSize > parameters.maxChunkSize) {
|
if (uploadFileSize > parameters.maxChunkSize) {
|
||||||
throw new Error(
|
throw new Error('Chunk size is too large to upload with a single call')
|
||||||
'Chunk size is too large to upload with a single call'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this.uploadChunk(
|
const result = await this.uploadChunk(
|
||||||
|
@ -279,6 +277,11 @@ export class UploadHttpClient {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if an individual file is greater than 100MB (1024*1024*100) in size, display extra information about the upload status
|
||||||
|
if (uploadFileSize > 104857600){
|
||||||
|
this.statusReporter.updateLargeFileStatus(parameters.file, `Uploading ${parameters.file} (${((offset/uploadFileSize)*100).toFixed(1)}%)`)
|
||||||
|
}
|
||||||
|
|
||||||
const start = offset
|
const start = offset
|
||||||
const end = offset + chunkSize - 1
|
const end = offset + chunkSize - 1
|
||||||
offset += parameters.maxChunkSize
|
offset += parameters.maxChunkSize
|
||||||
|
@ -374,10 +377,10 @@ export class UploadHttpClient {
|
||||||
while (retryCount <= retryLimit) {
|
while (retryCount <= retryLimit) {
|
||||||
try {
|
try {
|
||||||
const response = await uploadChunkRequest()
|
const response = await uploadChunkRequest()
|
||||||
// read the body to properly drain the response before possibly reusing the connection
|
|
||||||
await response.readBody()
|
|
||||||
|
|
||||||
if (isSuccessStatusCode(response.message.statusCode)) {
|
if (isSuccessStatusCode(response.message.statusCode)) {
|
||||||
|
// read the body to properly drain the response before possibly reusing the connection
|
||||||
|
await response.readBody()
|
||||||
return true
|
return true
|
||||||
} else if (isRetryableStatusCode(response.message.statusCode)) {
|
} else if (isRetryableStatusCode(response.message.statusCode)) {
|
||||||
// dispose the existing connection and create a new one
|
// dispose the existing connection and create a new one
|
||||||
|
@ -390,7 +393,7 @@ export class UploadHttpClient {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
info(
|
info(
|
||||||
`HTTP ${response.message.statusCode} during chunk upload, will retry at offset ${start} after 10 seconds. Retry count #${retryCount}. URL ${resourceUrl}`
|
`HTTP ${response.message.statusCode} during chunk upload, will retry at offset ${start} after ${getRetryWaitTime} milliseconds. Retry count #${retryCount}. URL ${resourceUrl}`
|
||||||
)
|
)
|
||||||
await new Promise(resolve =>
|
await new Promise(resolve =>
|
||||||
setTimeout(resolve, getRetryWaitTime())
|
setTimeout(resolve, getRetryWaitTime())
|
||||||
|
@ -407,6 +410,9 @@ export class UploadHttpClient {
|
||||||
// if an error is thrown, it is most likely due to a timeout, dispose of the connection, wait and retry with a new connection
|
// if an error is thrown, it is most likely due to a timeout, dispose of the connection, wait and retry with a new connection
|
||||||
this.uploadHttpManager.disposeClient(httpClientIndex)
|
this.uploadHttpManager.disposeClient(httpClientIndex)
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error)
|
||||||
|
|
||||||
retryCount++
|
retryCount++
|
||||||
if (retryCount > retryLimit) {
|
if (retryCount > retryLimit) {
|
||||||
info(
|
info(
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import {info} from '@actions/core'
|
import {info} from '@actions/core'
|
||||||
|
|
||||||
// Displays information about the progress/status of an artifact being uploaded
|
// displays information about the progress/status of an artifact being uploaded
|
||||||
export class UploadStatusReporter {
|
export class UploadStatusReporter {
|
||||||
private displayDelay = 10000
|
|
||||||
private totalNumberOfFilesToUpload = 0
|
private totalNumberOfFilesToUpload = 0
|
||||||
private processedCount = 0
|
private processedCount = 0
|
||||||
private uploadStatus: NodeJS.Timeout | undefined
|
private largeUploads = new Map<string, string>()
|
||||||
|
private totalUploadStatus: NodeJS.Timeout | undefined
|
||||||
|
private largeFileUploadStatus: NodeJS.Timeout | undefined
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.uploadStatus = undefined
|
this.totalUploadStatus = undefined
|
||||||
|
this.largeFileUploadStatus = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
setTotalNumberOfFilesToUpload(fileTotal: number): void {
|
setTotalNumberOfFilesToUpload(fileTotal: number): void {
|
||||||
|
@ -17,7 +19,9 @@ export class UploadStatusReporter {
|
||||||
|
|
||||||
startDisplayingStatus(): void {
|
startDisplayingStatus(): void {
|
||||||
const _this = this
|
const _this = this
|
||||||
this.uploadStatus = setInterval(function() {
|
|
||||||
|
// displays information about the total upload status every 10 seconds
|
||||||
|
this.totalUploadStatus = setInterval(function() {
|
||||||
info(
|
info(
|
||||||
`Total file(s): ${
|
`Total file(s): ${
|
||||||
_this.totalNumberOfFilesToUpload
|
_this.totalNumberOfFilesToUpload
|
||||||
|
@ -26,12 +30,30 @@ export class UploadStatusReporter {
|
||||||
100
|
100
|
||||||
).toFixed(1)}%)`
|
).toFixed(1)}%)`
|
||||||
)
|
)
|
||||||
}, this.displayDelay)
|
}, 10000)
|
||||||
|
|
||||||
|
// displays extra information about any large files that take a significant amount of time to upload every 1 second
|
||||||
|
this.largeFileUploadStatus = setInterval(function() {
|
||||||
|
for (const value of Array.from(_this.largeUploads.values())) {
|
||||||
|
info(value)
|
||||||
|
}
|
||||||
|
// delete all entires in the map after displaying the information so it will not be displayed again unless explicitly added
|
||||||
|
_this.largeUploads = new Map<string, string>()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLargeFileStatus(fileName: string, displayInformation: string): void {
|
||||||
|
// any previously added display information should be overwritten for the specific large file because a map is being used
|
||||||
|
this.largeUploads.set(fileName, displayInformation)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopDisplayingStatus(): void {
|
stopDisplayingStatus(): void {
|
||||||
if (this.uploadStatus) {
|
if (this.totalUploadStatus) {
|
||||||
clearInterval(this.uploadStatus)
|
clearInterval(this.totalUploadStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.largeFileUploadStatus) {
|
||||||
|
clearInterval(this.largeFileUploadStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,7 @@ export function isRetryableStatusCode(statusCode?: number): boolean {
|
||||||
const retryableStatusCodes = [
|
const retryableStatusCodes = [
|
||||||
HttpCodes.BadGateway,
|
HttpCodes.BadGateway,
|
||||||
HttpCodes.ServiceUnavailable,
|
HttpCodes.ServiceUnavailable,
|
||||||
HttpCodes.GatewayTimeout,
|
HttpCodes.GatewayTimeout
|
||||||
HttpCodes.InternalServerError
|
|
||||||
]
|
]
|
||||||
return retryableStatusCodes.includes(statusCode)
|
return retryableStatusCodes.includes(statusCode)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue