diff --git a/src/yb/tablet/operations/truncate_operation.cc b/src/yb/tablet/operations/truncate_operation.cc index 18dd9b1e1a4a..4a2c1be44d7d 100644 --- a/src/yb/tablet/operations/truncate_operation.cc +++ b/src/yb/tablet/operations/truncate_operation.cc @@ -22,6 +22,10 @@ #include "yb/util/trace.h" +DEFINE_test_flag(bool, skip_applying_truncate, false, + "If true, the test will skip applying tablet truncate operation." + "Note that other operations will still be applied."); + namespace yb { namespace tablet { @@ -43,6 +47,11 @@ Status TruncateOperation::DoAborted(const Status& status) { Status TruncateOperation::DoReplicated(int64_t leader_term, Status* complete_status) { TRACE("APPLY TRUNCATE: started"); + if (FLAGS_TEST_skip_applying_truncate) { + TRACE("APPLY TRUNCATE: skipped"); + return Status::OK(); + } + RETURN_NOT_OK(VERIFY_RESULT(tablet_safe())->Truncate(this)); TRACE("APPLY TRUNCATE: finished"); diff --git a/src/yb/tablet/tablet_bootstrap.cc b/src/yb/tablet/tablet_bootstrap.cc index d74b03522b18..7ee0a89ab3ef 100644 --- a/src/yb/tablet/tablet_bootstrap.cc +++ b/src/yb/tablet/tablet_bootstrap.cc @@ -1739,6 +1739,9 @@ class TabletBootstrap { TruncateOperation operation(tablet_, req); + operation.set_op_id(OpId::FromPB(replicate_msg->id())); + operation.set_hybrid_time(HybridTime::FromPB(replicate_msg->hybrid_time())); + Status s = tablet_->Truncate(&operation); RETURN_NOT_OK_PREPEND(s, "Failed to Truncate:"); diff --git a/src/yb/tablet/transaction_loader.cc b/src/yb/tablet/transaction_loader.cc index c5726a071d64..e5155351bf8a 100644 --- a/src/yb/tablet/transaction_loader.cc +++ b/src/yb/tablet/transaction_loader.cc @@ -89,6 +89,10 @@ class TransactionLoader::Executor { Status status; auto se = ScopeExit([this, &status] { + regular_iterator_.Reset(); + intents_iterator_.Reset(); + scoped_pending_operation_.Reset(); + loader_.FinishLoad(status); // Destroy this executor object. Must be the last statement before we return from the Execute // function. diff --git a/src/yb/tserver/ts_tablet_manager.cc b/src/yb/tserver/ts_tablet_manager.cc index 9f9f86d0f2d9..cd4414f77bc6 100644 --- a/src/yb/tserver/ts_tablet_manager.cc +++ b/src/yb/tserver/ts_tablet_manager.cc @@ -838,10 +838,16 @@ void TSTabletManager::CleanupSplitTablets() { } } -Status TSTabletManager::WaitForAllBootstrapsToFinish() { +Status TSTabletManager::WaitForAllBootstrapsToFinish(MonoDelta timeout) { CHECK_EQ(state(), MANAGER_RUNNING); - open_tablet_pool_->Wait(); + if (timeout.Initialized()) { + if (!open_tablet_pool_->WaitFor(timeout)) { + return STATUS(TimedOut, "Timeout waiting for all bootstraps to finish"); + } + } else { + open_tablet_pool_->Wait(); + } Status s = Status::OK(); diff --git a/src/yb/tserver/ts_tablet_manager.h b/src/yb/tserver/ts_tablet_manager.h index 808a2b09c367..2ed4716e2820 100644 --- a/src/yb/tserver/ts_tablet_manager.h +++ b/src/yb/tserver/ts_tablet_manager.h @@ -160,7 +160,7 @@ class TSTabletManager : public tserver::TabletPeerLookupIf, public tablet::Table // Returns Status::OK if all tablets bootstrapped successfully. If // the bootstrap of any tablet failed returns the failure reason for // the first tablet whose bootstrap failed. - Status WaitForAllBootstrapsToFinish(); + Status WaitForAllBootstrapsToFinish(MonoDelta timeout = MonoDelta()); // Starts shutdown process. void StartShutdown(); diff --git a/src/yb/yql/pgwrapper/pg_single_tserver-test.cc b/src/yb/yql/pgwrapper/pg_single_tserver-test.cc index dc68835eb6f9..aa94e6dc43a0 100644 --- a/src/yb/yql/pgwrapper/pg_single_tserver-test.cc +++ b/src/yb/yql/pgwrapper/pg_single_tserver-test.cc @@ -10,12 +10,15 @@ // or implied. See the License for the specific language governing permissions and limitations // under the License. // +#include "yb/consensus/log.h" #include "yb/tablet/tablet.h" #include "yb/tablet/tablet_peer.h" #include "yb/tablet/transaction_participant.h" #include "yb/tserver/mini_tablet_server.h" +#include "yb/tserver/tablet_server.h" +#include "yb/tserver/ts_tablet_manager.h" #include "yb/util/countdown_latch.h" #include "yb/util/hdr_histogram.h" @@ -35,12 +38,15 @@ DECLARE_bool(ysql_enable_packed_row_for_colocated_table); DECLARE_int64(global_memstore_size_mb_max); DECLARE_int64(db_block_cache_size_bytes); DECLARE_int32(rocksdb_max_write_buffer_number); +DECLARE_bool(TEST_skip_applying_truncate); METRIC_DECLARE_histogram(handler_latency_yb_tserver_TabletServerService_Read); METRIC_DECLARE_histogram(handler_latency_yb_tserver_TabletServerService_Write); DEFINE_RUNTIME_int32(TEST_scan_reads, 3, "Number of reads in scan tests"); +using namespace std::literals; + namespace yb::pgwrapper { class PgSingleTServerTest : public PgMiniTestBase { @@ -760,6 +766,34 @@ TEST_F(PgSingleTServerTest, RangeConflict) { ASSERT_NOK(conn2.Execute("INSERT INTO t VALUES (0, 2), (1, 2)")); } +TEST_F(PgSingleTServerTest, BootstrapReplayTruncate) { + auto conn = ASSERT_RESULT(Connect()); + ASSERT_OK(conn.Execute( + "CREATE TABLE t(id INT PRIMARY KEY, s TEXT) SPLIT INTO 1 TABLETS;")); + + ANNOTATE_UNPROTECTED_WRITE(FLAGS_TEST_skip_applying_truncate) = true; + ASSERT_OK(conn.Execute("TRUNCATE TABLE t;")); + + // Rollover and flush the WAL, so that the truncate will be replayed during next restart. + auto peers = ListTabletPeers(cluster_.get(), ListPeersFilter::kAll); + for (const auto& peer : peers) { + if (peer->tablet()->transaction_participant()) { + ASSERT_OK(peer->log()->AllocateSegmentAndRollOver()); + } + } + + ANNOTATE_UNPROTECTED_WRITE(FLAGS_TEST_skip_applying_truncate) = false; + + auto* ts = cluster_->mini_tablet_server(0); + + ASSERT_OK(ts->Restart()); + + auto timeout = MonoDelta::FromSeconds(10); + if (!ts->server()->tablet_manager()->WaitForAllBootstrapsToFinish(timeout).ok()) { + LOG(FATAL) << "Tablet bootstrap didn't complete within within " << timeout.ToString(); + } +} + TEST_F(PgSingleTServerTest, UpdateIndexWithHole) { auto conn = ASSERT_RESULT(Connect()); ASSERT_OK(conn.Execute("CREATE TABLE t (id INT PRIMARY KEY, value INT)"));