Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docdb: fix deadlock issue with RepartitionTable #10304

Closed
jaki opened this issue Oct 14, 2021 · 3 comments
Closed

docdb: fix deadlock issue with RepartitionTable #10304

jaki opened this issue Oct 14, 2021 · 3 comments
Assignees
Labels
area/docdb YugabyteDB core features area/ysql Yugabyte SQL (YSQL) kind/bug This issue is a bug

Comments

@jaki
Copy link
Contributor

jaki commented Oct 14, 2021

RepartitionTable may get into deadlock on table rwc lock if ProcessTabletReportBatch takes read lock before the write lock held in RepartitionTable is committed. This can probably be fixed by holding catalog manager mutex_ for longer (unfortunately).

The following logs are based from in-progress changes:

[m-1] W1014 16:06:45.908854 29576 rwc_lock.cc:153] Too long commit lock wait, num readers: 3, current thread stack:     @     0x7fe594d07ea3  yb::RWCLock::UpgradeToCommitLock() (src/yb/util/rwc_lock.cc:147)
[m-1]     @     0x7fe599e402b4  yb::CowObject<yb::master::PersistentTableInfo>::CommitMutation() (src/yb/util/cow_object.h:93)
[m-1]     @     0x7fe599e34470  yb::CowWriteLock<yb::master::PersistentTableInfo>::Commit() (src/yb/util/cow_object.h:242)
[m-1]     @     0x7fe59a090d8c  yb::master::enterprise::CatalogManager::RepartitionTable(scoped_refptr<yb::master::TableInfo>, yb::master::enterprise::CatalogManager::ExternalTableSnapshotData const*) (yb/master/catalog_manager_ent.cc:1293)
[m-1]     @     0x7fe59a08d509  yb::master::enterprise::CatalogManager::ImportTableEntry(std::map<string, std::pair<string, yb::YQLDatabase>, std::less<string >, std::allocator<std::pair<string const, std::pair<string, yb::YQLDatabase> > > > const&, std::map<string, yb::master::enterprise::CatalogManager::ExternalTableSnapshotData, std::less<string >, std::allocator<std::pair<string const, yb::master::enterprise::CatalogManager::ExternalTableSnapshotData> > > const&, yb::master::enterprise::CatalogManager::ExternalTableSnapshotData*) (yb/master/catalog_manager_ent.cc:1562)
[m-1]     @     0x7fe59a08b821  yb::master::enterprise::CatalogManager::ImportSnapshotCreateObject(yb::master::SnapshotInfoPB const&, yb::master::ImportSnapshotMetaResponsePB*, std::map<string, std::pair<string, yb::YQLDatabase>, std::less<string >, std::allocator<std::pair<string const, std::pair<string, yb::YQLDatabase> > > >*, std::map<string, yb::master::enterprise::CatalogManager::ExternalTableSnapshotData, std::less<string >, std::allocator<std::pair<string const, yb::master::enterprise::CatalogManager::ExternalTableSnapshotData> > >*, yb::master::enterprise::CreateObjects) (yb/master/catalog_manager_ent.cc:844)
[m-1]     @     0x7fe59a08efbd  yb::master::enterprise::CatalogManager::ImportSnapshotMeta(yb::master::ImportSnapshotMetaRequestPB const*, yb::master::ImportSnapshotMetaResponsePB*) (yb/master/catalog_manager_ent.cc:968)
[m-1]     @     0x7fe59a0e183d  operator() (src/yb/master/master_service_base-internal.h:121)
[m-1]     @     0x7fe59a0e1791  void yb::master::MasterServiceBase::HandleOnLeader<yb::master::ImportSnapshotMetaRequestPB, yb::master::ImportSnapshotMetaResponsePB, void yb::master::MasterServiceBase::HandleIn<yb::master::enterprise::CatalogManager, yb::master::ImportSnapshotMetaRequestPB, yb::master::ImportSnapshotMetaResponsePB>(yb::master::ImportSnapshotMetaRequestPB const*, yb::master::ImportSnapshotMetaResponsePB*, yb::rpc::RpcContext*, yb::Status (yb::master::enterprise::CatalogManager::*)(yb::master::ImportSnapshotMetaRequestPB const*, yb::master::ImportSnapshotMetaResponsePB*), char const*, int, char const*, yb::StronglyTypedBool<yb::master::HoldCatalogLock_Tag>)::'lambda'()>(yb::master::enterprise::CatalogManager const*, yb::master::ImportSnapshotMetaRequestPB*, yb::rpc::RpcContext*, yb::master::ImportSnapshotMetaResponsePB, char const*, int, char const*, yb::StronglyTypedBool<yb::master::HoldCatalogLock_Tag>) (src/yb/master/master_service_base-internal.h:74)
[m-1]     @     0x7fe59a0e05af  void yb::master::MasterServiceBase::HandleIn<yb::master::enterprise::CatalogManager, yb::master::ImportSnapshotMetaRequestPB, yb::master::ImportSnapshotMetaResponsePB>(yb::master::ImportSnapshotMetaRequestPB const*, yb::master::ImportSnapshotMetaResponsePB*, yb::rpc::RpcContext*, yb::Status (yb::master::enterprise::CatalogManager::*)(yb::master::ImportSnapshotMetaRequestPB const*, yb::master::ImportSnapshotMetaResponsePB*), char const*, int, char const*, yb::StronglyTypedBool<yb::master::HoldCatalogLock_Tag>) (src/yb/master/master_service_base-internal.h:120)
[m-1]     @     0x7fe59a0e003b  yb::master::MasterBackupServiceImpl::ImportSnapshotMeta(yb::master::ImportSnapshotMetaRequestPB const*, yb::master::ImportSnapshotMetaResponsePB*, yb::rpc::RpcContext) (yb/master/master_backup_service.cc:38)
[m-1]     @     0x7fe596ed9c9a  yb::master::MasterBackupServiceIf::Handle(shared_ptr<yb::rpc::InboundCall>) (src/yb/master/master_backup.service.cc:295)
[m-1]     @     0x7fe59585df93  yb::rpc::ServicePoolImpl::Handle(shared_ptr<yb::rpc::InboundCall>) (src/yb/rpc/service_pool.cc:260)
[m-1]     @     0x7fe5957d00c9  yb::rpc::InboundCall::InboundCallTask::Run() (src/yb/rpc/inbound_call.cc:218)
[m-1]     @     0x7fe59586f81b  yb::rpc::(anonymous namespace)::Worker::Execute() (src/yb/rpc/thread_pool.cc:105)
[m-1]     @     0x7fe59587010f  decltype(*(std::forward<yb::rpc::(anonymous namespace)::Worker*&>(fp0)).*fp()) std::__invoke<void (yb::rpc::(anonymous namespace)::Worker::*&)(), yb::rpc::(anonymous namespace)::Worker*&, void>(void (yb::rpc::(anonymous namespace)::Worker::*&)(), yb::rpc::(anonymous namespace)::Worker*&) (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/type_traits:3840)
[m-1]     @     0x7fe5958700db  std::__bind_return<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, tuple<>, __is_valid_bind_return<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, tuple<> >::value>::type std::__apply_functor<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, 0ul, tuple<> >(void (yb::rpc::(anonymous namespace)::Worker::*&)(), tuple<yb::rpc::(anonymous namespace)::Worker*>&, std::__tuple_indices<0ul>, tuple<>&&) (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/functional:2853)
[m-1]     @     0x7fe5958700bc  std::__bind_return<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, tuple<>, __is_valid_bind_return<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, tuple<> >::value>::type std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>::operator()<>() (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/functional:2886)
[m-1]     @     0x7fe5958700a8  decltype(std::forward<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&>(fp)()) std::__invoke<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&>(std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&) (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/type_traits:3899)
[m-1]     @     0x7fe595870088  void std::__invoke_void_return_wrapper<void>::__call<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&>(std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&) (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/__functional_base:348)
[m-1]     @     0x7fe595870070  std::__function::__alloc_func<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>, std::allocator<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&> >, void ()>::operator()() (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/functional:1557)
[m-1]     @     0x7fe59586fabc  std::__function::__func<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>, std::allocator<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&> >, void ()>::operator()() (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/functional:1731)
[m-1]     @     0x7fe59a04e192  std::__function::__value_func<void ()>::operator()() const (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/functional:1884)
[m-1]     @     0x7fe59a04ddc8  std::function<void ()>::operator()() const (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/functional:2556)
[m-1]     @     0x7fe594d21ada  yb::Thread::SuperviseThread(void*) (src/yb/util/thread.cc:771)
[m-1]     @     0x7fe592d5cea4
[m-1]     @     0x7fe592a849fc
[m-1] reader thread 29776 stack:     @     0x7fe594d078fa  yb::RWCLock::ReadLock() (src/yb/util/rwc_lock.cc:73)
[m-1]     @     0x7fe599e0e908  yb::CowObject<yb::master::PersistentTableInfo>::ReadLock() const (src/yb/util/cow_object.h:58)
[m-1]     @     0x7fe599e0e8ee  CowReadLock (src/yb/util/cow_object.h:159)
[m-1]     @     0x7fe599e04beb  yb::master::MetadataCowWrapper<yb::master::PersistentTableInfo>::LockForRead() const (yb/master/../../../../src/yb/master/catalog_entity_info.h:142)
[m-1]     @     0x7fe599e7ff78  yb::master::CatalogManager::ProcessTabletReportBatch(yb::master::TSDescriptor*, bool, std::__wrap_iter<yb::master::CatalogManager::ReportedTablet const*>, std::__wrap_iter<yb::master::CatalogManager::ReportedTablet const*, yb::master::TabletReportUpdatesPB*, std::vector<shared_ptr<yb::master::RetryingTSRpcTask>, std::allocator<shared_ptr<yb::master::RetryingTSRpcTask> > >*) (src/yb/master/catalog_manager.cc:5760)
[m-1]     @     0x7fe599e820e6  yb::master::CatalogManager::ProcessTabletReport(yb::master::TSDescriptor*, yb::master::TabletReportPB const&, yb::master::TabletReportUpdatesPB*, yb::rpc::RpcContext*) (src/yb/master/catalog_manager.cc:6019)
[m-1]     @     0x7fe599fa483d  yb::master::MasterServiceImpl::TSHeartbeat(yb::master::TSHeartbeatRequestPB const*, yb::master::TSHeartbeatResponsePB*, yb::rpc::RpcContext) (src/yb/master/master_service.cc:201)
[m-1]     @     0x7fe59604c151  yb::master::MasterServiceIf::Handle(shared_ptr<yb::rpc::InboundCall>) (src/yb/master/master.service.cc:1578)
[m-1]     @     0x7fe59585df93  yb::rpc::ServicePoolImpl::Handle(shared_ptr<yb::rpc::InboundCall>) (src/yb/rpc/service_pool.cc:260)
[m-1]     @     0x7fe5957d00c9  yb::rpc::InboundCall::InboundCallTask::Run() (src/yb/rpc/inbound_call.cc:218)
[m-1]     @     0x7fe59586f81b  yb::rpc::(anonymous namespace)::Worker::Execute() (src/yb/rpc/thread_pool.cc:105)
[m-1]     @     0x7fe59587010f  decltype(*(std::forward<yb::rpc::(anonymous namespace)::Worker*&>(fp0)).*fp()) std::__invoke<void (yb::rpc::(anonymous namespace)::Worker::*&)(), yb::rpc::(anonymous namespace)::Worker*&, void>(void (yb::rpc::(anonymous namespace)::Worker::*&)(), yb::rpc::(anonymous namespace)::Worker*&) (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/type_traits:3840)
[m-1]     @     0x7fe5958700db  std::__bind_return<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, tuple<>, __is_valid_bind_return<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, tuple<> >::value>::type std::__apply_functor<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, 0ul, tuple<> >(void (yb::rpc::(anonymous namespace)::Worker::*&)(), tuple<yb::rpc::(anonymous namespace)::Worker*>&, std::__tuple_indices<0ul>, tuple<>&&) (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/functional:2853)
[m-1]     @     0x7fe5958700bc  std::__bind_return<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, tuple<>, __is_valid_bind_return<void (yb::rpc::(anonymous namespace)::Worker::*)(), tuple<yb::rpc::(anonymous namespace)::Worker*>, tuple<> >::value>::type std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>::operator()<>() (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/functional:2886)
[m-1]     @     0x7fe5958700a8  decltype(std::forward<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&>(fp)()) std::__invoke<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&>(std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&) (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/type_traits:3899)
[m-1]     @     0x7fe595870088  void std::__invoke_void_return_wrapper<void>::__call<std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&>(std::__bind<void (yb::rpc::(anonymous namespace)::Worker::* const&)(), yb::rpc::(anonymous namespace)::Worker* const&>&) (/opt/yb-build/thirdparty/yugabyte-db-thirdparty-v20210916160331-44ba371965-centos7-x86_64-clang11/installed/uninstrumented/libcxx/include/c++/v1/__functional_base:348)
@jaki jaki added kind/bug This issue is a bug area/ysql Yugabyte SQL (YSQL) area/docdb YugabyteDB core features labels Oct 14, 2021
@jaki jaki self-assigned this Oct 14, 2021
@jaki
Copy link
Contributor Author

jaki commented Oct 15, 2021

Actually, holding mutex_ longer by itself will not fix it since neither thread holds mutex_ (I previously thought ProcessTabletReportBatch held it).

@jaki
Copy link
Contributor Author

jaki commented Oct 15, 2021

Here is an outline of the deadlock. t1 is RepartitionTable; t2 is ProcessTabletReportBatch.

t1: table->LockForWrite
t2: table->LockForRead
t1: tablet->LockForWrite
t1: table->Commit (blocks on t2 LockForRead)
t2: tablet->LockForWrite (blocks on t1 LockForWrite)

I'm not sure how to solve this. Grabbing mutex_ over both is expensive.

@jaki
Copy link
Contributor Author

jaki commented Oct 15, 2021

t2 could LockForWrite instead, but that's only for deadlock purposes (since there can only be one writer at a time), so it's hacky.

@bmatican bmatican added this to Backlog in YBase features via automation Oct 16, 2021
@bmatican bmatican added this to To do in Master components via automation Oct 16, 2021
@jaki jaki closed this as completed in cc90f01 Oct 21, 2021
YBase features automation moved this from Backlog to Done Oct 21, 2021
Master components automation moved this from To do to Done Oct 21, 2021
jaki added a commit that referenced this issue Nov 1, 2021
Summary:
Since commit b14485a ([#8229] backup:
repartition table if needed on YSQL restore), there is a rare deadlock
issue between ProcessTabletReportBatch and RepartitionTable.  It can be
hit when

- thread 1 (RepartitionTable): import a YSQL snapshot where the number
  of tablets for a table mismatch between the cluster and the external
  snapshot
- thread 2 (ProcessTabletReportBatch): process tablets of that table
  from heartbeat

A more in-depth sequence of steps follows:

1. t1: table->LockForWrite (WriteLock)
1. t2: table->LockForRead (ReadLock)
1. t1: tablet->StartMutation (WriteLock)
2. t1: table_lock.Commit (UpgradeToCommitLock; blocks on t2 ReadLock)
3. t2: tablet->LockForWrite (WriteLock; blocks on t1 WriteLock)

To fix, for ProcessTabletReportBatch, take table write lock instead of
read lock.  The table metadata isn't mutated, so this is purely for
deadlock avoidance reasons (since only one writer is allowed at a time).

Bogdan thinks we should expect table write lock to be taken whenever
tablet write lock is taken.

Original Commit: cc90f01

Original Differential Revision: https://phabricator.dev.yugabyte.com/D13459

Test Plan:
    ./yb_build.sh \
      --cxx-test tools_yb-backup-test_ent \
      --gtest_filter YBBackupTest.TestYSQLChangeDefaultNumTablets \
      -n 1000 \
      --tp 1 \
      fastdebug

Reviewers: nicolas

Reviewed By: nicolas

Subscribers: bogdan

Differential Revision: https://phabricator.dev.yugabyte.com/D13730
jasonyb pushed a commit that referenced this issue Nov 11, 2021
Summary:
Since commit b14485a ([#8229] backup:
repartition table if needed on YSQL restore), there is a rare deadlock
issue between ProcessTabletReportBatch and RepartitionTable.  It can be
hit when

- thread 1 (RepartitionTable): import a YSQL snapshot where the number
  of tablets for a table mismatch between the cluster and the external
  snapshot
- thread 2 (ProcessTabletReportBatch): process tablets of that table
  from heartbeat

A more in-depth sequence of steps follows:

1. t1: table->LockForWrite (WriteLock)
1. t2: table->LockForRead (ReadLock)
1. t1: tablet->StartMutation (WriteLock)
2. t1: table_lock.Commit (UpgradeToCommitLock; blocks on t2 ReadLock)
3. t2: tablet->LockForWrite (WriteLock; blocks on t1 WriteLock)

To fix, for ProcessTabletReportBatch, take table write lock instead of
read lock.  The table metadata isn't mutated, so this is purely for
deadlock avoidance reasons (since only one writer is allowed at a time).

Bogdan thinks we should expect table write lock to be taken whenever
tablet write lock is taken.

For backport to 2.6, since commit
afd8775 ([#9182] Fix return from
CatalogManager::ProcessTabletReport) is not present, do changes to
ProcessTabletReport instead.

Depends on D13851

Original Commit: cc90f01

Original Differential Revision: https://phabricator.dev.yugabyte.com/D13459

Test Plan:
    ./yb_build.sh \
      --cxx-test tools_yb-backup-test_ent \
      --gtest_filter YBBackupTest.TestYSQLChangeDefaultNumTablets \
      -n 1000 \
      --tp 1 \
      fastdebug

Reviewers: nicolas

Reviewed By: nicolas

Subscribers: bogdan, ybase

Differential Revision: https://phabricator.dev.yugabyte.com/D13852
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/docdb YugabyteDB core features area/ysql Yugabyte SQL (YSQL) kind/bug This issue is a bug
Projects
Development

No branches or pull requests

1 participant