- 每个任务存放到一个文件:由于任务的数据理论上没有上限,生产中有达到3000个的例子,且lowdb以json文件的形式存储, json文件会全量加载到内存中,所以每个人物存放到一个文件,每次只会加载一个任务
- 存储位置:由于任务是用户&身份区分的,所以存放到用户对应的文件夹下
- user-userType:每个用户(身份)的存储文件夹
- 'manuscriptBatchExport':固定字符串,这里是‘底稿批量导出’
- queue: 任务列表
- tasks-ExportID: 每个任务的队列详情
- 'manuscriptBatchExport':固定字符串,这里是‘底稿批量导出’
有两个表
- 任务列表:所有的导出任务
- 任务详情:每个导出任务中,所有需导出文件的状态
任务列表的状态:
- 初始化中:统计所有需下载的文件
- 等待中:已初始化,但目前有其他任务执行中
- 进行中:正在导出中
- 已暂停:用户手动暂停,
- 已完成:所有任务已处理,可成功可失败。
- 已取消:用户手动取消,
任务详情:
- 等待中:
- 进行中:
- 已暂停:
- 已完成:成功 or 失败(包含失败原因)。
A:不关联
队列的状态控制该任务是否初始化,进行中,暂停中,已取消,已完成等状态; 文件的状态反向控制任务状态,只有一种场景,就是所有文件都是已完成状态
任务列表/任务详情的状态变更,来源于:下载回调,用户操作。 同时操作两个表可能会导致状态不一致的bug。
- 单向状态流转:通过taskManager的操作,变更
任务详情
的状态,然后通过taskChangeCallback更新任务列表
的状态。 - 任务列表特有的状态:
- 初始化中:任务详情正在统计中,不可操作
- 进行中:数据库中状态实际为
等待中
,对比taskManager的状态和缓存的id,显示为进行中 - 已取消:
基本流程:获取任务队列,取一个可执行任务,缓存当前的用户和任务,创建任务管理器并执行(每个任务管理器都会与当前用户,当前任务关联,避免切换用户导致的错误)。在完成的回调重复这个过程,直到所有任务完成,或因异常暂停。
需求:实现上,尽量简化,稳定第一
- 同一时间只有一个任务在运行
- 任务连续执行
场景:
- 登录后:基本流程
- 注销:基本流程,理论上任务都会401,任务都会暂停。现在在注销时清除主进程的user信息,中断这个不必要的重复流程
- 协议切换用户:基本流程,每次获取可执行的任务,都会重新获取当前用户信息,理论上不影响
细节说明: 3. 任务队列一旦失败,无论401,暂停,退出登录等,都置为暂停,不用多次重试
每一个任务完成或其他原因终止,都重新拉取队列
,避免:
- 401用户失效,此时user为空,所以拉取队列失败
- 用户变更,此时是完全不同的队列
注意:401需要清除当前用户的缓存信息,避免无效的执行
缓存正在运行的任务,数据包括:
- 当前任务
- 当前用户
- 创建的taskManager实例
- 维护队列:任务执行完成后,任务状态的流转,任务队列的变动
- 任务队列内的连续执行
- 队列
- 存储队列:任务队列变动时,队列的存储,存储的方式有多种选择,
- 多个任务队列连续执行:在onComplete中判断和处理
- 队列
- 下载队列:等待下载的队列
- 下载中:正在下载的队列
- 暂停队列:任何情况停止的任务,可以包括:暂停,取消,失败等
- 完成队列:已完成的任务
- 队列的状态
包含场景:队列执行完成,401,暂停或取消队列
- 内部任务循环执行,所有任务执行完成,任务终止
- 方案:taskManager内部循环函数判断
- 401
- 方案:taskManager内部循环函数判断,增加对这种场景的判断
- 用户操作,暂停,取消等
- taskFun中提供一个用户存储abort的引用
以上场景,都需要taskFun
内部返回特定表示终止的特定错误canceled_con
的标识,
方案2:taskFun
返回中添加一个是否继续执行下一个任务
的标识,next === false
时,停止队列,且taskChangeCallback
不执行
方案1:taskFun:返回一个abort函数,赋值给taskManager
内部的pause函数
参考选型
- indexDB
- levelDB
- sqlite3
indexDB/Dexie.js:基于indexDB的封装,浏览器端的数据库,目前业务场景主要是在主进程端实现任务队列
levelDB:nosql数据库,基于levelDB,node和browser都可用,这里主要用于主进程
sqlite3:关系型数据库,主要是安装环境坑多
Q: 创建任务 A:方案1:在创建任务时,获取所有文件信息,计算所有文件的路径。方案2:在创建任务时,仅记录项目,目录等主要信息,在导出时获取文件信息,计算路径。 方案2: 1.无法展示完整的导出列表,必须下载一部分展示一部分;2.下载中断后继续,定位困难 方案1:创建时计算,实现上逻辑更清晰,但在文件有修改的情况,文件的size会对不上,导致断点续传异常?是否考虑这种场景 方案3:使用方案1,但是下载时携带版本号,只下载创建任务时的版本
Q: 创建任务耗时随目录增加而增加,该过程放到主进程还是渲染进程?需要为上传下载队列生成专用的线程吗? A:上传下载主要是请求+I/O操作,都有异步版本,CPU占用并不繁重,没必要使用专用线程
按字典顺序排序的键值数据库,基于levelDB,用于Node.js的classical-level和browsers的browser-level的包装的入口
- Node.js:基于levelDB
- browser:基于indexDB 以下内容,只包含Node.js的部分
https://github.com/Level/level
npm install level
创建一个新的数据库 or 打开一个已存在的数据库
- location:本地路径(相对或绝对路径)
- options:
- keyEncoding (string or object, default 'utf8'): encoding to use for keys
- valueEncoding (string or object, default 'utf8'): encoding to use for values.
获取当前数据的状态,只读,包括:opening open closing closed
打开数据库,成功或失败后调用callback or 返回promise,失败时参数为一个错误信息(nodejs的error-first模式)
一般来说,不用主动调用open,因为构造函数会自动调用,但也有使用场景
- 在主动
close
关闭数据库后,再次打开 - 用于捕获打开失败的错误信息?
关闭数据库,成功或失败后调用callback or 返回promise,失败时参数为一个错误信息(nodejs的error-first模式)
推荐:在使用完后关闭数据库,因为数据库可能有关联的资源,如文件句柄和锁。
获取key对应的value