4import * as _ from 'lodash-es' 12type ZhongJob = { name: string; runFnc: (lastData: unknown) => Promise<unknown>; intervalSec?: number; runInPoll?: boolean } 14export const zhongLongRun = async ({jobsA, pollIntervalSec=10}: {jobsA: ZhongJob[], pollIntervalSec?: number}) => { 16 const ranJobNames = await zhongRunOnce({jobsA, withinLongRun: true}) 17 jobsA = _.reject(jobsA, ({runInPoll}) => runInPoll === false) 18 if (ranJobNames.length > 0) { 21 await sleep(pollIntervalSec * 1000) 26export const zhongInfo = ({jobsA}: {jobsA: (ZhongJob | undefined)[]}) => { 27 const zhongRunsDir = ptDiskDir + '/zhongruns/' 28 for (const jobH of _.compact(jobsA)) { 29 const {name, intervalSec} = jobH 30 const lastRunFile = zhongRunsDir + name 31 let lastRunStr = 'never' 33 if (fs.existsSync(lastRunFile)) { 34 const stats = fs.statSync(lastRunFile) 35 const elapsedSec = (Date.now() - stats.mtimeMs) / 1000 38 wouldRun = elapsedSec >= intervalSec 41 const intervalStr = intervalSec ? secToHumanAbs(intervalSec) : 'on-demand' 43 console.log(name.padEnd(40), lastRunStr.padEnd(14), coloredInterval) 47export const eptZhong = async ({jobsA}: {jobsA: (ZhongJob | undefined)[]}) => { 48 const cliSchema = { ignoreRecent: { flag: true, desc: 'run job regardless of interval throttling' }, retrySkipped: { flag: true, desc: 'retry skip-listed import sessions' } } as const 50 info: { desc: 'list jobs and last-run times without running any' }, 51 help: { desc: 'show this help' }, 53 const { ignoreRecent, retrySkipped, _action } = parseCli(cliSchema, cliActions) 54 if (_action === 'help') { 58 if (_action === 'info') { 62 let jobs = _.compact(jobsA) 64 jobs = _.filter(jobs, ({name}) => name == _action) 66 await zhongRunOnce({jobsA: jobs, ignoreRecent}) 69export const zhongRunOnce = async ({jobsA, withinLongRun=false, ignoreRecent=false}: {jobsA: ZhongJob[], withinLongRun?: boolean, ignoreRecent?: boolean}) => { 70 jobsA = _.compact(jobsA) // in case someone wants to comment out jobs w/ "false &&" at higher level 71 const ranJobNames = [] 72 let throttledAny = false 73 for (const ii in jobsA) { 74 const jobH = jobsA[ii] 75 const {name, runFnc, intervalSec} = jobH 77 const zhongRunsDir = ptDiskDir + '/zhongruns/' 78 const lastRunFile = zhongRunsDir + name 80 let lastRunData = null 81 if (fs.existsSync(lastRunFile)) { 84 const stats = fs.statSync(lastRunFile) 85 const elapsedSec = (Date.now() - stats.mtimeMs) / 1000 86 const isRecent = elapsedSec < intervalSec 90 if (isRecent && !ignoreRecent) { 98 const startedAt = _.now() 101 lastRunData = await runFnc(lastRunData) 104 // TODO retry when: Error: ROLLBACK - Client has encountered a connection error and is not queryable 108 await fs1Promises.mkdir(zhongRunsDir, {recursive: true}) 109 await fs1Promises.writeFile(lastRunFile, _.isUndefined(lastRunData) ? '' : JSON.stringify(lastRunData)) 112 ranJobNames.push(name) 113 const elapsedSec = (_.now() - startedAt) / 1000 116 if (throttledAny && !withinLongRun) { 117 console.log(chalkDim('use --ignoreRecent to force-run skipped jobs'))