1
0
Fork 0

More detailed status information during large uploads

pull/367/head
Konrad Pabjan 2020-03-04 18:03:52 +01:00
parent 4d5d3f1362
commit 06beaf91d4
4 changed files with 45 additions and 21 deletions

View File

@ -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 {

View File

@ -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(

View File

@ -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)
} }
} }

View File

@ -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)
} }