UNPKG

10.8 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const lifecycle_1 = require("@stoplight/lifecycle");
5const immer_1 = require("immer");
6const lodash_1 = require("lodash");
7const mobx_1 = require("mobx");
8const neo_async_1 = require("neo-async");
9const errorReporter_1 = require("../errorReporter");
10const scheduler_1 = require("../scheduler");
11const types_1 = require("../types");
12function createScheduler(props) {
13 return new Scheduler(props);
14}
15exports.createScheduler = createScheduler;
16class Scheduler {
17 constructor(props) {
18 this._queued = {};
19 this._handlers = handlerMap();
20 this._running = {};
21 this.mightBeStuck = false;
22 this.queue = (task) => {
23 if (!this._handlers[task.op].length)
24 return Promise.resolve();
25 const taskId = computeTaskId(task);
26 let queued;
27 if (taskId) {
28 queued = this._queued[taskId];
29 }
30 if (!queued) {
31 queued = new Promise((resolve, reject) => {
32 this._queue.push(task, priorityMap[task.op] || 99, err => {
33 if (taskId) {
34 delete this._queued[taskId];
35 }
36 if (err) {
37 errorReporter_1.errorReporter.reportError({
38 code: types_1.GraphiteErrorCode.Generic,
39 message: `Error running task ${task.op} ${err}`,
40 trace: task.trace,
41 nodeId: task.nodeId,
42 });
43 reject(err);
44 }
45 resolve();
46 });
47 });
48 if (taskId) {
49 this._queued[taskId] = queued;
50 }
51 }
52 return queued;
53 };
54 this.queueAll = (tasks) => {
55 return tasks.map(task => this.queue(task));
56 };
57 this.registerHandler = (op, handler) => {
58 this._handlers[op].push(handler);
59 return lifecycle_1.createDisposable(() => {
60 const idx = this._handlers[op].indexOf(handler);
61 if (idx >= 0) {
62 this._handlers[op].splice(idx, 1);
63 }
64 });
65 };
66 this.drain = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
67 yield this.drainQueue();
68 yield mobx_1.when(() => !this.isBusy);
69 });
70 this._run = (task) => {
71 const node = this._graph.getNodeById(task.nodeId);
72 if (!node) {
73 throw new Error(`Cannot find node with id ${task.nodeId} for task ${scheduler_1.GraphTaskOp[task.op]}.`);
74 }
75 const handler = findHandler(this._handlers[task.op], node);
76 if (!handler)
77 return;
78 return handler.run(node, {
79 task,
80 getNodeById: this._graph.getNodeById,
81 getNodeByUri: this._graph.getNodeByUri,
82 getNodesByType: this._graph.getNodesByType,
83 moveNode: this._graph.moveNode,
84 applyPatch: patch => {
85 return this._graph.applyPatch({
86 operations: patch.operations,
87 trace: Object.assign({}, patch.trace, { sourceOp: task.op }),
88 });
89 },
90 removeNode: (id, trace = {}) => {
91 return this._graph.removeNode(id, Object.assign({}, trace, { sourceOp: task.op }));
92 },
93 addNode: (props, trace = {}) => {
94 return this._graph.addNode(props, Object.assign({}, trace, { sourceOp: task.op }));
95 },
96 setSourceNodeProp: (id, prop, value, trace = {}) => {
97 return this._graph.setSourceNodeProp(id, prop, value, Object.assign({}, trace, { sourceOp: task.op }));
98 },
99 patchSourceNodeProp: (id, prop, parsed, trace = {}) => {
100 return this._graph.patchSourceNodeProp(id, prop, parsed, Object.assign({}, trace, { sourceOp: task.op }));
101 },
102 reportError: (nodeId, error, trace = {}) => {
103 return this._graph.reportError(nodeId, error, immer_1.default(trace, source => {
104 source.sourceOp = task.op;
105 }));
106 },
107 setSourceNodeDiagnostics: (id, diagnostics, trace = {}) => {
108 return this._graph.setSourceNodeDiagnostics(id, handler.id, diagnostics, Object.assign({}, trace, { sourceOp: task.op }));
109 },
110 runTask: (t) => {
111 return this.run(t);
112 },
113 resolver: this._resolver,
114 });
115 };
116 this.drainQueue = () => {
117 return new Promise((resolve, reject) => {
118 if (this.queueLength()) {
119 this._queue.drain = resolve;
120 this._queue.error = reject;
121 }
122 else {
123 resolve();
124 }
125 });
126 };
127 this._runQueueTask = (task, cb) => {
128 const taskId = computeTaskId(task);
129 this._running[taskId] = true;
130 try {
131 const result = this._run(task);
132 if (result instanceof Promise) {
133 result
134 .then(r => {
135 this._handleRunResult(r);
136 delete this._running[taskId];
137 cb();
138 })
139 .catch(cb)
140 .then(() => delete this._running[taskId]);
141 }
142 else {
143 this._handleRunResult(result);
144 delete this._running[taskId];
145 setTimeout(cb, 0);
146 }
147 }
148 catch (e) {
149 delete this._running[taskId];
150 cb(e);
151 }
152 };
153 this._handleRunResult = (result) => {
154 if (result) {
155 this.queueAll(result);
156 }
157 };
158 this._graph = props.graph;
159 this._queue = neo_async_1.priorityQueue(this._runQueueTask, 1);
160 this._resolver = props.resolver;
161 mobx_1.reaction(() => this.queuedCount, mobx_1.action(() => {
162 this.mightBeStuck = false;
163 if (this._mightBeStuckTimeout)
164 clearTimeout(this._mightBeStuckTimeout);
165 this._mightBeStuckTimeout = setTimeout(mobx_1.action(() => {
166 this.mightBeStuck = this.queuedCount !== 0;
167 this._mightBeStuckTimeout = void 0;
168 }), 5000);
169 }));
170 }
171 get isBusy() {
172 return this.queuedCount !== 0;
173 }
174 get queuedCount() {
175 return lodash_1.size(this._queued);
176 }
177 snapshot() {
178 const tasks = [];
179 let task = lodash_1.get(this._queue, '_tasks.head');
180 while (task !== null && typeof task === 'object') {
181 tasks.push(task.data);
182 task = task.next;
183 }
184 const runningKeys = Object.keys(this._running);
185 const running = [];
186 for (const key of runningKeys) {
187 const [op, id] = key.split(':');
188 let node;
189 try {
190 node = this._graph.getNodeById(id);
191 }
192 catch (e) {
193 }
194 running.push({
195 id,
196 op: Number(op),
197 type: node && node.type,
198 spec: node && node.spec,
199 path: node && node.path,
200 language: node && node.language,
201 });
202 }
203 return {
204 running,
205 queued: Object.keys(this._queued),
206 tasks,
207 };
208 }
209 queueLength() {
210 return this._queue.length();
211 }
212 run(task) {
213 const taskId = computeTaskId(task);
214 const queued = this._queued[taskId];
215 if (queued && this._running[taskId]) {
216 return queued;
217 }
218 this._queue.remove((t) => taskId === computeTaskId(t));
219 const taskPromise = new Promise((resolve, reject) => this._runQueueTask(task, err => {
220 if (taskId) {
221 delete this._queued[taskId];
222 }
223 if (err) {
224 console.error(`Error running task ${task.op}`, err);
225 reject(err);
226 }
227 resolve();
228 }));
229 this._queued[taskId] = taskPromise;
230 return taskPromise;
231 }
232}
233tslib_1.__decorate([
234 mobx_1.observable,
235 tslib_1.__metadata("design:type", Object)
236], Scheduler.prototype, "_queued", void 0);
237tslib_1.__decorate([
238 mobx_1.observable,
239 tslib_1.__metadata("design:type", Object)
240], Scheduler.prototype, "mightBeStuck", void 0);
241tslib_1.__decorate([
242 mobx_1.computed,
243 tslib_1.__metadata("design:type", Object),
244 tslib_1.__metadata("design:paramtypes", [])
245], Scheduler.prototype, "isBusy", null);
246tslib_1.__decorate([
247 mobx_1.computed,
248 tslib_1.__metadata("design:type", Object),
249 tslib_1.__metadata("design:paramtypes", [])
250], Scheduler.prototype, "queuedCount", null);
251function computeTaskId(task) {
252 return `${task.op}:${task.nodeId}`;
253}
254const priorityMap = {
255 [scheduler_1.GraphTaskOp.SerializeSourceNode]: 1,
256 [scheduler_1.GraphTaskOp.DeserializeSourceNode]: 1,
257 [scheduler_1.GraphTaskOp.DiffRawToParsed]: 1,
258 [scheduler_1.GraphTaskOp.ComputeSourceMap]: 2,
259 [scheduler_1.GraphTaskOp.ReadSourceNode]: 3,
260 [scheduler_1.GraphTaskOp.WriteSourceNode]: 3,
261 [scheduler_1.GraphTaskOp.DeleteSourceNode]: 3,
262 [scheduler_1.GraphTaskOp.MoveSourceNode]: 3,
263 [scheduler_1.GraphTaskOp.ResolveSourceNode]: 4,
264 [scheduler_1.GraphTaskOp.TransformParsed]: 5,
265 [scheduler_1.GraphTaskOp.ValidateSourceNode]: 6,
266};
267const handlerMap = () => ({
268 [scheduler_1.GraphTaskOp.SerializeSourceNode]: [],
269 [scheduler_1.GraphTaskOp.DeserializeSourceNode]: [],
270 [scheduler_1.GraphTaskOp.DiffRawToParsed]: [],
271 [scheduler_1.GraphTaskOp.ComputeSourceMap]: [],
272 [scheduler_1.GraphTaskOp.ReadSourceNode]: [],
273 [scheduler_1.GraphTaskOp.WriteSourceNode]: [],
274 [scheduler_1.GraphTaskOp.DeleteSourceNode]: [],
275 [scheduler_1.GraphTaskOp.TransformParsed]: [],
276 [scheduler_1.GraphTaskOp.MoveSourceNode]: [],
277 [scheduler_1.GraphTaskOp.ResolveSourceNode]: [],
278 [scheduler_1.GraphTaskOp.ValidateSourceNode]: [],
279});
280const findHandler = (handlers, node) => {
281 return handlers.find(h => h.selector(node));
282};
283//# sourceMappingURL=scheduler.js.map
\No newline at end of file