Commit 2fafe98
committed
[BACKPORT 2025.1][yugabyte#27559] DocDB: Table locks: Disable reuse of docdb txn incase of abort when object locking feature is enabled
Summary:
Original commit: 7d02f94 / D44857
Timed out requests at the Object lock Manager are resumed organically by the poller thread once the deadline is exceeded. So this could mean that a ysql statement could time out, execute a finish transaction rpc and could initiate a new ysql transaction before the poller resumes the obsolete timed out lock waiter.
Given that we try re-using docdb transactions wherever possible when object locking is enabled, the above in combination with transaction re-use could lead to undesired issues. Here's how the OLM works in brief:
1. While serving new incoming lock requests, shared `TrackedTransactionLockEntry` is created if one doesn't exists, keyed against txn id, stored in `txn_locks_`
2. `PrepareAcquire` is executed which validates whether the lock request should be tried.
3. moves on to acquire the lock if available, else enters the wait queue.
4. When waiters are resumed, goto step 2.
5. When serving a release all request, remove entry from `txn_locks_`, release acquired locks, and let obsolete waiting locks be resumed by the poller thread.
Here's the brief working of the object lock tracker code.
1. For incoming lock requests, instrument the lock in the corresponding map key against `<txn, subtxn>` with state set to `WAITING` and invoke the OLM.
2. When OLM executes the lock callback, tap into it, try finding the map with key `<txn, subtxn>`, and if exists, change the state of the lock entry in the map accordingly.
Consider the following scenario:
1. ysql starts read only `ysql_txnA`, issues a lock request. this is associated with `docdb_txnA`, and the lock request is forwarded to the OLM.
2. lock request enters the wait-queue
3. YSQL detects timeout, cancels the request, issues a finish txn call. `docdb_txnA` doesn't get consumed since it didn't write any intents nor the txn itself failed (failed heartbeats). OLM erases `TrackedTransactionLockEntry` keyed against `docdb_txnA`.
4. YSQL start a new `ysql_txnB`, issues a lock request. docdb re-uses `docdb_txnA` issues the lock request, OLM creates a new entry for the same transaction id, and moves on to acquire the lock.
5. The poller might now realize that the earlier waiting request timed out, and try resuming it.
This results in issues with observability, since at some point the OLM has state corresponding to different ysql transactions stored under the same docdb transaction id. As a consequence, it results in a segv with the lock tracking code in the above scenario as follows:
- Step 1 creates a map for key `<docdb_txnA, 1>`, and inserts `key(lock)`.
- Step 3 erases the map for key `<docdb_txnA, 1>`.
- Step 4 creates a new map for key `<docdb_txnA, 1>`, and inserts `key(lock_new)`.
- Step 5 tries to new access entry `key(lock)` in the new map which doesn't exists, resulting in a segv
```
* thread #1, name = 'yb-tserver', stop reason = signal SIGSEGV
* frame #0: 0x0000aaaaead1b198 yb-tserver`std::__1::__function::__func<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status), std::__1::allocator<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)>, void (yb::Status const&)>::operator()(yb::Status const&) [inlined] yb::tserver::ObjectLockTracker::UntrackLock(this=<unavailable>, lock_context=0x000010f1bbe20800) at ts_local_lock_manager.cc:134:25
frame #1: 0x0000aaaaead1b0c8 yb-tserver`std::__1::__function::__func<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status), std::__1::allocator<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)>, void (yb::Status const&)>::operator()(yb::Status const&) at ts_local_lock_manager.cc:104:7
frame #2: 0x0000aaaaead1b0b4 yb-tserver`std::__1::__function::__func<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status), std::__1::allocator<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)>, void (yb::Status const&)>::operator()(yb::Status const&) [inlined] yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(this=0x000010f1b5ad4cd0, status=Status @ 0x0000ffef78896640)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)::operator()(yb::Status) const at ts_local_lock_manager.cc:324:46
frame #3: 0x0000aaaaead1b034 yb-tserver`std::__1::__function::__func<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status), std::__1::allocator<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)>, void (yb::Status const&)>::operator()(yb::Status const&) [inlined] decltype(std::declval<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)&>()(std::declval<yb::Status const&>())) std::__1::__invoke[abi:ne190100]<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)&, yb::Status const&>(__f=<unavailable>, __args=<unavailable>) at invoke.h:149:25
frame #4: 0x0000aaaaead1b01c yb-tserver`std::__1::__function::__func<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status), std::__1::allocator<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)>, void (yb::Status const&)>::operator()(yb::Status const&) [inlined] void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ne190100]<yb::tserver::TSLocalLockManager::Impl::PrepareAndExecuteAcquire(yb::tserver::AcquireObjectLockRequestPB const&, std::__1::chrono::time_point<yb::CoarseMonoClock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>, std::__1::function<void (yb::Status const&)>&, yb::StronglyTypedBool<yb::tserver::WaitForBootstrap_Tag>)::'lambda'(yb::Status)&, yb::Status const&>(__args=<unavailable>, __args=<unavailable>) at invoke.h:224:5
```
This revision address the issue by burning/consuming the docdb transaction in case of YSQL transaction abort. This would force the `ysql_txnB` above to be associated with a new `docdb_txnB` and wouldn't lead to any observability issues/mixed state.
Note that there isn't any problem with reusing docdb transactions across ysql read-only transactions that move on to commit successfully. This is because if the ysql transaction moves on to commit, it implies that all the object locks were granted => no waiting object locks, and hence all corresponding entries of the respective docdb transaction would be released at the OLM in-line with the commit.
Jira: DB-17123
Test Plan:
Jenkins
./yb_build.sh --cxx-test pg_object_locks-test --gtest_filter PgObjectLocksTestRF1.TestDisableReuseAbortedPlainTxn
./yb_build.sh release --java-test 'org.yb.pgsql.TestPgRegressPgAuth#schedule'
Reviewers: #db-approvers, amitanand, yyan, rthallam
Reviewed By: amitanand
Subscribers: yql, ybase, svc_phabricator
Differential Revision: https://phorge.dev.yugabyte.com/D450041 parent c9b1fc7 commit 2fafe98
2 files changed
Lines changed: 43 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1062 | 1062 | | |
1063 | 1063 | | |
1064 | 1064 | | |
| 1065 | + | |
| 1066 | + | |
| 1067 | + | |
| 1068 | + | |
1065 | 1069 | | |
1066 | 1070 | | |
1067 | 1071 | | |
| |||
1796 | 1800 | | |
1797 | 1801 | | |
1798 | 1802 | | |
| 1803 | + | |
1799 | 1804 | | |
1800 | 1805 | | |
1801 | 1806 | | |
1802 | 1807 | | |
1803 | 1808 | | |
1804 | 1809 | | |
1805 | 1810 | | |
1806 | | - | |
| 1811 | + | |
1807 | 1812 | | |
1808 | 1813 | | |
1809 | | - | |
| 1814 | + | |
| 1815 | + | |
| 1816 | + | |
| 1817 | + | |
| 1818 | + | |
| 1819 | + | |
| 1820 | + | |
| 1821 | + | |
| 1822 | + | |
| 1823 | + | |
1810 | 1824 | | |
1811 | 1825 | | |
1812 | 1826 | | |
| |||
3283 | 3297 | | |
3284 | 3298 | | |
3285 | 3299 | | |
3286 | | - | |
| 3300 | + | |
3287 | 3301 | | |
3288 | 3302 | | |
3289 | 3303 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
836 | 836 | | |
837 | 837 | | |
838 | 838 | | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
839 | 865 | | |
0 commit comments