在无状态操作上等待 Lock()

await for Lock() on stateless action

问题:

前端页面发出x个并行请求(姑且称之为第一组), 下一组(x 请求)将在 5 秒后,第一个请求(第一组)从数据库设置缓存。 其他 x-1 请求得到空数组而不是等待第一个请求完成他的工作。 第二组和所有下一个请求都从缓存中获得了正确的数据。

在无状态机制中锁定其他线程直到第一个完成(或失败)之前的最佳实践是什么?

编辑:

缓存模块允许使用 set chache 的触发器,但由于它是无状态机制,所以它不起作用。

const GetDataFromDB= async (req, res, next) => {
  var cachedTableName = undefined;
  //  "lockFlag" uses to prevent parallel request to get into  critical section (because its  take time to set cache from db)
  //  to prevent that we uses "lockFlag" that is short-initiation to cache.
  //
  if ( !myCache.has( "lockFlag" ) && !myCache.has( "dbtable" ) ){
      // here  arrive first req from first group only
      // the other x-1 of first group went to the nest condition
      // here i would build mechanism to wait 'till first req come back from DB (init cache)
      myCache.set( "lockFlag", "1" )  

      const connection1 = await odbc.connect(connectionConfig); 

      const cachedTableName = await connection1.query(`select * from ${tableName}`);
      
      if(cachedTableName.length){
          const success = myCache.set([
              {key: "dbtable", val: cachedTableName, ttl: 180},
          ])
          if(success)
          {
              cachedTableName = myCache.get( "dbtable" );
          }
      }
      myCache.take("lockFlag");
      connection1.close();
      return res.status(200).json(cachedTableName ); // uses for first response.
  }
  // here comes x-1 of first group went to the nest condition and got nothing, bacause the cache not set yet
  // 
  if ( myCache.has( "dbtable" ) ){
    cachedTableName = myCache.get( "dbtable" );
  }
  return res.status(200).json(cachedTableName );
}

作为解决方法,我添加了“最终”范围以在第一次启动后从缓存中删除锁键,并且:

while(myCache.has( "lockFlag" )){
        await wait(1500);
}

以及“等待”函数:

function wait(milleseconds) {
    return new Promise(resolve => setTimeout(resolve, milleseconds))
}

(source)

这是有效的,但仍然可能有时间(<1500 毫秒)有缓存并且线程不知道。

我很乐意提供连击解决方案。

您可以尝试给出的方法 here,稍作修改以将其应用于您的案例。

  • 为简洁起见,我删除了注释并缩短了变量名称。

代码,然后解释:

const EventEmitter = require('events');
const bus = new EventEmitter();

const getDataFromDB = async (req, res, next) => {
  var table = undefined;
  if (myCache.has("lockFlag")) { 
     await new Promise(resolve => bus.once("unlocked", resolve));
  }
  if (myCache.has("dbtable")) {
     table = myCache.get("dbtable");
  }
  else {
      myCache.set("lockFlag", "1");  
      const connection = await odbc.connect(connectionConfig); 
      table = await connection.query(`select * from ${tableName}`);
      connection.close();
      if (table.length) {
          const success = myCache.set([
              { key: "dbtable", val: table, ttl: 180 },
          ]);
      }
      myCache.take("lockFlag");
      bus.emit("unlocked");
  }
  return res.status(200).json(table);
}

它应该是这样工作的:

  • 起初,lockFlag 不存在。
  • 然后,一些代码调用 getDataFromDB。该代码将第一个 if 块评估为 false,因此它继续:它将 lockFlag 设置为 true ("1"),然后继续从 db 检索 table 数据。与此同时:
  • 其他一些代码调用 getDataFromDB。但是,该代码将第一个 if 块评估为真,因此它等待承诺,直到发出 unlocked 事件。
  • 回到第一个调用代码:它完成它的逻辑,缓存 table 数据,将 lockFlag 设置回 false,发出一个 unlocked 事件,并且 returns.
  • 其他代码现在可以继续执行:它将第二个 if 评估为真,因此它从缓存中获取 table,并且 returns.