diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts index bba3bf51e541fd67ca8bdbb020c13bf518d10444..a134c16826c518590f75535f5280cc365d49f29a 100644 --- a/src/servers/model/server/query.ts +++ b/src/servers/model/server/query.ts @@ -6,7 +6,6 @@ import { Column } from 'mol-data/db'; import { CifWriter } from 'mol-io/writer/cif'; -import Writer from 'mol-io/writer/writer'; import { StructureQuery, StructureSelection } from 'mol-model/structure'; import { encode_mmCIF_categories } from 'mol-model/structure/export/mmcif'; import { now, Progress } from 'mol-task'; @@ -26,12 +25,11 @@ export interface Stats { const perf = new PerformanceMonitor(); -export async function resolveJob(job: Job, writer: Writer) { +export async function resolveJob(job: Job): Promise<CifWriter.Encoder<any>> { ConsoleLogger.logId(job.id, 'Query', 'Starting.'); const wrappedStructure = await getStructure(job); - let startedWriting = false; try { const encoder = CifWriter.createEncoder({ binary: job.responseFormat.isBinary, encoderName: `ModelServer ${Version}` }); perf.start('query'); @@ -54,8 +52,6 @@ export async function resolveJob(job: Job, writer: Writer) { // encoder.setFilter(); perf.end('encode'); - ConsoleLogger.logId(job.id, 'Query', 'Encoded.'); - const stats: Stats = { structure: wrappedStructure, queryTimeMs: perf.time('query'), @@ -64,26 +60,21 @@ export async function resolveJob(job: Job, writer: Writer) { encoder.writeCategory(_model_server_stats, [stats]); encoder.encode(); - startedWriting = true; - encoder.writeTo(writer); - ConsoleLogger.logId(job.id, 'Query', 'Written.'); + ConsoleLogger.logId(job.id, 'Query', 'Encoded.'); + return encoder; } 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".'); - } + return doError(job, e); } } -function doError(job: Job, writer: Writer, e: any) { +function doError(job: Job, e: any) { const encoder = CifWriter.createEncoder({ binary: job.responseFormat.isBinary, encoderName: `ModelServer ${Version}` }); encoder.writeCategory(_model_server_result, [job]); encoder.writeCategory(_model_server_params, [job]); encoder.writeCategory(_model_server_error, ['' + e]); encoder.encode(); - encoder.writeTo(writer); + return encoder; } const maxTime = Config.maxQueryTimeInMs; diff --git a/src/servers/model/server/structure-wrapper.ts b/src/servers/model/server/structure-wrapper.ts index 2190b602ab3d752a2d9427ce9a16e45c341d574f..72bc68992d12d99716441beb557ab8ad100d6c38 100644 --- a/src/servers/model/server/structure-wrapper.ts +++ b/src/servers/model/server/structure-wrapper.ts @@ -85,7 +85,7 @@ async function parseCif(data: string|Uint8Array) { async function readStructure(key: string, sourceId: string, entryId: string) { const filename = sourceId === '_local_' ? entryId : Config.mapFile(sourceId, entryId); if (!filename) throw new Error(`Cound not map '${key}' to a valid filename.`); - if (!fs.existsSync(filename)) throw new Error(`Could not map '${key}' to an existing file.`); + if (!fs.existsSync(filename)) throw new Error(`Could not find source file for '${key}'.`); perf.start('read'); let data; diff --git a/src/servers/model/server/web-api.ts b/src/servers/model/server/web-api.ts index 61eee63eb68b502e932cd31ea5fb0a641fd442a6..ca96bbbe570eed8a552621917d6de3a93ee1791e 100644 --- a/src/servers/model/server/web-api.ts +++ b/src/servers/model/server/web-api.ts @@ -18,9 +18,9 @@ function makePath(p: string) { function wrapResponse(fn: string, res: express.Response) { const w = { - doError(this: any, code = 404) { + doError(this: any, code = 404, message = 'Not Found.') { if (!this.headerWritten) { - res.writeHead(code); + res.status(code).send(message); this.headerWritten = true; } this.end(); @@ -66,33 +66,34 @@ async function processNextJob() { const filenameBase = `${job.entryId}_${job.queryDefinition.name.replace(/\s/g, '_')}` const writer = wrapResponse(job.responseFormat.isBinary ? `${filenameBase}.bcif` : `${filenameBase}.cif`, response); + try { + const encoder = await resolveJob(job); writer.writeHeader(job.responseFormat.isBinary); - await resolveJob(job, writer); + encoder.writeTo(writer); } catch (e) { ConsoleLogger.errorId(job.id, '' + e); - // TODO: add some error? - writer.doError(404); + writer.doError(404, '' + e); } finally { writer.end(); + ConsoleLogger.logId(job.id, 'Query', 'Finished.'); setImmediate(processNextJob); } } function mapQuery(app: express.Express, queryName: string, queryDefinition: QueryDefinition) { - app.get(makePath(':entryId/' + queryName), async (req, res) => { + app.get(makePath(':entryId/' + queryName), (req, res) => { ConsoleLogger.log('Server', `Query '${req.params.entryId}/${queryName}'...`); if (JobManager.size >= Config.maxQueueLength) { - // TODO use proper code: server busy - res.writeHead(404); + res.status(503).send('Too many queries, please try again later.'); res.end(); return; } const jobId = JobManager.add('pdb', req.params.entryId, queryName, req.query); responseMap.set(jobId, res); - processNextJob(); + if (JobManager.size === 1) processNextJob(); }); } diff --git a/src/servers/model/test.ts b/src/servers/model/test.ts index 330e31780b20c15c4bbf818fd8cb5b24638009c4..882c18b6afc6095f813f4f12c2e5e48bd07b0d3a 100644 --- a/src/servers/model/test.ts +++ b/src/servers/model/test.ts @@ -36,8 +36,9 @@ function wrapFile(fn: string) { async function run() { try { const request = createJob('_local_', 'e:/test/quick/1cbs_updated.cif', 'residueInteraction', { label_comp_id: 'REA' }); + const encoder = await resolveJob(request); const writer = wrapFile('e:/test/mol-star/1cbs_full.cif'); - await resolveJob(request, writer); + encoder.writeTo(writer); writer.end(); } finally { StructureCache.expireAll();