2019-07-03 08:41:01 -04:00
|
|
|
// TODO: This file was created by bulk-decaffeinate.
|
|
|
|
// Sanity-check the conversion and remove this comment.
|
|
|
|
/*
|
|
|
|
* decaffeinate suggestions:
|
|
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
|
|
* DS206: Consider reworking classes to avoid initClass
|
|
|
|
* DS207: Consider shorter variations of null checks
|
|
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
|
|
*/
|
|
|
|
const ASpellWorker = require('./ASpellWorker')
|
|
|
|
const _ = require('underscore')
|
2021-12-14 08:00:35 -05:00
|
|
|
const logger = require('@overleaf/logger')
|
2020-11-25 06:57:24 -05:00
|
|
|
const metrics = require('@overleaf/metrics')
|
2020-06-03 05:14:44 -04:00
|
|
|
const OError = require('@overleaf/o-error')
|
2019-07-03 08:41:01 -04:00
|
|
|
|
|
|
|
class ASpellWorkerPool {
|
|
|
|
static initClass() {
|
|
|
|
this.prototype.MAX_REQUESTS = 100 * 1024
|
|
|
|
this.prototype.MAX_WORKERS = 32
|
|
|
|
this.prototype.MAX_IDLE_TIME = 1000
|
|
|
|
this.prototype.MAX_REQUEST_TIME = 60 * 1000
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(options) {
|
|
|
|
this.options = options
|
|
|
|
this.PROCESS_POOL = []
|
|
|
|
}
|
|
|
|
|
|
|
|
create(language) {
|
|
|
|
if (this.PROCESS_POOL.length >= this.MAX_WORKERS) {
|
2022-05-16 08:38:18 -04:00
|
|
|
logger.debug(
|
2019-07-03 08:41:01 -04:00
|
|
|
{ maxworkers: this.MAX_WORKERS },
|
|
|
|
'maximum number of workers already running'
|
|
|
|
)
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
const worker = new ASpellWorker(language, this.options)
|
|
|
|
worker.pipe.on('exit', () => {
|
|
|
|
if (worker.killTimer != null) {
|
|
|
|
clearTimeout(worker.killTimer)
|
|
|
|
worker.killTimer = null
|
|
|
|
}
|
|
|
|
if (worker.idleTimer != null) {
|
|
|
|
clearTimeout(worker.idleTimer)
|
|
|
|
worker.idleTimer = null
|
|
|
|
}
|
2022-05-16 08:38:18 -04:00
|
|
|
logger.debug(
|
2019-07-03 08:41:01 -04:00
|
|
|
{ process: worker.pipe.pid, lang: language },
|
|
|
|
'removing aspell worker from pool'
|
|
|
|
)
|
|
|
|
return this.cleanup()
|
|
|
|
})
|
|
|
|
this.PROCESS_POOL.push(worker)
|
|
|
|
metrics.gauge('aspellWorkerPool-size', this.PROCESS_POOL.length)
|
|
|
|
return worker
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup() {
|
2021-07-13 07:04:47 -04:00
|
|
|
const active = this.PROCESS_POOL.filter(worker => worker.state !== 'killed')
|
2019-07-03 08:41:01 -04:00
|
|
|
this.PROCESS_POOL = active
|
|
|
|
return metrics.gauge('aspellWorkerPool-size', this.PROCESS_POOL.length)
|
|
|
|
}
|
|
|
|
|
|
|
|
check(language, words, timeout, callback) {
|
|
|
|
// look for an existing process in the pool
|
|
|
|
let worker
|
|
|
|
const availableWorker = _.find(
|
|
|
|
this.PROCESS_POOL,
|
2021-07-13 07:04:47 -04:00
|
|
|
cached => cached.language === language && cached.isReady()
|
2019-07-03 08:41:01 -04:00
|
|
|
)
|
|
|
|
if (availableWorker == null) {
|
|
|
|
worker = this.create(language)
|
|
|
|
} else {
|
|
|
|
worker = availableWorker
|
|
|
|
}
|
|
|
|
|
|
|
|
if (worker == null) {
|
|
|
|
// return error if too many workers
|
2020-06-03 05:14:44 -04:00
|
|
|
callback(new OError('no worker available'))
|
2019-07-03 08:41:01 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (worker.idleTimer != null) {
|
|
|
|
clearTimeout(worker.idleTimer)
|
|
|
|
worker.idleTimer = null
|
|
|
|
}
|
|
|
|
|
|
|
|
worker.killTimer = setTimeout(
|
|
|
|
() => worker.kill('spell check timed out'),
|
|
|
|
timeout || this.MAX_REQUEST_TIME
|
|
|
|
)
|
|
|
|
|
|
|
|
return worker.check(words, (err, output) => {
|
|
|
|
if (worker.killTimer != null) {
|
|
|
|
clearTimeout(worker.killTimer)
|
|
|
|
worker.killTimer = null
|
|
|
|
}
|
|
|
|
callback(err, output)
|
|
|
|
if (err != null) {
|
|
|
|
return
|
|
|
|
} // process has shut down
|
|
|
|
if (worker.count > this.MAX_REQUESTS) {
|
|
|
|
return worker.shutdown(`reached limit of ${this.MAX_REQUESTS} requests`)
|
|
|
|
} else {
|
|
|
|
// queue a shutdown if worker is idle
|
2020-08-10 12:23:15 -04:00
|
|
|
return (worker.idleTimer = setTimeout(function () {
|
2019-07-03 08:41:01 -04:00
|
|
|
worker.shutdown('idle worker')
|
|
|
|
return (worker.idleTimer = null)
|
|
|
|
}, this.MAX_IDLE_TIME))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ASpellWorkerPool.initClass()
|
|
|
|
|
|
|
|
module.exports = ASpellWorkerPool
|