/Users/deen/code/yugabyte-db/src/yb/tablet/tablet_peer-test.cc
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | // Licensed to the Apache Software Foundation (ASF) under one  | 
2  |  | // or more contributor license agreements.  See the NOTICE file  | 
3  |  | // distributed with this work for additional information  | 
4  |  | // regarding copyright ownership.  The ASF licenses this file  | 
5  |  | // to you under the Apache License, Version 2.0 (the  | 
6  |  | // "License"); you may not use this file except in compliance  | 
7  |  | // with the License.  You may obtain a copy of the License at  | 
8  |  | //  | 
9  |  | //   http://www.apache.org/licenses/LICENSE-2.0  | 
10  |  | //  | 
11  |  | // Unless required by applicable law or agreed to in writing,  | 
12  |  | // software distributed under the License is distributed on an  | 
13  |  | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY  | 
14  |  | // KIND, either express or implied.  See the License for the  | 
15  |  | // specific language governing permissions and limitations  | 
16  |  | // under the License.  | 
17  |  | //  | 
18  |  | // The following only applies to changes made to this file as part of YugaByte development.  | 
19  |  | //  | 
20  |  | // Portions Copyright (c) YugaByte, Inc.  | 
21  |  | //  | 
22  |  | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except  | 
23  |  | // in compliance with the License.  You may obtain a copy of the License at  | 
24  |  | //  | 
25  |  | // http://www.apache.org/licenses/LICENSE-2.0  | 
26  |  | //  | 
27  |  | // Unless required by applicable law or agreed to in writing, software distributed under the License  | 
28  |  | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  | 
29  |  | // or implied.  See the License for the specific language governing permissions and limitations  | 
30  |  | // under the License.  | 
31  |  | //  | 
32  |  |  | 
33  |  | #include <glog/logging.h>  | 
34  |  | #include <gtest/gtest.h>  | 
35  |  |  | 
36  |  | #include "yb/common/hybrid_time.h"  | 
37  |  | #include "yb/common/wire_protocol-test-util.h"  | 
38  |  | #include "yb/common/wire_protocol.h"  | 
39  |  |  | 
40  |  | #include "yb/consensus/consensus.h"  | 
41  |  | #include "yb/consensus/consensus_fwd.h"  | 
42  |  | #include "yb/consensus/consensus_meta.h"  | 
43  |  | #include "yb/consensus/log.h"  | 
44  |  | #include "yb/consensus/log_anchor_registry.h"  | 
45  |  | #include "yb/consensus/log_reader.h"  | 
46  |  | #include "yb/consensus/log_util.h"  | 
47  |  | #include "yb/consensus/metadata.pb.h"  | 
48  |  | #include "yb/consensus/opid_util.h"  | 
49  |  | #include "yb/consensus/multi_raft_batcher.h"  | 
50  |  | #include "yb/consensus/state_change_context.h"  | 
51  |  |  | 
52  |  | #include "yb/gutil/bind.h"  | 
53  |  | #include "yb/gutil/macros.h"  | 
54  |  |  | 
55  |  | #include "yb/rpc/messenger.h"  | 
56  |  | #include "yb/rpc/proxy.h"  | 
57  |  |  | 
58  |  | #include "yb/server/clock.h"  | 
59  |  | #include "yb/server/logical_clock.h"  | 
60  |  |  | 
61  |  | #include "yb/tablet/tablet-test-util.h"  | 
62  |  | #include "yb/tablet/tablet.h"  | 
63  |  | #include "yb/tablet/tablet_metadata.h"  | 
64  |  | #include "yb/tablet/tablet_peer.h"  | 
65  |  | #include "yb/tablet/write_query.h"  | 
66  |  |  | 
67  |  | #include "yb/tserver/tserver.pb.h"  | 
68  |  |  | 
69  |  | #include "yb/util/metrics.h"  | 
70  |  | #include "yb/util/result.h"  | 
71  |  | #include "yb/util/status_log.h"  | 
72  |  | #include "yb/util/test_macros.h"  | 
73  |  | #include "yb/util/threadpool.h"  | 
74  |  |  | 
75  |  | METRIC_DECLARE_entity(table);  | 
76  |  | METRIC_DECLARE_entity(tablet);  | 
77  |  |  | 
78  |  | DECLARE_int32(log_min_seconds_to_retain);  | 
79  |  |  | 
80  |  | DECLARE_bool(quick_leader_election_on_create);  | 
81  |  |  | 
82  |  | namespace yb { | 
83  |  | namespace tablet { | 
84  |  |  | 
85  |  | using consensus::Consensus;  | 
86  |  | using consensus::ConsensusBootstrapInfo;  | 
87  |  | using consensus::ConsensusMetadata;  | 
88  |  | using consensus::MakeOpId;  | 
89  |  | using consensus::MinimumOpId;  | 
90  |  | using consensus::OpIdEquals;  | 
91  |  | using consensus::RaftPeerPB;  | 
92  |  | using consensus::WRITE_OP;  | 
93  |  | using docdb::KeyValueWriteBatchPB;  | 
94  |  | using log::Log;  | 
95  |  | using log::LogAnchorRegistry;  | 
96  |  | using log::LogOptions;  | 
97  |  | using server::Clock;  | 
98  |  | using server::LogicalClock;  | 
99  |  | using std::shared_ptr;  | 
100  |  | using std::string;  | 
101  |  | using strings::Substitute;  | 
102  |  | using tserver::WriteRequestPB;  | 
103  |  | using tserver::WriteResponsePB;  | 
104  |  |  | 
105  | 4  | static Schema GetTestSchema() { | 
106  | 4  |   return Schema({ ColumnSchema("key", INT32) }, 1); | 
107  | 4  | }  | 
108  |  |  | 
109  |  | class TabletPeerTest : public YBTabletTest { | 
110  |  |  public:  | 
111  |  |   TabletPeerTest()  | 
112  |  |     : YBTabletTest(GetTestSchema(), YQL_TABLE_TYPE),  | 
113  |  |       insert_counter_(0),  | 
114  | 4  |       delete_counter_(0) { | 
115  | 4  |   }  | 
116  |  |  | 
117  | 4  |   void SetUp() override { | 
118  | 4  |     YBTabletTest::SetUp();  | 
119  |  |  | 
120  | 4  |     ASSERT_OK(ThreadPoolBuilder("raft").Build(&raft_pool_)); | 
121  | 4  |     ASSERT_OK(ThreadPoolBuilder("prepare").Build(&tablet_prepare_pool_)); | 
122  |  |  | 
123  | 4  |     rpc::MessengerBuilder builder(CURRENT_TEST_NAME());  | 
124  | 4  |     messenger_ = ASSERT_RESULT(builder.Build());  | 
125  | 4  |     proxy_cache_ = std::make_unique<rpc::ProxyCache>(messenger_.get());  | 
126  |  |  | 
127  | 4  |     table_metric_entity_ = METRIC_ENTITY_table.Instantiate(&metric_registry_, "test-table");  | 
128  | 4  |     tablet_metric_entity_ = METRIC_ENTITY_tablet.Instantiate(&metric_registry_, "test-tablet");  | 
129  |  |  | 
130  | 4  |     RaftPeerPB config_peer;  | 
131  | 4  |     config_peer.set_permanent_uuid(tablet()->metadata()->fs_manager()->uuid());  | 
132  | 4  |     config_peer.set_member_type(consensus::PeerMemberType::VOTER);  | 
133  | 4  |     auto addr = config_peer.mutable_last_known_private_addr()->Add();  | 
134  | 4  |     addr->set_host("fake-host"); | 
135  | 4  |     addr->set_port(0);  | 
136  |  |  | 
137  | 4  |     multi_raft_manager_ = std::make_unique<consensus::MultiRaftManager>(messenger_.get(),  | 
138  | 4  |                                                                         proxy_cache_.get(),  | 
139  | 4  |                                                                         config_peer.cloud_info());  | 
140  |  |  | 
141  |  |     // "Bootstrap" and start the TabletPeer.  | 
142  | 4  |     tablet_peer_.reset(new TabletPeer(  | 
143  | 4  |         make_scoped_refptr(tablet()->metadata()), config_peer, clock(),  | 
144  | 4  |         tablet()->metadata()->fs_manager()->uuid(),  | 
145  | 4  |         Bind(  | 
146  | 4  |             &TabletPeerTest::TabletPeerStateChangedCallback,  | 
147  | 4  |             Unretained(this),  | 
148  | 4  |             tablet()->tablet_id()),  | 
149  | 4  |         &metric_registry_,  | 
150  | 4  |         nullptr, // tablet_splitter  | 
151  | 4  |         std::shared_future<client::YBClient*>()));  | 
152  |  |  | 
153  |  |     // Make TabletPeer use the same LogAnchorRegistry as the Tablet created by the harness.  | 
154  |  |     // TODO: Refactor TabletHarness to allow taking a LogAnchorRegistry, while also providing  | 
155  |  |     // RaftGroupMetadata for consumption by TabletPeer before Tablet is instantiated.  | 
156  | 4  |     tablet_peer_->log_anchor_registry_ = tablet()->log_anchor_registry_;  | 
157  |  |  | 
158  | 4  |     consensus::RaftConfigPB config;  | 
159  | 4  |     config.add_peers()->CopyFrom(config_peer);  | 
160  | 4  |     config.set_opid_index(consensus::kInvalidOpIdIndex);  | 
161  |  |  | 
162  | 4  |     std::unique_ptr<ConsensusMetadata> cmeta;  | 
163  | 4  |     ASSERT_OK(ConsensusMetadata::Create(tablet()->metadata()->fs_manager(),  | 
164  | 4  |                                         tablet()->tablet_id(),  | 
165  | 4  |                                         tablet()->metadata()->fs_manager()->uuid(),  | 
166  | 4  |                                         config,  | 
167  | 4  |                                         consensus::kMinimumTerm,  | 
168  | 4  |                                         &cmeta));  | 
169  |  |  | 
170  | 4  |     ASSERT_OK(ThreadPoolBuilder("log") | 
171  | 4  |                  .unlimited_threads()  | 
172  | 4  |                  .Build(&log_thread_pool_));  | 
173  | 4  |     scoped_refptr<Log> log;  | 
174  | 4  |     ASSERT_OK(Log::Open(LogOptions(), tablet()->tablet_id(),  | 
175  | 4  |                         tablet()->metadata()->wal_dir(), tablet()->metadata()->fs_manager()->uuid(),  | 
176  | 4  |                         *tablet()->schema(), tablet()->metadata()->schema_version(),  | 
177  | 4  |                         table_metric_entity_.get(), tablet_metric_entity_.get(),  | 
178  | 4  |                         log_thread_pool_.get(), log_thread_pool_.get(),  | 
179  | 4  |                         tablet()->metadata()->cdc_min_replicated_index(), &log));  | 
180  |  |  | 
181  | 4  |     ASSERT_OK(tablet_peer_->SetBootstrapping());  | 
182  | 4  |     ASSERT_OK(tablet_peer_->InitTabletPeer(tablet(),  | 
183  | 4  |                                            nullptr /* server_mem_tracker */,  | 
184  | 4  |                                            messenger_.get(),  | 
185  | 4  |                                            proxy_cache_.get(),  | 
186  | 4  |                                            log,  | 
187  | 4  |                                            table_metric_entity_,  | 
188  | 4  |                                            tablet_metric_entity_,  | 
189  | 4  |                                            raft_pool_.get(),  | 
190  | 4  |                                            tablet_prepare_pool_.get(),  | 
191  | 4  |                                            nullptr /* retryable_requests */,  | 
192  | 4  |                                            multi_raft_manager_.get()));  | 
193  | 4  |   }  | 
194  |  |  | 
195  | 3  |   CHECKED_STATUS StartPeer(const ConsensusBootstrapInfo& info) { | 
196  | 3  |     RETURN_NOT_OK(tablet_peer_->Start(info));  | 
197  |  |  | 
198  | 3  |     return LoggedWaitFor([&]() -> Result<bool> { | 
199  | 3  |       if (FLAGS_quick_leader_election_on_create) { | 
200  | 0  |         return tablet_peer_->LeaderStatus() == consensus::LeaderStatus::LEADER_AND_READY;  | 
201  | 0  |       }  | 
202  | 3  |       RETURN_NOT_OK(tablet_peer_->consensus()->EmulateElection());  | 
203  | 3  |       return true;  | 
204  | 3  |     }, MonoDelta::FromMilliseconds(500), "If quick leader elections enabled, wait for peer to be a "  | 
205  | 3  |                                          "leader, otherwise emulate.");  | 
206  | 3  |   }  | 
207  |  |  | 
208  |  |   void TabletPeerStateChangedCallback(  | 
209  |  |       const string& tablet_id,  | 
210  | 11  |       std::shared_ptr<consensus::StateChangeContext> context) { | 
211  | 11  |     LOG(INFO) << "Tablet peer state changed for tablet " << tablet_id  | 
212  | 11  |               << ". Reason: " << context->ToString();  | 
213  | 11  |   }  | 
214  |  |  | 
215  | 4  |   void TearDown() override { | 
216  | 4  |     messenger_->Shutdown();  | 
217  | 4  |     WARN_NOT_OK(tablet_peer_->Shutdown(), "Tablet peer shutdown failed");  | 
218  | 4  |     YBTabletTest::TearDown();  | 
219  | 4  |   }  | 
220  |  |  | 
221  |  |  protected:  | 
222  |  |   // Generate monotonic sequence of key column integers.  | 
223  | 11  |   void GenerateSequentialInsertRequest(WriteRequestPB* write_req) { | 
224  | 11  |     write_req->set_tablet_id(tablet()->tablet_id());  | 
225  | 11  |     AddTestRowInsert(insert_counter_++, write_req);  | 
226  | 11  |   }  | 
227  |  |  | 
228  |  |   // Generate monotonic sequence of deletions, starting with 0.  | 
229  |  |   // Will assert if you try to delete more rows than you inserted.  | 
230  | 2  |   void GenerateSequentialDeleteRequest(WriteRequestPB* write_req) { | 
231  | 2  |     CHECK_LT(delete_counter_, insert_counter_);  | 
232  | 2  |     write_req->set_tablet_id(tablet()->tablet_id());  | 
233  | 2  |     AddTestRowDelete(delete_counter_++, write_req);  | 
234  | 2  |   }  | 
235  |  |  | 
236  | 13  |   Status ExecuteWriteAndRollLog(TabletPeer* tablet_peer, const WriteRequestPB& req) { | 
237  | 13  |     WriteResponsePB resp;  | 
238  | 13  |     auto query = std::make_unique<WriteQuery>(  | 
239  | 13  |         /* leader_term */ 1, CoarseTimePoint::max(), tablet_peer, tablet_peer->tablet(), &resp);  | 
240  | 13  |     query->set_client_request(req);  | 
241  |  |  | 
242  | 13  |     CountDownLatch rpc_latch(1);  | 
243  | 13  |     query->set_callback(MakeLatchOperationCompletionCallback(&rpc_latch, &resp));  | 
244  |  |  | 
245  | 13  |     tablet_peer->WriteAsync(std::move(query));  | 
246  | 13  |     rpc_latch.Wait();  | 
247  | 0  |     CHECK(!resp.has_error())  | 
248  | 0  |         << "\nReq:\n" << req.DebugString() << "Resp:\n" << resp.DebugString();  | 
249  |  |  | 
250  | 13  |     Synchronizer synchronizer;  | 
251  | 13  |     CHECK_OK(tablet_peer->log_->TEST_SubmitFuncToAppendToken([&synchronizer, tablet_peer] { | 
252  | 13  |       synchronizer.StatusCB(tablet_peer->log_->AllocateSegmentAndRollOver());  | 
253  | 13  |     }));  | 
254  | 13  |     return synchronizer.Wait();  | 
255  | 13  |   }  | 
256  |  |  | 
257  |  |   // Execute insert requests and roll log after each one.  | 
258  | 4  |   CHECKED_STATUS ExecuteInsertsAndRollLogs(int num_inserts) { | 
259  | 15  |     for (int i = 0; i < num_inserts; i++) { | 
260  | 11  |       WriteRequestPB req;  | 
261  | 11  |       GenerateSequentialInsertRequest(&req);  | 
262  | 11  |       RETURN_NOT_OK(ExecuteWriteAndRollLog(tablet_peer_.get(), req));  | 
263  | 11  |     }  | 
264  |  |  | 
265  | 4  |     return Status::OK();  | 
266  | 4  |   }  | 
267  |  |  | 
268  |  |   // Execute delete requests and roll log after each one.  | 
269  | 1  |   Status ExecuteDeletesAndRollLogs(int num_deletes) { | 
270  | 3  |     for (int i = 0; i < num_deletes; i++) { | 
271  | 2  |       WriteRequestPB req;  | 
272  | 2  |       GenerateSequentialDeleteRequest(&req);  | 
273  | 2  |       CHECK_OK(ExecuteWriteAndRollLog(tablet_peer_.get(), req));  | 
274  | 2  |     }  | 
275  |  |  | 
276  | 1  |     return Status::OK();  | 
277  | 1  |   }  | 
278  |  |  | 
279  |  |   // Assert that the Log GC() anchor is earlier than the latest OpId in the Log.  | 
280  | 2  |   void AssertLogAnchorEarlierThanLogLatest() { | 
281  | 2  |     int64_t earliest_index = ASSERT_RESULT(tablet_peer_->GetEarliestNeededLogIndex());  | 
282  | 2  |     auto last_log_opid = tablet_peer_->log_->GetLatestEntryOpId();  | 
283  | 4  |     ASSERT_LE(earliest_index, last_log_opid.index)  | 
284  | 4  |       << "Expected valid log anchor, got earliest opid: " << earliest_index  | 
285  | 4  |       << " (expected any value earlier than last log id: " << last_log_opid << ")";  | 
286  | 2  |   }  | 
287  |  |  | 
288  |  |   // We disable automatic log GC. Don't leak those changes.  | 
289  |  |   google::FlagSaver flag_saver_;  | 
290  |  |  | 
291  |  |   int32_t insert_counter_;  | 
292  |  |   int32_t delete_counter_;  | 
293  |  |   MetricRegistry metric_registry_;  | 
294  |  |   scoped_refptr<MetricEntity> table_metric_entity_;  | 
295  |  |   scoped_refptr<MetricEntity> tablet_metric_entity_;  | 
296  |  |   std::unique_ptr<rpc::Messenger> messenger_;  | 
297  |  |   std::unique_ptr<rpc::ProxyCache> proxy_cache_;  | 
298  |  |   std::unique_ptr<ThreadPool> raft_pool_;  | 
299  |  |   std::unique_ptr<ThreadPool> tablet_prepare_pool_;  | 
300  |  |   std::unique_ptr<ThreadPool> log_thread_pool_;  | 
301  |  |   std::shared_ptr<TabletPeer> tablet_peer_;  | 
302  |  |   std::unique_ptr<consensus::MultiRaftManager> multi_raft_manager_;  | 
303  |  | };  | 
304  |  |  | 
305  |  | // Ensure that Log::GC() doesn't delete logs with anchors.  | 
306  | 1  | TEST_F(TabletPeerTest, TestLogAnchorsAndGC) { | 
307  | 1  |   FLAGS_log_min_seconds_to_retain = 0;  | 
308  | 1  |   ConsensusBootstrapInfo info;  | 
309  | 1  |   ASSERT_OK(StartPeer(info));  | 
310  |  |  | 
311  | 1  |   Log* log = tablet_peer_->log();  | 
312  | 1  |   int32_t num_gced;  | 
313  |  |  | 
314  | 1  |   log::SegmentSequence segments;  | 
315  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
316  |  |  | 
317  | 1  |   ASSERT_EQ(1, segments.size());  | 
318  | 1  |   ASSERT_OK(ExecuteInsertsAndRollLogs(3));  | 
319  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
320  | 1  |   ASSERT_EQ(4, segments.size());  | 
321  |  |  | 
322  | 1  |   ASSERT_NO_FATALS(AssertLogAnchorEarlierThanLogLatest());  | 
323  |  |  | 
324  |  |   // Ensure nothing gets deleted.  | 
325  | 1  |   int64_t min_log_index = ASSERT_RESULT(tablet_peer_->GetEarliestNeededLogIndex());  | 
326  | 1  |   ASSERT_OK(log->GC(min_log_index, &num_gced));  | 
327  | 2  |   ASSERT_EQ(2, num_gced) << "Earliest needed: " << min_log_index;  | 
328  |  |  | 
329  |  |   // Flush RocksDB to ensure that we don't have OpId in anchors.  | 
330  | 1  |   ASSERT_OK(tablet_peer_->tablet()->Flush(tablet::FlushMode::kSync));  | 
331  |  |  | 
332  |  |   // The first two segments should be deleted.  | 
333  |  |   // The last is anchored due to the commit in the last segment being the last  | 
334  |  |   // OpId in the log.  | 
335  | 1  |   int32_t earliest_needed = 0;  | 
336  | 1  |   auto total_segments = log->GetLogReader()->num_segments();  | 
337  | 1  |   min_log_index = ASSERT_RESULT(tablet_peer_->GetEarliestNeededLogIndex());  | 
338  | 1  |   ASSERT_OK(log->GC(min_log_index, &num_gced));  | 
339  | 2  |   ASSERT_EQ(earliest_needed, num_gced) << "earliest needed: " << min_log_index;  | 
340  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
341  | 1  |   ASSERT_EQ(total_segments - earliest_needed, segments.size());  | 
342  | 1  | }  | 
343  |  |  | 
344  |  | // Ensure that Log::GC() doesn't delete logs when the DMS has an anchor.  | 
345  | 1  | TEST_F(TabletPeerTest, TestDMSAnchorPreventsLogGC) { | 
346  | 1  |   FLAGS_log_min_seconds_to_retain = 0;  | 
347  | 1  |   ConsensusBootstrapInfo info;  | 
348  | 1  |   ASSERT_OK(StartPeer(info));  | 
349  |  |  | 
350  | 1  |   Log* log = tablet_peer_->log_.get();  | 
351  | 1  |   int32_t num_gced;  | 
352  |  |  | 
353  | 1  |   log::SegmentSequence segments;  | 
354  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
355  |  |  | 
356  | 1  |   ASSERT_EQ(1, segments.size());  | 
357  | 1  |   ASSERT_OK(ExecuteInsertsAndRollLogs(2));  | 
358  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
359  | 1  |   ASSERT_EQ(3, segments.size());  | 
360  |  |  | 
361  |  |   // Flush RocksDB so the next mutation goes into a DMS.  | 
362  | 1  |   ASSERT_OK(tablet_peer_->tablet()->Flush(tablet::FlushMode::kSync));  | 
363  |  |  | 
364  | 1  |   int32_t earliest_needed = 1;  | 
365  | 1  |   auto total_segments = log->GetLogReader()->num_segments();  | 
366  | 1  |   int64_t min_log_index = ASSERT_RESULT(tablet_peer_->GetEarliestNeededLogIndex());  | 
367  | 1  |   ASSERT_OK(log->GC(min_log_index, &num_gced));  | 
368  |  |   // We will only GC 1, and have 1 left because the earliest needed OpId falls  | 
369  |  |   // back to the latest OpId written to the Log if no anchors are set.  | 
370  | 1  |   ASSERT_EQ(earliest_needed, num_gced);  | 
371  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
372  | 1  |   ASSERT_EQ(total_segments - earliest_needed, segments.size());  | 
373  |  |  | 
374  | 1  |   auto id = log->GetLatestEntryOpId();  | 
375  | 1  |   LOG(INFO) << "Before: " << id;  | 
376  |  |  | 
377  |  |   // We currently have no anchors and the last operation in the log is 0.3  | 
378  |  |   // Before the below was ExecuteDeletesAndRollLogs(1) but that was breaking  | 
379  |  |   // what I think is a wrong assertion.  | 
380  |  |   // I.e. since 0.4 is the last operation that we know is in memory 0.4 is the  | 
381  |  |   // last anchor we expect _and_ it's the last op in the log.  | 
382  |  |   // Only if we apply two operations is the last anchored operation and the  | 
383  |  |   // last operation in the log different.  | 
384  |  |  | 
385  |  |   // Execute a mutation.  | 
386  | 1  |   ASSERT_OK(ExecuteDeletesAndRollLogs(2));  | 
387  | 1  |   ASSERT_NO_FATALS(AssertLogAnchorEarlierThanLogLatest());  | 
388  |  |  | 
389  | 1  |   total_segments += 1;  | 
390  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
391  | 1  |   ASSERT_EQ(total_segments, segments.size());  | 
392  |  |  | 
393  |  |   // Execute another couple inserts, but Flush it so it doesn't anchor.  | 
394  | 1  |   ASSERT_OK(ExecuteInsertsAndRollLogs(2));  | 
395  | 1  |   total_segments += 2;  | 
396  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
397  | 1  |   ASSERT_EQ(total_segments, segments.size());  | 
398  |  |  | 
399  |  |   // Ensure the delta and last insert remain in the logs, anchored by the delta.  | 
400  |  |   // Note that this will allow GC of the 2nd insert done above.  | 
401  | 1  |   earliest_needed = 4;  | 
402  | 1  |   min_log_index = ASSERT_RESULT(tablet_peer_->GetEarliestNeededLogIndex());  | 
403  | 1  |   ASSERT_OK(log->GC(min_log_index, &num_gced));  | 
404  | 1  |   ASSERT_EQ(earliest_needed, num_gced);  | 
405  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
406  | 1  |   ASSERT_EQ(total_segments - earliest_needed, segments.size());  | 
407  |  |  | 
408  | 1  |   earliest_needed = 0;  | 
409  | 1  |   total_segments = log->GetLogReader()->num_segments();  | 
410  |  |   // We should only hang onto one segment due to no anchors.  | 
411  |  |   // The last log OpId is the commit in the last segment, so it only anchors  | 
412  |  |   // that segment, not the previous, because it's not the first OpId in the  | 
413  |  |   // segment.  | 
414  | 1  |   min_log_index = ASSERT_RESULT(tablet_peer_->GetEarliestNeededLogIndex());  | 
415  | 1  |   ASSERT_OK(log->GC(min_log_index, &num_gced));  | 
416  | 1  |   ASSERT_EQ(earliest_needed, num_gced);  | 
417  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
418  | 1  |   ASSERT_EQ(total_segments - earliest_needed, segments.size());  | 
419  | 1  | }  | 
420  |  |  | 
421  |  | // Ensure that Log::GC() doesn't compact logs with OpIds of active transactions.  | 
422  | 1  | TEST_F(TabletPeerTest, TestActiveOperationPreventsLogGC) { | 
423  | 1  |   FLAGS_log_min_seconds_to_retain = 0;  | 
424  | 1  |   ConsensusBootstrapInfo info;  | 
425  | 1  |   ASSERT_OK(StartPeer(info));  | 
426  |  |  | 
427  | 1  |   Log* log = tablet_peer_->log_.get();  | 
428  |  |  | 
429  | 1  |   log::SegmentSequence segments;  | 
430  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
431  |  |  | 
432  | 1  |   ASSERT_EQ(1, segments.size());  | 
433  | 1  |   ASSERT_OK(ExecuteInsertsAndRollLogs(4));  | 
434  | 1  |   ASSERT_OK(log->GetLogReader()->GetSegmentsSnapshot(&segments));  | 
435  | 1  |   ASSERT_EQ(5, segments.size());  | 
436  | 1  | }  | 
437  |  |  | 
438  | 1  | TEST_F(TabletPeerTest, TestGCEmptyLog) { | 
439  | 1  |   ConsensusBootstrapInfo info;  | 
440  | 1  |   ASSERT_OK(tablet_peer_->Start(info));  | 
441  |  |   // We don't wait on consensus on purpose.  | 
442  | 1  |   ASSERT_OK(tablet_peer_->RunLogGC());  | 
443  | 1  | }  | 
444  |  |  | 
445  |  | } // namespace tablet  | 
446  |  | } // namespace yb  |