Browse Source

ModelServer error handling

David Sehnal 6 years ago
parent
commit
c636caab0b

+ 57 - 33
src/servers/model/server/query.ts

@@ -31,43 +31,59 @@ export async function resolveJob(job: Job, writer: Writer) {
 
     const wrappedStructure = await getStructure(job);
 
-    perf.start('query');
-    // TODO: encode errors that happen past this point as CIF rather than just 404
-    const structure = job.queryDefinition.structureTransform
-        ? await job.queryDefinition.structureTransform(job.normalizedParams, wrappedStructure.structure)
-        : wrappedStructure.structure;
-    const query = job.queryDefinition.query(job.normalizedParams, structure);
-    const result = StructureSelection.unionStructure(StructureQuery.run(query, structure, Config.maxQueryTimeInMs));
-    perf.end('query');
-
-    ConsoleLogger.logId(job.id, 'Query', 'Query finished.');
+    let startedWriting = false;
+    try {
+        const encoder = CifWriter.createEncoder({ binary: job.responseFormat.isBinary, encoderName: `ModelServer ${Version}` });
+        perf.start('query');
+        const structure = job.queryDefinition.structureTransform
+            ? await job.queryDefinition.structureTransform(job.normalizedParams, wrappedStructure.structure)
+            : wrappedStructure.structure;
+        const query = job.queryDefinition.query(job.normalizedParams, structure);
+        const result = await StructureSelection.unionStructure(StructureQuery.run(query, structure, Config.maxQueryTimeInMs));
+        perf.end('query');
+
+        ConsoleLogger.logId(job.id, 'Query', 'Query finished.');
+
+        perf.start('encode');
+        encoder.startDataBlock(structure.units[0].model.label.toUpperCase());
+        encoder.writeCategory(_model_server_result, [job]);
+        encoder.writeCategory(_model_server_params, [job]);
+
+        // encoder.setFilter(mmCIF_Export_Filters.onlyPositions);
+        encode_mmCIF_categories(encoder, result);
+        // encoder.setFilter();
+        perf.end('encode');
+
+        ConsoleLogger.logId(job.id, 'Query', 'Encoded.');
+
+        const stats: Stats = {
+            structure: wrappedStructure,
+            queryTimeMs: perf.time('query'),
+            encodeTimeMs: perf.time('encode')
+        };
+
+        encoder.writeCategory(_model_server_stats, [stats]);
+        encoder.encode();
+        startedWriting = true;
+        encoder.writeTo(writer);
+        ConsoleLogger.logId(job.id, 'Query', 'Written.');
+    } catch (e) {
+        ConsoleLogger.errorId(job.id, e);
+        if (!startedWriting) {
+            doError(job, writer, e);
+        } else {
+            ConsoleLogger.errorId(job.id, 'Error was not relayed to the user because it happened during "write".');
+        }
+    }
+}
 
+function doError(job: Job, writer: Writer, e: any) {
     const encoder = CifWriter.createEncoder({ binary: job.responseFormat.isBinary, encoderName: `ModelServer ${Version}` });
-
-    perf.start('encode');
-    encoder.startDataBlock(structure.units[0].model.label.toUpperCase());
     encoder.writeCategory(_model_server_result, [job]);
     encoder.writeCategory(_model_server_params, [job]);
-
-    // encoder.setFilter(mmCIF_Export_Filters.onlyPositions);
-    encode_mmCIF_categories(encoder, result);
-    // encoder.setFilter();
-    perf.end('encode');
-
-    ConsoleLogger.logId(job.id, 'Query', 'Encoded.');
-
-    const stats: Stats = {
-        structure: wrappedStructure,
-        queryTimeMs: perf.time('query'),
-        encodeTimeMs: perf.time('encode')
-    };
-
-    encoder.writeCategory(_model_server_stats, [stats]);
+    encoder.writeCategory(_model_server_error, ['' + e]);
     encoder.encode();
-
     encoder.writeTo(writer);
-
-    ConsoleLogger.logId(job.id, 'Query', 'Written.');
 }
 
 const maxTime = Config.maxQueryTimeInMs;
@@ -88,7 +104,7 @@ function int32<T>(name: string, value: (data: T) => number): CifField<number, T>
     return CifField.int(name, (i, d) => value(d));
 }
 
-const _model_server_result_fields: CifField<number, Job>[] = [
+const _model_server_result_fields: CifField<any, Job>[] = [
     string<Job>('job_id', ctx => '' + ctx.id),
     string<Job>('datetime_utc', ctx => ctx.datetime_utc),
     string<Job>('server_version', ctx => Version),
@@ -102,6 +118,10 @@ const _model_server_params_fields: CifField<number, string[]>[] = [
     string<string[]>('value', (ctx, i) => ctx[i][1])
 ];
 
+const _model_server_error_fields: CifField<number, string>[] = [
+    string<string>('message', (ctx, i) => ctx)
+];
+
 const _model_server_stats_fields: CifField<number, Stats>[] = [
     int32<Stats>('io_time_ms', ctx => ctx.structure.info.readTime | 0),
     int32<Stats>('parse_time_ms', ctx => ctx.structure.info.parseTime | 0),
@@ -110,12 +130,16 @@ const _model_server_stats_fields: CifField<number, Stats>[] = [
     int32<Stats>('encode_time_ms', ctx => ctx.encodeTimeMs | 0)
 ];
 
-
 const _model_server_result: CifWriter.Category<Job> = {
     name: 'model_server_result',
     instance: (job) => ({ data: job, fields: _model_server_result_fields, rowCount: 1 })
 };
 
+const _model_server_error: CifWriter.Category<string> = {
+    name: 'model_server_error',
+    instance: (message) => ({ data: message, fields: _model_server_error_fields, rowCount: 1 })
+};
+
 const _model_server_params: CifWriter.Category<Job> = {
     name: 'model_server_params',
     instance(job) {

+ 9 - 1
src/servers/model/server/structure-wrapper.ts

@@ -13,6 +13,7 @@ import * as util from 'util'
 import * as fs from 'fs'
 import * as zlib from 'zlib'
 import { Job } from './jobs';
+import { ConsoleLogger } from 'mol-util/console-logger';
 
 require('util.promisify').shim();
 
@@ -87,7 +88,14 @@ async function readStructure(key: string, sourceId: string, entryId: string) {
     if (!fs.existsSync(filename)) throw new Error(`Could not map '${key}' to an existing file.`);
 
     perf.start('read');
-    const data = await readFile(filename);
+    let data;
+    try {
+        data = await readFile(filename);
+    } catch (e) {
+        ConsoleLogger.error(key, '' + e);
+        throw new Error(`Could not read the file for '${key}' from disk.`);
+    }
+
     perf.end('read');
     perf.start('parse');
     const frame = (await parseCif(data)).blocks[0];

+ 1 - 1
src/servers/model/server/web-api.ts

@@ -69,12 +69,12 @@ async function processNextJob() {
     try {
         writer.writeHeader(job.responseFormat.isBinary);
         await resolveJob(job, writer);
-        writer.end();
     } catch (e) {
         ConsoleLogger.errorId(job.id, '' + e);
         // TODO: add some error?
         writer.doError(404);
     } finally {
+        writer.end();
         setImmediate(processNextJob);
     }
 }