开篇废话
这几天我 @zaobao_bot 又抽风了,先是官方接口挂了,然后下午官方 API 修好后频道推送就炸了,表现为重复推送,而我代码在更新 MeiliSearch 之前跑了半年都是没炸锅的,所以锅应该还是在 MeiliSearch 上。
居然没想到这系列能出第三集,又被 MeiliSearch 坑了一把。
接前篇 「MeiliSearch 升级」
正文
分析
还好前文分析了下 MeiliSearch 这几个月的更新日志,然后我注意到在 0.25.0 新增了个 task api,大概有以下几个处理都会在队列里面被安排,等待被执行。
- indexCreation
- indexUpdate
- indexDeletion
- documentsAddition
- documentsPartial
- documentsDeletion
- settingsUpdate
- clearAll
原来添加/修改也是要走队列的。。。
这个特性会导致数据的实时性都保证不了,也就是基本没法当数据库来用了,或者只能用来对付对于实时性要求不怎么高的项目(但是用这玩意不就是图一个搜索速度快么,感觉这样的设计本末倒置了)。
然后我们有两个思路(其实是一样的思路 都是等待队列完成)来解决这个实时性问题:
- 插入数据的请求完后请求 waitForTasks API (JavaScript 版 SDK 有)然后再返回插入成功的消息
- 开始新一轮拉取(插入数据)的时候 获取所有 task 然后判断是否都成功,否则继续等
我们的思路是,请求
/task
的 API 获取表的 新增/修改 是否都成功,全部成功(status: succeeded)就继续安排文章推送。
这里我选择了第二种方法,因为 waitFor 还是会出现下一轮又炸了的情况下,我们就让下一轮请求在所有队列没完成之前一直等待大概就 ok。
实践
这里我创建了个自己的等待函数来解决(20次即超时并且警告)
以 JavaScript API 为例,msc 为 MeiliSarch
new MeiliSearch()
以后的
async function wait_msc_task_done(retry_time = 0) {
let d = (await msc.getTasks()).results.filter(x => {
// 如果正处于添加 或者修改
return ['documentAddition', 'documentPartial'].includes(x.documentAddition) && x.status !== 'succeeded' // 过滤出来所有未成功的
})
if (d.length > 0) {
// 函数不能死锁,这里设计了 20 次就 GG,按照需求自己添加代码通知到自己或者错误处理
if (retry_time > 20) {
console.warn('msc task still not done')
return false
}
// 等待 3s 然后进行下一轮执行
await sleep(3000)
await wait_msc_task_done()
}
return true
}
然后就是在对应函数里面引用了,在新一轮轮训之前添加此代码即可。
总结
感觉被这玩意坑惨了,加个数据都要进队列处理的设计我觉得是很不合理的,即使我的 rankingrule(s) 只有一个 desc(document_id)
也得队列。
不过既然用都用了,也懒得再迁移到正常的数据库去,再炸的话也许就是存放到奇妙的地方了(填坑预定)
// 也许需要弄极端情况下的单元测试才能发现这样的 bug 了,这样的 bug 在写的时候是基本没法测试出来的。
因为懒所以懒得上单元测试而且还喜欢乱升依赖然后又被坑了
完