Skip to content

Commit 185c840

Browse files
committed
fix(MongoBinaryDownload::attempDownload): add experimental feature "EXP_RESUME_DOWNLOAD"
1 parent b907cb0 commit 185c840

File tree

3 files changed

+51
-14
lines changed

3 files changed

+51
-14
lines changed

docs/api/config-options.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ Format: `https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.0.20
146146

147147
### DOWNLOAD_IGNORE_MISSING_HEADER
148148

149-
| Environment Variable | PackageJson |
150-
| :--------------------: | :-----------: |
149+
| Environment Variable | PackageJson |
150+
| :------------------------------: | :---------------------------: |
151151
| `DOWNLOAD_IGNORE_MISSING_HEADER` | `downloadIgnoreMissingHeader` |
152152

153153
Option `DOWNLOAD_IGNORE_MISSING_HEADER` can be set to `true` to ignore missing response headers like `content-length`.
@@ -239,9 +239,9 @@ Default: `false`
239239

240240
### MAX_REDIRECTS
241241

242-
| Environment Variable | PackageJson |
243-
| :------------------: | :---------: |
244-
| `MONGOMS_MAX_REDIRECTS` | `maxRedirects` |
242+
| Environment Variable | PackageJson |
243+
| :---------------------: | :------------: |
244+
| `MONGOMS_MAX_REDIRECTS` | `maxRedirects` |
245245

246246
Option `MAX_REDIRECTS` is used to set the maximal amount of redirects to follow
247247

@@ -271,16 +271,27 @@ Keep in mind that downloaded binaries will never be automatically deleted.
271271

272272
### MAX_RETRIES
273273

274-
| Environment Variable | PackageJson |
275-
| :------------------: | :---------: |
276-
| `MONGOMS_MAX_RETRIES` | `maxRetries` |
274+
| Environment Variable | PackageJson |
275+
| :-------------------: | :----------: |
276+
| `MONGOMS_MAX_RETRIES` | `maxRetries` |
277277

278278
Option `MAX_RETRIES` is used to set the maximum number of retry attempts for downloading binaries when a retryable error occurs.
279279

280280
Default: `3`
281281

282282
Set this to control how many times the downloader will attempt to recover from transient errors (like network issues) before failing.
283283

284+
### EXP_RESUME_DOWNLOAD
285+
286+
| Environment Variable | PackageJson |
287+
| :---------------------------: | :-----------------: |
288+
| `MONGOMS_EXP_RESUME_DOWNLOAD` | `expResumeDownload` |
289+
290+
Option `EXP_RESUME_DOWNLOAD` is used to enable / disable resuming a download of a binary instead of starting over on retries or interruptions.
291+
292+
This is a experimental option, it maybe removed, renamed or have changed behavior in the future and any version.
293+
294+
Default: `false`
284295

285296
## How to use them in the package.json
286297

packages/mongodb-memory-server-core/src/util/MongoBinaryDownload.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import MongoBinaryDownloadUrl from './MongoBinaryDownloadUrl';
1010
import { HttpsProxyAgent } from 'https-proxy-agent';
1111
import resolveConfig, { envToBool, ResolveConfigVariables } from './resolveConfig';
1212
import debug from 'debug';
13-
import { assertion, mkdir, pathExists, md5FromFile } from './utils';
13+
import { assertion, mkdir, pathExists, md5FromFile, statPath } from './utils';
1414
import { DryMongoBinary } from './DryMongoBinary';
1515
import { MongoBinaryOpts } from './MongoBinary';
1616
import { clearLine } from 'readline';
@@ -19,7 +19,7 @@ import { RequestOptions } from 'https';
1919

2020
const log = debug('MongoMS:MongoBinaryDownload');
2121

22-
const retryableStatusCodes = [429, 500, 503];
22+
const retryableStatusCodes = [416, 429, 500, 503];
2323

2424
const retryableErrorCodes = [
2525
'ECONNRESET',
@@ -460,17 +460,31 @@ export class MongoBinaryDownload {
460460
*/
461461
private async attemptDownload(
462462
url: URL,
463-
useHttpsOptions: Parameters<typeof https.get>[1],
463+
useHttpsOptions: RequestOptions,
464464
downloadLocation: string,
465465
tempDownloadLocation: string,
466466
downloadUrl: string,
467467
httpOptions: RequestOptions
468468
): Promise<string> {
469+
/** Offset to resume from; for now a non-0 value indicates to use file "append" mode */
470+
let offset = 0;
471+
472+
if (envToBool(resolveConfig(ResolveConfigVariables.EXP_RESUME_DOWNLOAD))) {
473+
const stat = await statPath(tempDownloadLocation);
474+
475+
if (stat && stat.size != 0) {
476+
useHttpsOptions.headers = useHttpsOptions.headers ?? {};
477+
useHttpsOptions.headers['Range'] = `bytes=${stat.size}-`;
478+
offset = stat.size;
479+
log(`httpDownload: resuming download at ${offset}`);
480+
}
481+
}
482+
469483
return new Promise((resolve, reject) => {
470484
log(`httpDownload: trying to download "${downloadUrl}"`);
471485

472-
const request = https.get(url, useHttpsOptions, (response) => {
473-
if (response.statusCode != 200) {
486+
const request = https.get(url, useHttpsOptions, async (response) => {
487+
if (response.statusCode != 200 && response.statusCode != 206) {
474488
if (response.statusCode === 403) {
475489
reject(
476490
new DownloadError(
@@ -487,6 +501,11 @@ export class MongoBinaryDownload {
487501
return;
488502
}
489503

504+
// delete temporary file on "416: Range Not Satisfiable" and let it re-try
505+
if (response.statusCode === 416) {
506+
await fspromises.rm(tempDownloadLocation, { force: true });
507+
}
508+
490509
reject(
491510
new DownloadError(
492511
downloadUrl,
@@ -499,6 +518,7 @@ export class MongoBinaryDownload {
499518
}
500519

501520
// content-length, otherwise 0
521+
// note that content-length will not be the full size when resuming a download via "Range"
502522
let contentLength: number;
503523

504524
if (typeof response.headers['content-length'] != 'string') {
@@ -533,7 +553,12 @@ export class MongoBinaryDownload {
533553
this.dlProgress.length = contentLength;
534554
this.dlProgress.totalMb = Math.round((this.dlProgress.length / 1048576) * 10) / 10;
535555

536-
const fileStream = createWriteStream(tempDownloadLocation);
556+
// "w" opens for write, creates the file if it does not exist, or truncates if it does
557+
// "a" opens for append, creates the file if it does not exist
558+
const flags = offset === 0 ? 'w' : 'a';
559+
560+
// not using option "start" as we already open in "append" mode
561+
const fileStream = createWriteStream(tempDownloadLocation, { /* start: offset, */ flags });
537562

538563
response.pipe(fileStream);
539564

packages/mongodb-memory-server-core/src/util/resolveConfig.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export enum ResolveConfigVariables {
3535
MAX_REDIRECTS = 'MAX_REDIRECTS',
3636
MAX_RETRIES = 'MAX_RETRIES', // Added for download retry configuration
3737
DISTRO = 'DISTRO',
38+
EXP_RESUME_DOWNLOAD = 'EXP_RESUME_DOWNLOAD',
3839
}
3940

4041
/** The Prefix for Environmental values */

0 commit comments

Comments
 (0)