How to work around the lack of transactions in MongoDB?

后端 未结 10 641
梦毁少年i
梦毁少年i 2020-11-27 09:19

I know there are similar questions here but they are either telling me to switch back to regular RDBMS systems if I need transactions or use atomic operations or two-phase c

10条回答
  •  栀梦
    栀梦 (楼主)
    2020-11-27 09:49

    This is late but think this will help in future. I use Redis for make a queue to solve this problem.

    • Requirement:
      Image below show 2 actions need execute concurrently but phase 2 and phase 3 of action 1 need finish before start phase 2 of action 2 or opposite (A phase can be a request REST api, a database request or execute javascript code...).

    • How a queue help you
      Queue make sure that every block code between lock() and release() in many function will not run as the same time, make them isolate.

      function action1() {
        phase1();
        queue.lock("action_domain");
        phase2();
        phase3();
        queue.release("action_domain");
      }
      
      function action2() {
        phase1();
        queue.lock("action_domain");
        phase2();
        queue.release("action_domain");
      }
      
    • How to build a queue
      I will only focus on how avoid race conditon part when building a queue on backend site. If you don't know the basic idea of queue, come here.
      The code below only show the concept, you need implement in correct way.

      function lock() {
        if(isRunning()) {
          addIsolateCodeToQueue(); //use callback, delegate, function pointer... depend on your language
        } else {
          setStateToRunning();
          pickOneAndExecute();
        }
      }
      
      function release() {
        setStateToRelease();
        pickOneAndExecute();
      }
      

    But you need isRunning() setStateToRelease() setStateToRunning() isolate it's self or else you face race condition again. To do this I choose Redis for ACID purpose and scalable.
    Redis document talk about it's transaction:

    All the commands in a transaction are serialized and executed sequentially. It can never happen that a request issued by another client is served in the middle of the execution of a Redis transaction. This guarantees that the commands are executed as a single isolated operation.

    P/s:
    I use Redis because my service already use it, you can use any other way support isolation to do that.
    The action_domain in my code is above for when you need only action 1 call by user A block action 2 of user A, don't block other user. The idea is put a unique key for lock of each user.

提交回复
热议问题