Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-66145

Identify and fix locations that write while holding read tickets

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 6.3.0-rc0
    • Affects Version/s: None
    • Component/s: None
    • Labels:
      None
    • Fully Compatible
    • ALL
    • Execution Team 2022-12-12, Execution Team 2022-11-28, Execution Team 2022-12-26
    • 169

      Due to our support of recursive locking, it is possible for an operation to hold a global IS, acquiring a read ticket, and then obtain an IX lock at a lower level without taking a write ticket.

      This test passes:

      // d_concurrency_test.cpp
      
      TEST_F(DConcurrencyTestFixture, WrongTicket) {
          auto clientOpctxPairs = makeKClientsWithLockers(1);
          auto opctx1 = clientOpctxPairs[0].second.get();
      
          Lock::GlobalLock read(opctx1, MODE_IS);
          ASSERT_TRUE(opctx1->lockState()->hasReadTicket());
          ASSERT_FALSE(opctx1->lockState()->hasWriteTicket());
          ASSERT_FALSE(opctx1->lockState()->isWriteLocked());
      
          Lock::GlobalLock write(opctx1, MODE_IX);
      
          ASSERT_TRUE(opctx1->lockState()->hasReadTicket());
          ASSERT_FALSE(opctx1->lockState()->hasWriteTicket());
          ASSERT_TRUE(opctx1->lockState()->isWriteLocked());
      }
      

      We can discover violations of this with the following assertion:

      Unable to find source-code formatter for language: diff. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp
      index a968d2eee6e..2d22ebf6790 100644
      --- a/src/mongo/db/concurrency/lock_state.cpp
      +++ b/src/mongo/db/concurrency/lock_state.cpp
      @@ -409,6 +409,11 @@ void LockerImpl::lockGlobal(OperationContext* opCtx, LockMode mode, Date_t deadl
                           _acquireTicket(opCtx, mode, deadline));
               }
               _modeForTicket = mode;
      +    } else if (!isModeCovered(mode, _modeForTicket)) {
      +        LOGV2_FATAL(99999,
      +                    "Ticket held does not cover requested mode for global lock",
      +                    "held"_attr = _modeForTicket,
      +                    "requested"_attr = mode);
           }
      

      This is mostly an accounting problem since we don't use tickets to block reads or writes. We only use tickets for throttling reads and writes, and most applications do not hit this limit.

            Assignee:
            louis.williams@mongodb.com Louis Williams
            Reporter:
            louis.williams@mongodb.com Louis Williams
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: