querysub 0.453.0 → 0.454.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -509,7 +509,10 @@ export class ArchivesBackblaze {
|
|
|
509
509
|
private last503Reset = 0;
|
|
510
510
|
// IMPORTANT! We must always CATCH AROUND the apiRetryLogic, NEVER inside of fnc. Otherwise we won't
|
|
511
511
|
// be able to recreate the auth token.
|
|
512
|
+
// `context` is a short label (verb + file path) included in every retry/error log so a stuck
|
|
513
|
+
// silent-retry loop is identifiable from the logs.
|
|
512
514
|
private async apiRetryLogic<T>(
|
|
515
|
+
context: string,
|
|
513
516
|
fnc: (api: B2Api) => Promise<T>,
|
|
514
517
|
retries = 3
|
|
515
518
|
): Promise<T> {
|
|
@@ -519,33 +522,33 @@ export class ArchivesBackblaze {
|
|
|
519
522
|
} catch (err: any) {
|
|
520
523
|
if (retries <= 0) throw err;
|
|
521
524
|
|
|
522
|
-
// If it's a 503 and it's been a minute since we last reset, then Wait and reset.
|
|
525
|
+
// If it's a 503 and it's been a minute since we last reset, then Wait and reset.
|
|
523
526
|
if (
|
|
524
527
|
(err.stack.includes(`"status": 503`)
|
|
525
528
|
|| err.stack.includes(`"service_unavailable"`)
|
|
526
529
|
|| err.stack.includes(`"internal_error"`)
|
|
527
530
|
|| err.stack.includes(`ENOBUFS`)
|
|
528
531
|
) && Date.now() - this.last503Reset > 60 * 1000) {
|
|
529
|
-
console.error(
|
|
530
|
-
this.log(
|
|
532
|
+
console.error(`[${context}] 503 error, waiting and resetting: ${err.message}`);
|
|
533
|
+
this.log(`[${context}] 503 error, waiting and resetting: ${err.message}`);
|
|
531
534
|
await delay(10 * 1000);
|
|
532
|
-
// We check again in case, and in the very likely case that this is being run in parallel, we only want to reset once.
|
|
535
|
+
// We check again in case, and in the very likely case that this is being run in parallel, we only want to reset once.
|
|
533
536
|
if (Date.now() - this.last503Reset > 60 * 1000) {
|
|
534
|
-
this.log(
|
|
537
|
+
this.log(`[${context}] Resetting getAPI and getBucketAPI: ${err.message}`);
|
|
535
538
|
this.last503Reset = Date.now();
|
|
536
539
|
getAPI.reset();
|
|
537
540
|
this.getBucketAPI.reset();
|
|
538
541
|
}
|
|
539
|
-
return this.apiRetryLogic(fnc, retries - 1);
|
|
542
|
+
return this.apiRetryLogic(context, fnc, retries - 1);
|
|
540
543
|
}
|
|
541
544
|
|
|
542
545
|
// If the error is that the authorization token is invalid, reset getBucketAPI and getAPI
|
|
543
546
|
// If the error is that the bucket isn't found, reset getBucketAPI
|
|
544
547
|
if (err.stack.includes(`"expired_auth_token"`)) {
|
|
545
|
-
this.log(
|
|
548
|
+
this.log(`[${context}] Authorization token expired`);
|
|
546
549
|
getAPI.reset();
|
|
547
550
|
this.getBucketAPI.reset();
|
|
548
|
-
return this.apiRetryLogic(fnc, retries - 1);
|
|
551
|
+
return this.apiRetryLogic(context, fnc, retries - 1);
|
|
549
552
|
}
|
|
550
553
|
|
|
551
554
|
if (
|
|
@@ -560,10 +563,10 @@ export class ArchivesBackblaze {
|
|
|
560
563
|
|| err.stack.includes(`ECONNREFUSED`)
|
|
561
564
|
|| err.stack.includes(`ENOBUFS`)
|
|
562
565
|
) {
|
|
563
|
-
console.error(
|
|
564
|
-
this.log(err.message
|
|
566
|
+
console.error(`[${context}] Retrying in 5s: ${err.message}`);
|
|
567
|
+
this.log(`[${context}] ${err.message} retrying in 5s`);
|
|
565
568
|
await delay(5000);
|
|
566
|
-
return this.apiRetryLogic(fnc, retries - 1);
|
|
569
|
+
return this.apiRetryLogic(context, fnc, retries - 1);
|
|
567
570
|
}
|
|
568
571
|
|
|
569
572
|
if (err.stack.includes(`getaddrinfo ENOTFOUND`)) {
|
|
@@ -579,10 +582,11 @@ export class ArchivesBackblaze {
|
|
|
579
582
|
resolve(addresses);
|
|
580
583
|
});
|
|
581
584
|
});
|
|
582
|
-
console.error(`getaddrinfo ENOTFOUND ${hostname}`, { lookupAddresses, resolveAddresses, apiUrl: api.apiUrl, fullError: err.stack });
|
|
585
|
+
console.error(`[${context}] getaddrinfo ENOTFOUND ${hostname}`, { lookupAddresses, resolveAddresses, apiUrl: api.apiUrl, fullError: err.stack });
|
|
583
586
|
}
|
|
584
587
|
|
|
585
588
|
// TODO: Handle if the bucket is deleted?
|
|
589
|
+
console.error(`[${context}] giving up after ${3 - retries + 1} attempts: ${err.stack ?? err}`);
|
|
586
590
|
throw err;
|
|
587
591
|
}
|
|
588
592
|
}
|
|
@@ -597,7 +601,7 @@ export class ArchivesBackblaze {
|
|
|
597
601
|
setTimeout(downloadPoll, 5000);
|
|
598
602
|
};
|
|
599
603
|
setTimeout(downloadPoll, 5000);
|
|
600
|
-
let result = await this.apiRetryLogic(async (api) => {
|
|
604
|
+
let result = await this.apiRetryLogic(`get ${fileName}`, async (api) => {
|
|
601
605
|
let range = config?.range;
|
|
602
606
|
if (range) {
|
|
603
607
|
let fileInfo = await this.getInfo(fileName);
|
|
@@ -649,7 +653,7 @@ export class ArchivesBackblaze {
|
|
|
649
653
|
public async set(fileName: string, data: Buffer): Promise<void> {
|
|
650
654
|
this.log(`backblaze upload (${formatNumber(data.length)}B) ${fileName}`);
|
|
651
655
|
let f = fileName;
|
|
652
|
-
await this.apiRetryLogic(async (api) => {
|
|
656
|
+
await this.apiRetryLogic(`uploadFile ${fileName}`, async (api) => {
|
|
653
657
|
await api.uploadFile({ bucketId: this.bucketId, fileName, data: data, });
|
|
654
658
|
});
|
|
655
659
|
let existsChecks = 30;
|
|
@@ -677,7 +681,7 @@ export class ArchivesBackblaze {
|
|
|
677
681
|
public async del(fileName: string): Promise<void> {
|
|
678
682
|
this.log(`backblaze delete ${fileName}`);
|
|
679
683
|
try {
|
|
680
|
-
await this.apiRetryLogic(async (api) => {
|
|
684
|
+
await this.apiRetryLogic(`hideFile ${fileName}`, async (api) => {
|
|
681
685
|
await api.hideFile({ bucketId: this.bucketId, fileName: fileName });
|
|
682
686
|
});
|
|
683
687
|
} catch (e: any) {
|
|
@@ -746,7 +750,7 @@ export class ArchivesBackblaze {
|
|
|
746
750
|
dataQueue.unshift(data, secondData);
|
|
747
751
|
|
|
748
752
|
|
|
749
|
-
let uploadInfo = await this.apiRetryLogic(async (api) => {
|
|
753
|
+
let uploadInfo = await this.apiRetryLogic(`startLargeFile ${fileName}`, async (api) => {
|
|
750
754
|
return await api.startLargeFile({
|
|
751
755
|
bucketId: this.bucketId,
|
|
752
756
|
fileName: fileName,
|
|
@@ -755,7 +759,7 @@ export class ArchivesBackblaze {
|
|
|
755
759
|
});
|
|
756
760
|
});
|
|
757
761
|
onError.push(async () => {
|
|
758
|
-
await this.apiRetryLogic(async (api) => {
|
|
762
|
+
await this.apiRetryLogic(`cancelLargeFile ${fileName}`, async (api) => {
|
|
759
763
|
await api.cancelLargeFile({ fileId: uploadInfo.fileId });
|
|
760
764
|
});
|
|
761
765
|
});
|
|
@@ -792,7 +796,7 @@ export class ArchivesBackblaze {
|
|
|
792
796
|
sha1.update(data);
|
|
793
797
|
let sha1Hex = sha1.digest("hex");
|
|
794
798
|
partSha1Array.push(sha1Hex);
|
|
795
|
-
await this.apiRetryLogic(async (api) => {
|
|
799
|
+
await this.apiRetryLogic(`uploadPart#${partNumber} ${fileName}`, async (api) => {
|
|
796
800
|
if (!data) throw new Error("Impossible, data is undefined");
|
|
797
801
|
|
|
798
802
|
let timeStr = formatTime(Date.now() - time);
|
|
@@ -818,7 +822,7 @@ export class ArchivesBackblaze {
|
|
|
818
822
|
}
|
|
819
823
|
this.log(`Finished uploading large file uploaded ${green(formatNumber(totalBytes))}B`);
|
|
820
824
|
|
|
821
|
-
await this.apiRetryLogic(async (api) => {
|
|
825
|
+
await this.apiRetryLogic(`finishLargeFile ${fileName}`, async (api) => {
|
|
822
826
|
await api.finishLargeFile({
|
|
823
827
|
fileId: uploadInfo.fileId,
|
|
824
828
|
partSha1Array: partSha1Array,
|
|
@@ -838,7 +842,7 @@ export class ArchivesBackblaze {
|
|
|
838
842
|
}
|
|
839
843
|
|
|
840
844
|
public async getInfo(fileName: string): Promise<{ writeTime: number; size: number; } | undefined> {
|
|
841
|
-
return await this.apiRetryLogic(async (api) => {
|
|
845
|
+
return await this.apiRetryLogic(`getInfo ${fileName}`, async (api) => {
|
|
842
846
|
try {
|
|
843
847
|
// NOTE: Apparently, there's no other way to do this, as the file name does not equal the file ID, and git file info requires the file ID.
|
|
844
848
|
let info = await api.listFileNames({ bucketId: this.bucketId, prefix: fileName, maxFileCount: 10 });
|
|
@@ -868,7 +872,7 @@ export class ArchivesBackblaze {
|
|
|
868
872
|
return result.map(x => x.path);
|
|
869
873
|
}
|
|
870
874
|
public async findInfo(prefix: string, config?: { shallow?: boolean; type: "files" | "folders" }): Promise<{ path: string; createTime: number; size: number; }[]> {
|
|
871
|
-
return await this.apiRetryLogic(async (api) => {
|
|
875
|
+
return await this.apiRetryLogic(`findInfo ${prefix}`, async (api) => {
|
|
872
876
|
if (!config?.shallow && config?.type === "folders") {
|
|
873
877
|
let allFiles = await this.findInfo(prefix);
|
|
874
878
|
let allFolders = new Map<string, { path: string; createTime: number; size: number }>();
|
|
@@ -935,7 +939,7 @@ export class ArchivesBackblaze {
|
|
|
935
939
|
if (target instanceof ArchivesBackblaze) {
|
|
936
940
|
let targetBucketId = target.bucketId;
|
|
937
941
|
if (targetBucketId === this.bucketId && path === targetPath) return;
|
|
938
|
-
await this.apiRetryLogic(async (api) => {
|
|
942
|
+
await this.apiRetryLogic(`move ${path} -> ${targetPath}`, async (api) => {
|
|
939
943
|
// Ugh... listing the file name sucks, but... I guess it's still better than
|
|
940
944
|
// downloading and re-uploading the entire file.
|
|
941
945
|
let info = await api.listFileNames({ bucketId: this.bucketId, prefix: path, maxFileCount: 10 });
|
|
@@ -974,7 +978,7 @@ export class ArchivesBackblaze {
|
|
|
974
978
|
}
|
|
975
979
|
|
|
976
980
|
public async getURL(path: string) {
|
|
977
|
-
return await this.apiRetryLogic(async (api) => {
|
|
981
|
+
return await this.apiRetryLogic(`getURL ${path}`, async (api) => {
|
|
978
982
|
if (path.startsWith("/")) {
|
|
979
983
|
path = path.slice(1);
|
|
980
984
|
}
|
|
@@ -996,7 +1000,7 @@ export class ArchivesBackblaze {
|
|
|
996
1000
|
fileNamePrefix: string;
|
|
997
1001
|
authorizationToken: string;
|
|
998
1002
|
}> {
|
|
999
|
-
return await this.apiRetryLogic(async (api) => {
|
|
1003
|
+
return await this.apiRetryLogic(`getDownloadAuthorization ${config.fileNamePrefix ?? ""}`, async (api) => {
|
|
1000
1004
|
return await api.getDownloadAuthorization({
|
|
1001
1005
|
bucketId: this.bucketId,
|
|
1002
1006
|
fileNamePrefix: config.fileNamePrefix ?? "",
|
|
@@ -208,11 +208,12 @@ export class PathValueArchives {
|
|
|
208
208
|
let fullPath = pathIdentifier + "/" + file;
|
|
209
209
|
console.log(`Write archive file ${fullPath}, with size ${formatNumber(data.byteLength)}B, and count ${formatNumber(values.length)}`);
|
|
210
210
|
try {
|
|
211
|
+
|
|
211
212
|
await retryFunctional(() => archives().set(fullPath, data), {
|
|
212
213
|
maxRetries: 10,
|
|
213
214
|
minDelay: 1000,
|
|
214
215
|
maxDelay: 5000,
|
|
215
|
-
});
|
|
216
|
+
})();
|
|
216
217
|
} catch (e) {
|
|
217
218
|
console.error(`Error writing archive file ${fullPath}. THIS IS BAD! WE ARE SHUTTING DOWN SO THIS ERROR IS LOUDER! WHAT'S THE POINT OF A DB THAT DOESN'T SAVE DATA!: ${(e as Error).stack ?? e}`);
|
|
218
219
|
await delay(5000);
|
|
@@ -420,10 +421,10 @@ export class PathValueArchives {
|
|
|
420
421
|
skipValues: config.skipValues,
|
|
421
422
|
});
|
|
422
423
|
if (dataValues.length !== decodedObj.valueCount) {
|
|
423
|
-
console.
|
|
424
|
+
console.warn(`Bad archive data file at ${config.path}, Decoded count ${formatNumber(decodedObj.valueCount)} !== count in file name ${formatNumber(dataValues.length)} (${decodedObj.valueCount} !== ${dataValues.length})`);
|
|
424
425
|
}
|
|
425
426
|
} catch (e: any) {
|
|
426
|
-
console.
|
|
427
|
+
console.warn(`Bad archive data file at ${config.path}, error: ${e.stack}`);
|
|
427
428
|
}
|
|
428
429
|
let rawCount = dataValues.length;
|
|
429
430
|
|