教程[16] MeiliSearch 的队列处理

开篇废话

这几天我 @zaobao_bot 又抽风了,先是官方接口挂了,然后下午官方 API 修好后频道推送就炸了,表现为重复推送,而我代码在更新 MeiliSearch 之前跑了半年都是没炸锅的,所以锅应该还是在 MeiliSearch 上。

居然没想到这系列能出第三集,又被 MeiliSearch 坑了一把。

接前篇 「MeiliSearch 升级

正文

分析

还好前文分析了下 MeiliSearch 这几个月的更新日志,然后我注意到在 0.25.0 新增了个 task api,大概有以下几个处理都会在队列里面被安排,等待被执行。

  • indexCreation
  • indexUpdate
  • indexDeletion
  • documentsAddition
  • documentsPartial
  • documentsDeletion
  • settingsUpdate
  • clearAll

原来添加/修改也是要走队列的。。。

这个特性会导致数据的实时性都保证不了,也就是基本没法当数据库来用了,或者只能用来对付对于实时性要求不怎么高的项目(但是用这玩意不就是图一个搜索速度快么,感觉这样的设计本末倒置了)。

然后我们有两个思路(其实是一样的思路 都是等待队列完成)来解决这个实时性问题:

  1. 插入数据的请求完后请求 waitForTasks API (JavaScript 版 SDK 有)然后再返回插入成功的消息
  2. 开始新一轮拉取(插入数据)的时候 获取所有 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
}

然后就是在对应函数里面引用了,在新一轮轮训之前添加此代码即可。

image

总结

感觉被这玩意坑惨了,加个数据都要进队列处理的设计我觉得是很不合理的,即使我的 rankingrule(s) 只有一个 desc(document_id) 也得队列。
不过既然用都用了,也懒得再迁移到正常的数据库去,再炸的话也许就是存放到奇妙的地方了(填坑预定)

// 也许需要弄极端情况下的单元测试才能发现这样的 bug 了,这样的 bug 在写的时候是基本没法测试出来的。

因为懒所以懒得上单元测试而且还喜欢乱升依赖然后又被坑了