1
0
Fork 0

@actions/cache: using concurrent download whenever server supported (via `Accept-Ranges` header)

pull/1835/head
Đặng Minh Dũng 2024-09-30 15:40:01 +07:00
parent 6dd369c0e6
commit 732400a59e
No known key found for this signature in database
GPG Key ID: 30B00965500D018B
2 changed files with 46 additions and 35 deletions

View File

@ -13,12 +13,12 @@ import * as utils from './cacheUtils'
import {CompressionMethod} from './constants'
import {
ArtifactCacheEntry,
InternalCacheOptions,
ArtifactCacheList,
CommitCacheRequest,
ReserveCacheRequest,
ReserveCacheResponse,
InternalCacheOptions,
ITypedResponseWithError,
ArtifactCacheList
ReserveCacheRequest,
ReserveCacheResponse
} from './contracts'
import {
downloadCacheHttpClient,
@ -27,9 +27,9 @@ import {
} from './downloadUtils'
import {
DownloadOptions,
UploadOptions,
getDownloadOptions,
getUploadOptions
getUploadOptions,
UploadOptions
} from '../options'
import {
isSuccessStatusCode,
@ -179,22 +179,45 @@ export async function downloadCache(
if (archiveUrl.hostname.endsWith('.blob.core.windows.net')) {
if (downloadOptions.useAzureSdk) {
// Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability.
await downloadCacheStorageSDK(
return await downloadCacheStorageSDK(
archiveLocation,
archivePath,
downloadOptions
)
} else if (downloadOptions.concurrentBlobDownloads) {
}
}
let acceptRange = false
let contentLength = -1
// Determine partial file downloads is supported by server
// via `Accept-Ranges: bytes` response header.
try {
const httpClient = new HttpClient('actions/cache', undefined, {
socketTimeout: downloadOptions.timeoutInMs,
keepAlive: true
})
const res = await retryHttpClientResponse(
'downloadCacheMetadata',
async () => await httpClient.request('HEAD', archiveLocation, null, {})
)
acceptRange = res.message.headers['Accept-Ranges'] === 'bytes'
const lengthHeader = res.message.headers['Content-Length']
contentLength = parseInt(lengthHeader)
} catch {
// ignore
}
if (acceptRange && contentLength > 0) {
// Use concurrent implementation with HttpClient to work around blob SDK issue
await downloadCacheHttpClientConcurrent(
archiveLocation,
archivePath,
contentLength,
downloadOptions
)
} else {
// Otherwise, download using the Actions http-client.
await downloadCacheHttpClient(archiveLocation, archivePath)
}
} else {
await downloadCacheHttpClient(archiveLocation, archivePath)
}

View File

@ -208,10 +208,13 @@ export async function downloadCacheHttpClient(
*
* @param archiveLocation the URL for the cache
* @param archivePath the local path where the cache is saved
* @param contentLength
* @param options
*/
export async function downloadCacheHttpClientConcurrent(
archiveLocation: string,
archivePath: fs.PathLike,
contentLength: number,
options: DownloadOptions
): Promise<void> {
const archiveDescriptor = await fs.promises.open(archivePath, 'w')
@ -220,29 +223,14 @@ export async function downloadCacheHttpClientConcurrent(
keepAlive: true
})
try {
const res = await retryHttpClientResponse(
'downloadCacheMetadata',
async () => await httpClient.request('HEAD', archiveLocation, null, {})
)
const lengthHeader = res.message.headers['content-length']
if (lengthHeader === undefined || lengthHeader === null) {
throw new Error('Content-Length not found on blob response')
}
const length = parseInt(lengthHeader)
if (Number.isNaN(length)) {
throw new Error(`Could not interpret Content-Length: ${length}`)
}
const downloads: {
offset: number
promiseGetter: () => Promise<DownloadSegment>
}[] = []
const blockSize = 4 * 1024 * 1024
for (let offset = 0; offset < length; offset += blockSize) {
const count = Math.min(blockSize, length - offset)
for (let offset = 0; offset < contentLength; offset += blockSize) {
const count = Math.min(blockSize, contentLength - offset)
downloads.push({
offset,
promiseGetter: async () => {
@ -260,7 +248,7 @@ export async function downloadCacheHttpClientConcurrent(
downloads.reverse()
let actives = 0
let bytesDownloaded = 0
const progress = new DownloadProgress(length)
const progress = new DownloadProgress(contentLength)
progress.startDisplayTimer()
const progressFn = progress.onProgress()