David Sehnal 6 jaren geleden
bovenliggende
commit
6066e3aedf

+ 31 - 0
src/mol-util/retry-if.ts

@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+export async function retryIf<T>(promiseProvider: () => Promise<T>, params: {
+    retryThenIf?: (result: T) => boolean,
+    retryCatchIf?: (error: any) => boolean,
+    retryCount: number
+}) {
+    let count = 0;
+    while (count <= params.retryCount) {
+        try {
+            const result = await promiseProvider();
+            if (params.retryThenIf && params.retryThenIf(result)) {
+                count++;
+                continue;
+            }
+            return result;
+        } catch (e) {
+            if (!params.retryCatchIf || params.retryCatchIf(e)) {
+                count++;
+                continue;
+            }
+            throw e;
+        }
+    }
+
+    throw new Error('Maximum retry count exceeded.');
+}

+ 5 - 5
src/servers/model/properties/structure-quality-report.ts

@@ -4,12 +4,12 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { ResidueIndex, ModelPropertyDescriptor, Model, Structure, Unit, StructureElement } from 'mol-model/structure';
-import fetch from 'node-fetch';
-import { CifWriter } from 'mol-io/writer/cif';
-import CifField = CifWriter.Field;
 import { Segmentation } from 'mol-data/int';
+import { CifWriter } from 'mol-io/writer/cif';
+import { Model, ModelPropertyDescriptor, ResidueIndex, Structure, StructureElement, Unit } from 'mol-model/structure';
 import { residueIdFields } from 'mol-model/structure/export/categories/atom_site';
+import { fetchRetry } from '../utils/fetch-retry';
+import CifField = CifWriter.Field;
 
 type IssueMap = Map<ResidueIndex, string[]>
 
@@ -99,7 +99,7 @@ export namespace StructureQualityReport {
         if (model.customProperties.has(Descriptor)) return true;
 
         const id = model.label.toLowerCase();
-        const rawData = await fetch(`https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/${model.label.toLowerCase()}`, { timeout: 1500 });
+        const rawData = await fetchRetry(`https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/${model.label.toLowerCase()}`, 1500, 5);
         const json = await rawData.json();
         const data = json[id];
         if (!data) return false;

+ 29 - 0
src/servers/model/utils/fetch-retry.ts

@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import fetch from 'node-fetch';
+import { retryIf } from 'mol-util/retry-if';
+
+const RETRIABLE_NETWORK_ERRORS = [
+    'ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT',
+    'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE', 'EAI_AGAIN'
+];
+
+function isRetriableNetworkError(error: any) {
+    return error && RETRIABLE_NETWORK_ERRORS.includes(error.code);
+}
+
+export async function fetchRetry(url: string, timeout: number, retryCount: number) {
+    const result = await retryIf(() => fetch(url, { timeout }), {
+        retryThenIf: r => r.status >= 500 && r.status < 600,
+        // TODO test retryCatchIf
+        retryCatchIf: e => isRetriableNetworkError(e),
+        retryCount
+    });
+
+    if (result.status >= 200 && result.status < 300) return result;
+    throw new Error(result.statusText);
+}