YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/tools/yb-admin-snapshot-schedule-test.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#include "yb/client/ql-dml-test-base.h"
15
#include "yb/client/yb_table_name.h"
16
17
#include "yb/common/json_util.h"
18
19
#include "yb/integration-tests/cql_test_util.h"
20
#include "yb/integration-tests/external_mini_cluster.h"
21
#include "yb/integration-tests/load_balancer_test_util.h"
22
23
#include "yb/master/master_ddl.proxy.h"
24
25
#include "yb/rpc/rpc_controller.h"
26
27
#include "yb/tools/admin-test-base.h"
28
29
#include "yb/tserver/tserver_admin.proxy.h"
30
#include "yb/tserver/tserver_service.proxy.h"
31
32
#include "yb/util/date_time.h"
33
#include "yb/util/format.h"
34
#include "yb/util/random_util.h"
35
#include "yb/util/range.h"
36
#include "yb/util/scope_exit.h"
37
#include "yb/util/status_format.h"
38
#include "yb/util/test_thread_holder.h"
39
#include "yb/util/tsan_util.h"
40
41
#include "yb/yql/pgwrapper/libpq_utils.h"
42
43
DECLARE_uint64(max_clock_skew_usec);
44
45
namespace yb {
46
namespace tools {
47
48
namespace {
49
50
const std::string kClusterName = "yugacluster";
51
52
constexpr auto kInterval = 6s;
53
constexpr auto kRetention = 10min;
54
constexpr auto kHistoryRetentionIntervalSec = 5;
55
constexpr auto kCleanupSplitTabletsInterval = 1s;
56
57
} // namespace
58
59
class YbAdminSnapshotScheduleTest : public AdminTestBase {
60
 public:
61
0
  Result<rapidjson::Document> GetSnapshotSchedule(const std::string& id = std::string()) {
62
0
    auto out = VERIFY_RESULT(id.empty() ? CallJsonAdmin("list_snapshot_schedules")
63
0
                                        : CallJsonAdmin("list_snapshot_schedules", id));
64
0
    auto schedules = VERIFY_RESULT(Get(&out, "schedules")).get().GetArray();
65
0
    if (schedules.Empty()) {
66
0
      return STATUS(NotFound, "Snapshot schedule not found");
67
0
    }
68
0
    SCHECK_EQ(schedules.Size(), 1U, NotFound, "Wrong schedules number");
69
0
    rapidjson::Document result;
70
0
    result.CopyFrom(schedules[0], result.GetAllocator());
71
0
    return result;
72
0
  }
73
74
0
  Result<rapidjson::Document> ListSnapshots() {
75
0
    auto out = VERIFY_RESULT(CallJsonAdmin("list_snapshots", "JSON"));
76
0
    rapidjson::Document result;
77
0
    result.CopyFrom(VERIFY_RESULT(Get(&out, "snapshots")).get(), result.GetAllocator());
78
0
    return result;
79
0
  }
80
81
  Result<rapidjson::Document> ListTablets(
82
0
      const client::YBTableName& table_name = client::kTableName) {
83
0
    auto out = VERIFY_RESULT(CallJsonAdmin(
84
0
        "list_tablets", "ycql." + table_name.namespace_name(), table_name.table_name(), "JSON"));
85
0
    rapidjson::Document result;
86
0
    result.CopyFrom(VERIFY_RESULT(Get(&out, "tablets")).get(), result.GetAllocator());
87
0
    return result;
88
0
  }
89
90
  Result<rapidjson::Document> WaitScheduleSnapshot(
91
0
      MonoDelta duration, const std::string& id = std::string(), uint32_t num_snapshots = 1) {
92
0
    rapidjson::Document result;
93
0
    RETURN_NOT_OK(WaitFor([this, id, num_snapshots, &result]() -> Result<bool> {
94
0
      auto schedule = VERIFY_RESULT(GetSnapshotSchedule(id));
95
0
      auto snapshots = VERIFY_RESULT(Get(&schedule, "snapshots")).get().GetArray();
96
0
      if (snapshots.Size() < num_snapshots) {
97
0
        return false;
98
0
      }
99
0
      result.CopyFrom(snapshots[snapshots.Size() - 1], result.GetAllocator());
100
0
      return true;
101
0
    }, duration, "Wait schedule snapshot"));
102
103
    // Wait for the present time to become at-least the time chosen by the first snapshot.
104
0
    auto snapshot_time_string = VERIFY_RESULT(Get(&result, "snapshot_time")).get().GetString();
105
0
    HybridTime snapshot_ht = VERIFY_RESULT(HybridTime::ParseHybridTime(snapshot_time_string));
106
107
0
    RETURN_NOT_OK(WaitFor([&snapshot_ht]() -> Result<bool> {
108
0
      Timestamp current_time(VERIFY_RESULT(WallClock()->Now()).time_point);
109
0
      HybridTime current_ht = HybridTime::FromMicros(current_time.ToInt64());
110
0
      return snapshot_ht <= current_ht;
111
0
    }, duration, "Wait Snapshot Time Elapses"));
112
0
    return result;
113
0
  }
114
115
  Result<std::string> StartRestoreSnapshotSchedule(
116
0
      const std::string& schedule_id, Timestamp restore_at) {
117
0
    auto out = VERIFY_RESULT(CallJsonAdmin(
118
0
        "restore_snapshot_schedule", schedule_id, restore_at.ToFormattedString()));
119
0
    std::string restoration_id = VERIFY_RESULT(Get(out, "restoration_id")).get().GetString();
120
0
    LOG(INFO) << "Restoration id: " << restoration_id;
121
0
    return restoration_id;
122
0
  }
123
124
0
  CHECKED_STATUS RestoreSnapshotSchedule(const std::string& schedule_id, Timestamp restore_at) {
125
0
    return WaitRestorationDone(
126
0
        VERIFY_RESULT(
127
0
            StartRestoreSnapshotSchedule(schedule_id, restore_at)), 40s * kTimeMultiplier);
128
0
  }
129
130
0
  CHECKED_STATUS WaitRestorationDone(const std::string& restoration_id, MonoDelta timeout) {
131
0
    return WaitFor([this, restoration_id]() -> Result<bool> {
132
0
      auto out = VERIFY_RESULT(CallJsonAdmin("list_snapshot_restorations", restoration_id));
133
0
      LOG(INFO) << "Restorations: " << common::PrettyWriteRapidJsonToString(out);
134
0
      const auto& restorations = VERIFY_RESULT(Get(out, "restorations")).get().GetArray();
135
0
      SCHECK_EQ(restorations.Size(), 1U, IllegalState, "Wrong restorations number");
136
0
      auto id = VERIFY_RESULT(Get(restorations[0], "id")).get().GetString();
137
0
      SCHECK_EQ(id, restoration_id, IllegalState, "Wrong restoration id");
138
0
      std::string state_str = VERIFY_RESULT(Get(restorations[0], "state")).get().GetString();
139
0
      master::SysSnapshotEntryPB::State state;
140
0
      if (!master::SysSnapshotEntryPB_State_Parse(state_str, &state)) {
141
0
        return STATUS_FORMAT(IllegalState, "Failed to parse restoration state: $0", state_str);
142
0
      }
143
0
      if (state == master::SysSnapshotEntryPB::RESTORING) {
144
0
        return false;
145
0
      }
146
0
      if (state == master::SysSnapshotEntryPB::RESTORED) {
147
0
        return true;
148
0
      }
149
0
      return STATUS_FORMAT(IllegalState, "Unexpected restoration state: $0",
150
0
                           master::SysSnapshotEntryPB_State_Name(state));
151
0
    }, timeout, "Wait restoration complete");
152
0
  }
153
154
0
  CHECKED_STATUS PrepareCommon() {
155
0
    LOG(INFO) << "Create cluster";
156
0
    CreateCluster(kClusterName, ExtraTSFlags(), ExtraMasterFlags());
157
158
0
    LOG(INFO) << "Create client";
159
0
    client_ = VERIFY_RESULT(CreateClient());
160
161
0
    return Status::OK();
162
0
  }
163
164
0
  virtual std::vector<std::string> ExtraTSFlags() {
165
0
    return { Format("--timestamp_history_retention_interval_sec=$0", kHistoryRetentionIntervalSec),
166
0
             "--history_cutoff_propagation_interval_ms=1000",
167
0
             "--enable_automatic_tablet_splitting=true",
168
0
             Format("--cleanup_split_tablets_interval_sec=$0",
169
0
                      MonoDelta(kCleanupSplitTabletsInterval).ToSeconds()) };
170
0
  }
171
172
0
  virtual std::vector<std::string> ExtraMasterFlags() {
173
    // To speed up tests.
174
0
    return { "--snapshot_coordinator_cleanup_delay_ms=1000",
175
0
             "--snapshot_coordinator_poll_interval_ms=500",
176
0
             "--enable_automatic_tablet_splitting=true",
177
0
             "--enable_transactional_ddl_gc=false",
178
0
             "--allow_consecutive_restore=true" };
179
0
  }
180
181
0
  Result<std::string> PrepareQl(MonoDelta interval = kInterval, MonoDelta retention = kRetention) {
182
0
    RETURN_NOT_OK(PrepareCommon());
183
184
0
    LOG(INFO) << "Create namespace";
185
0
    RETURN_NOT_OK(client_->CreateNamespaceIfNotExists(
186
0
        client::kTableName.namespace_name(), client::kTableName.namespace_type()));
187
188
0
    return CreateSnapshotScheduleAndWaitSnapshot(
189
0
        client::kTableName.namespace_name(), interval, retention);
190
0
  }
191
192
  Result<std::string> CreateSnapshotScheduleAndWaitSnapshot(
193
0
      const std::string& filter, MonoDelta interval, MonoDelta retention) {
194
0
    LOG(INFO) << "Create snapshot schedule";
195
0
    std::string schedule_id = VERIFY_RESULT(CreateSnapshotSchedule(
196
0
        interval, retention, filter));
197
198
0
    LOG(INFO) << "Wait snapshot schedule";
199
0
    RETURN_NOT_OK(WaitScheduleSnapshot(30s, schedule_id));
200
201
0
    return schedule_id;
202
0
  }
203
204
0
  Result<std::string> PreparePg() {
205
0
    RETURN_NOT_OK(PrepareCommon());
206
207
0
    auto conn = VERIFY_RESULT(PgConnect());
208
0
    RETURN_NOT_OK(conn.ExecuteFormat("CREATE DATABASE $0", client::kTableName.namespace_name()));
209
210
0
    return CreateSnapshotScheduleAndWaitSnapshot(
211
0
        "ysql." + client::kTableName.namespace_name(), kInterval, kRetention);
212
0
  }
213
214
0
  Result<pgwrapper::PGConn> PgConnect(const std::string& db_name = std::string()) {
215
0
    auto* ts = cluster_->tablet_server(
216
0
        RandomUniformInt<size_t>(0, cluster_->num_tablet_servers() - 1));
217
0
    return pgwrapper::PGConn::Connect(HostPort(ts->bind_host(), ts->pgsql_rpc_port()), db_name);
218
0
  }
219
220
0
  Result<std::string> PrepareCql(MonoDelta interval = kInterval, MonoDelta retention = kRetention) {
221
0
    RETURN_NOT_OK(PrepareCommon());
222
223
0
    auto conn = VERIFY_RESULT(CqlConnect());
224
0
    RETURN_NOT_OK(conn.ExecuteQuery(Format(
225
0
        "CREATE KEYSPACE IF NOT EXISTS $0", client::kTableName.namespace_name())));
226
227
0
    return CreateSnapshotScheduleAndWaitSnapshot(
228
0
        "ycql." + client::kTableName.namespace_name(), interval, retention);
229
0
  }
230
231
  template <class... Args>
232
  Result<std::string> CreateSnapshotSchedule(
233
0
      MonoDelta interval, MonoDelta retention, Args&&... args) {
234
0
    auto out = VERIFY_RESULT(CallJsonAdmin(
235
0
        "create_snapshot_schedule", interval.ToMinutes(), retention.ToMinutes(),
236
0
        std::forward<Args>(args)...));
237
238
0
    std::string schedule_id = VERIFY_RESULT(Get(out, "schedule_id")).get().GetString();
239
0
    LOG(INFO) << "Schedule id: " << schedule_id;
240
0
    return schedule_id;
241
0
  }
Unexecuted instantiation: _ZN2yb5tools27YbAdminSnapshotScheduleTest22CreateSnapshotScheduleIJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEENS_6ResultIS9_EENS_9MonoDeltaESE_DpOT_
Unexecuted instantiation: _ZN2yb5tools27YbAdminSnapshotScheduleTest22CreateSnapshotScheduleIJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESB_EEENS_6ResultIS9_EENS_9MonoDeltaESE_DpOT_
242
243
0
  CHECKED_STATUS DeleteSnapshotSchedule(const std::string& schedule_id) {
244
0
    auto out = VERIFY_RESULT(CallJsonAdmin("delete_snapshot_schedule", schedule_id));
245
246
0
    SCHECK_EQ(VERIFY_RESULT(Get(out, "schedule_id")).get().GetString(), schedule_id, IllegalState,
247
0
              "Deleted wrong schedule");
248
0
    return Status::OK();
249
0
  }
250
251
0
  CHECKED_STATUS WaitTabletsCleaned(CoarseTimePoint deadline) {
252
0
    return Wait([this, deadline]() -> Result<bool> {
253
0
      size_t alive_tablets = 0;
254
0
      for (size_t i = 0; i != cluster_->num_tablet_servers(); ++i) {
255
0
        auto proxy = cluster_->GetTServerProxy<tserver::TabletServerServiceProxy>(i);
256
0
        tserver::ListTabletsRequestPB req;
257
0
        tserver::ListTabletsResponsePB resp;
258
0
        rpc::RpcController controller;
259
0
        controller.set_deadline(deadline);
260
0
        RETURN_NOT_OK(proxy.ListTablets(req, &resp, &controller));
261
0
        for (const auto& tablet : resp.status_and_schema()) {
262
0
          if (tablet.tablet_status().table_type() != TableType::TRANSACTION_STATUS_TABLE_TYPE) {
263
0
            LOG(INFO) << "Not yet deleted tablet: " << tablet.ShortDebugString();
264
0
            ++alive_tablets;
265
0
          }
266
0
        }
267
0
      }
268
0
      LOG(INFO) << "Alive tablets: " << alive_tablets;
269
0
      return alive_tablets == 0;
270
0
    }, deadline, "Deleted tablet cleanup");
271
0
  }
272
273
  void TestUndeleteTable(bool restart_masters);
274
275
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
276
0
    options->bind_to_unique_loopback_addresses = true;
277
0
    options->use_same_ts_ports = true;
278
0
    options->num_masters = 3;
279
0
  }
280
281
  std::unique_ptr<CppCassandraDriver> cql_driver_;
282
};
283
284
class YbAdminSnapshotScheduleTestWithYsql : public YbAdminSnapshotScheduleTest {
285
 public:
286
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* opts) override {
287
0
    opts->enable_ysql = true;
288
0
    opts->extra_tserver_flags.emplace_back("--ysql_num_shards_per_tserver=1");
289
0
    opts->num_masters = 3;
290
0
  }
291
};
292
293
0
TEST_F(YbAdminSnapshotScheduleTest, BadArguments) {
294
0
  BuildAndStart();
295
296
0
  ASSERT_NOK(CreateSnapshotSchedule(
297
0
      6s, 10min, kTableName.namespace_name(), kTableName.table_name()));
298
0
}
299
300
0
TEST_F(YbAdminSnapshotScheduleTest, Basic) {
301
0
  BuildAndStart();
302
303
0
  std::string schedule_id = ASSERT_RESULT(CreateSnapshotSchedule(
304
0
      6s, 10min, kTableName.namespace_name()));
305
0
  std::this_thread::sleep_for(20s);
306
307
0
  Timestamp last_snapshot_time;
308
0
  ASSERT_OK(WaitFor([this, schedule_id, &last_snapshot_time]() -> Result<bool> {
309
0
    auto schedule = VERIFY_RESULT(GetSnapshotSchedule());
310
0
    auto received_schedule_id = VERIFY_RESULT(Get(schedule, "id")).get().GetString();
311
0
    SCHECK_EQ(schedule_id, received_schedule_id, IllegalState, "Wrong schedule id");
312
    // Check schedule options.
313
0
    auto& options = VERIFY_RESULT(Get(schedule, "options")).get();
314
0
    std::string filter = VERIFY_RESULT(
315
0
        Get(options, "filter")).get().GetString();
316
0
    SCHECK_EQ(filter, Format("ycql.$0", kTableName.namespace_name()),
317
0
              IllegalState, "Wrong filter");
318
0
    std::string interval = VERIFY_RESULT(
319
0
        Get(options, "interval")).get().GetString();
320
0
    SCHECK_EQ(interval, "0 min", IllegalState, "Wrong interval");
321
0
    std::string retention = VERIFY_RESULT(
322
0
        Get(options, "retention")).get().GetString();
323
0
    SCHECK_EQ(retention, "10 min", IllegalState, "Wrong retention");
324
    // Check actual snapshots.
325
0
    const auto& snapshots = VERIFY_RESULT(Get(schedule, "snapshots")).get().GetArray();
326
0
    if (snapshots.Size() < 2) {
327
0
      return false;
328
0
    }
329
0
    std::string last_snapshot_time_str;
330
0
    for (const auto& snapshot : snapshots) {
331
0
      std::string snapshot_time = VERIFY_RESULT(
332
0
          Get(snapshot, "snapshot_time")).get().GetString();
333
0
      if (!last_snapshot_time_str.empty()) {
334
0
        std::string previous_snapshot_time = VERIFY_RESULT(
335
0
            Get(snapshot, "previous_snapshot_time")).get().GetString();
336
0
        SCHECK_EQ(previous_snapshot_time, last_snapshot_time_str, IllegalState,
337
0
                  "Wrong previous_snapshot_hybrid_time");
338
0
      }
339
0
      last_snapshot_time_str = snapshot_time;
340
0
    }
341
0
    LOG(INFO) << "Last snapshot time: " << last_snapshot_time_str;
342
0
    last_snapshot_time = VERIFY_RESULT(DateTime::TimestampFromString(last_snapshot_time_str));
343
0
    return true;
344
0
  }, 20s, "At least 2 snapshots"));
345
346
0
  last_snapshot_time.set_value(last_snapshot_time.value() + 1);
347
0
  LOG(INFO) << "Restore at: " << last_snapshot_time.ToFormattedString();
348
349
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, last_snapshot_time));
350
0
}
351
352
0
TEST_F(YbAdminSnapshotScheduleTest, Delete) {
353
0
  auto schedule_id = ASSERT_RESULT(PrepareQl(kRetention, kRetention));
354
355
0
  auto session = client_->NewSession();
356
0
  LOG(INFO) << "Create table";
357
0
  ASSERT_NO_FATALS(client::kv_table_test::CreateTable(
358
0
      client::Transactional::kTrue, 3, client_.get(), &table_));
359
360
0
  LOG(INFO) << "Write values";
361
0
  const auto kKeys = Range(100);
362
0
  for (auto i : kKeys) {
363
0
    ASSERT_OK(client::kv_table_test::WriteRow(&table_, session, i, -i));
364
0
  }
365
366
0
  for (auto i : kKeys) {
367
0
    ASSERT_OK(client::kv_table_test::DeleteRow(&table_, session, i));
368
0
  }
369
370
0
  ASSERT_OK(DeleteSnapshotSchedule(schedule_id));
371
372
0
  ASSERT_OK(WaitFor([this, schedule_id]() -> Result<bool> {
373
0
    auto schedule = GetSnapshotSchedule();
374
0
    if (!schedule.ok()) {
375
0
      if (schedule.status().IsNotFound()) {
376
0
        return true;
377
0
      }
378
0
      return schedule.status();
379
0
    }
380
381
0
    auto& options = VERIFY_RESULT(Get(*schedule, "options")).get();
382
0
    auto delete_time = VERIFY_RESULT(Get(options, "delete_time")).get().GetString();
383
0
    LOG(INFO) << "Delete time: " << delete_time;
384
0
    return false;
385
0
  }, 10s * kTimeMultiplier, "Snapshot schedule cleaned up"));
386
387
0
  ASSERT_OK(WaitFor(
388
0
      [this]() -> Result<bool> {
389
0
        auto snapshots_json = VERIFY_RESULT(ListSnapshots());
390
0
        auto snapshots = snapshots_json.GetArray();
391
0
        LOG(INFO) << "Snapshots left: " << snapshots.Size();
392
0
        return snapshots.Empty();
393
0
      },
394
0
      10s * kTimeMultiplier, "Snapshots cleaned up"));
395
396
0
  ASSERT_OK(WaitFor([this]() -> Result<bool> {
397
0
    for (auto* tserver : cluster_->tserver_daemons()) {
398
0
      auto proxy = cluster_->GetProxy<tserver::TabletServerAdminServiceProxy>(tserver);
399
0
      tserver::FlushTabletsRequestPB req;
400
0
      req.set_dest_uuid(tserver->uuid());
401
0
      req.set_all_tablets(true);
402
0
      tserver::FlushTabletsResponsePB resp;
403
0
      rpc::RpcController controller;
404
0
      controller.set_timeout(30s);
405
0
      RETURN_NOT_OK(proxy.FlushTablets(req, &resp, &controller));
406
407
0
      req.set_operation(tserver::FlushTabletsRequestPB::COMPACT);
408
0
      controller.Reset();
409
0
      RETURN_NOT_OK(proxy.FlushTablets(req, &resp, &controller));
410
0
    }
411
412
0
    for (auto* tserver : cluster_->tserver_daemons()) {
413
0
      auto proxy = cluster_->GetProxy<tserver::TabletServerServiceProxy>(tserver);
414
0
      tserver::ListTabletsRequestPB req;
415
0
      tserver::ListTabletsResponsePB resp;
416
417
0
      rpc::RpcController controller;
418
0
      controller.set_timeout(30s);
419
420
0
      RETURN_NOT_OK(proxy.ListTablets(req, &resp, &controller));
421
422
0
      for (const auto& tablet : resp.status_and_schema()) {
423
0
        if (tablet.tablet_status().table_type() == TableType::TRANSACTION_STATUS_TABLE_TYPE) {
424
0
          continue;
425
0
        }
426
0
        if (tablet.tablet_status().sst_files_disk_size() != 0) {
427
0
          LOG(INFO) << "Tablet status: " << tablet.tablet_status().ShortDebugString();
428
0
          return false;
429
0
        }
430
0
      }
431
0
    }
432
0
    return true;
433
0
  }, 1s * kHistoryRetentionIntervalSec * kTimeMultiplier, "Compact SST files"));
434
0
}
435
436
0
void YbAdminSnapshotScheduleTest::TestUndeleteTable(bool restart_masters) {
437
0
  auto schedule_id = ASSERT_RESULT(PrepareQl());
438
439
0
  auto session = client_->NewSession();
440
0
  LOG(INFO) << "Create table";
441
0
  ASSERT_NO_FATALS(client::kv_table_test::CreateTable(
442
0
      client::Transactional::kTrue, 3, client_.get(), &table_));
443
444
0
  LOG(INFO) << "Write values";
445
0
  constexpr int kMinKey = 1;
446
0
  constexpr int kMaxKey = 100;
447
0
  for (int i = kMinKey; i <= kMaxKey; ++i) {
448
0
    ASSERT_OK(client::kv_table_test::WriteRow(&table_, session, i, -i));
449
0
  }
450
451
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
452
453
0
  LOG(INFO) << "Delete table";
454
0
  ASSERT_OK(client_->DeleteTable(client::kTableName));
455
456
0
  ASSERT_NOK(client::kv_table_test::WriteRow(&table_, session, kMinKey, 0));
457
458
0
  ASSERT_NO_FATALS(client::kv_table_test::CreateTable(
459
0
      client::Transactional::kTrue, 3, client_.get(), &table_));
460
461
0
  ASSERT_OK(client::kv_table_test::WriteRow(&table_, session, kMinKey, 0));
462
463
0
  if (restart_masters) {
464
0
    ASSERT_OK(RestartAllMasters(cluster_.get()));
465
0
  }
466
467
0
  LOG(INFO) << "Restore schedule";
468
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
469
470
0
  ASSERT_OK(table_.Open(client::kTableName, client_.get()));
471
472
0
  LOG(INFO) << "Reading rows";
473
0
  auto rows = ASSERT_RESULT(client::kv_table_test::SelectAllRows(&table_, session));
474
0
  LOG(INFO) << "Rows: " << AsString(rows);
475
0
  ASSERT_EQ(rows.size(), kMaxKey - kMinKey + 1);
476
0
  for (int i = kMinKey; i <= kMaxKey; ++i) {
477
0
    ASSERT_EQ(rows[i], -i);
478
0
  }
479
480
0
  constexpr int kExtraKey = kMaxKey + 1;
481
0
  ASSERT_OK(client::kv_table_test::WriteRow(&table_, session, kExtraKey, -kExtraKey));
482
0
  auto extra_value = ASSERT_RESULT(client::kv_table_test::SelectRow(&table_, session, kExtraKey));
483
0
  ASSERT_EQ(extra_value, -kExtraKey);
484
0
}
485
486
0
TEST_F(YbAdminSnapshotScheduleTest, UndeleteTable) {
487
0
  TestUndeleteTable(false);
488
0
}
489
490
0
TEST_F(YbAdminSnapshotScheduleTest, UndeleteTableWithRestart) {
491
0
  TestUndeleteTable(true);
492
0
}
493
494
0
TEST_F(YbAdminSnapshotScheduleTest, CleanupDeletedTablets) {
495
0
  auto schedule_id = ASSERT_RESULT(PrepareQl(kInterval, kInterval));
496
497
0
  auto session = client_->NewSession();
498
0
  LOG(INFO) << "Create table";
499
0
  ASSERT_NO_FATALS(client::kv_table_test::CreateTable(
500
0
      client::Transactional::kTrue, 3, client_.get(), &table_));
501
502
0
  LOG(INFO) << "Write values";
503
0
  constexpr int kMinKey = 1;
504
0
  constexpr int kMaxKey = 100;
505
0
  for (int i = kMinKey; i <= kMaxKey; ++i) {
506
0
    ASSERT_OK(client::kv_table_test::WriteRow(&table_, session, i, -i));
507
0
  }
508
509
0
  LOG(INFO) << "Delete table";
510
0
  ASSERT_OK(client_->DeleteTable(client::kTableName));
511
512
0
  auto deadline = CoarseMonoClock::now() + kInterval + 10s;
513
514
  // Wait tablets deleted from tservers.
515
0
  ASSERT_OK(WaitTabletsCleaned(deadline));
516
517
  // Wait table marked as deleted.
518
0
  ASSERT_OK(Wait([this, deadline]() -> Result<bool> {
519
0
    auto proxy = cluster_->GetLeaderMasterProxy<master::MasterDdlProxy>();
520
0
    master::ListTablesRequestPB req;
521
0
    master::ListTablesResponsePB resp;
522
0
    rpc::RpcController controller;
523
0
    controller.set_deadline(deadline);
524
0
    req.set_include_not_running(true);
525
0
    RETURN_NOT_OK(proxy.ListTables(req, &resp, &controller));
526
0
    for (const auto& table : resp.tables()) {
527
0
      if (table.table_type() != TableType::TRANSACTION_STATUS_TABLE_TYPE
528
0
          && table.relation_type() != master::RelationType::SYSTEM_TABLE_RELATION
529
0
          && table.state() != master::SysTablesEntryPB::DELETED) {
530
0
        LOG(INFO) << "Not yet deleted table: " << table.ShortDebugString();
531
0
        return false;
532
0
      }
533
0
    }
534
0
    return true;
535
0
  }, deadline, "Deleted table cleanup"));
536
0
}
537
538
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(Pgsql),
539
0
          YbAdminSnapshotScheduleTestWithYsql) {
540
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
541
542
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
543
544
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
545
546
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
547
548
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
549
550
0
  ASSERT_OK(conn.Execute("UPDATE test_table SET value = 'after'"));
551
552
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
553
554
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
555
556
0
  ASSERT_EQ(res, "before");
557
0
}
558
559
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlCreateTable),
560
0
          YbAdminSnapshotScheduleTestWithYsql) {
561
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
562
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
563
564
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
565
566
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
567
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
568
569
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
570
0
  ASSERT_OK(restore_status);
571
572
  // Wait for Restore to complete.
573
0
  ASSERT_OK(WaitFor([this]() -> Result<bool> {
574
0
    bool all_tablets_hidden = true;
575
0
    for (size_t i = 0; i < cluster_->num_tablet_servers(); i++) {
576
0
      auto proxy = cluster_->GetTServerProxy<tserver::TabletServerServiceProxy>(i);
577
0
      tserver::ListTabletsRequestPB req;
578
0
      tserver::ListTabletsResponsePB resp;
579
0
      rpc::RpcController controller;
580
0
      controller.set_timeout(30s);
581
0
      RETURN_NOT_OK(proxy.ListTablets(req, &resp, &controller));
582
0
      for (const auto& tablet : resp.status_and_schema()) {
583
0
        if (tablet.tablet_status().namespace_name() == client::kTableName.namespace_name()) {
584
0
          LOG(INFO) << "Tablet " << tablet.tablet_status().tablet_id() << " of table "
585
0
                    << tablet.tablet_status().table_name() << ", hidden status "
586
0
                    << tablet.tablet_status().is_hidden();
587
0
          all_tablets_hidden = all_tablets_hidden && tablet.tablet_status().is_hidden();
588
0
        }
589
0
      }
590
0
    }
591
0
    return all_tablets_hidden;
592
0
  }, 30s, "Restore failed."));
593
594
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (2, 'now')"));
595
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
596
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'after')"));
597
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>(
598
0
      "SELECT value FROM test_table WHERE key = 1"));
599
0
  ASSERT_EQ(res, "after");
600
0
}
601
602
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlCreateIndex),
603
0
          YbAdminSnapshotScheduleTestWithYsql) {
604
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
605
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
606
607
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
608
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
609
610
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
611
612
0
  ASSERT_OK(conn.Execute("CREATE INDEX test_table_idx ON test_table (value)"));
613
614
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
615
0
  ASSERT_OK(restore_status);
616
617
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
618
0
  ASSERT_EQ(res, "before");
619
0
  ASSERT_OK(conn.Execute("CREATE INDEX test_table_idx ON test_table (value)"));
620
0
  ASSERT_OK(conn.Execute("UPDATE test_table SET value = 'after'"));
621
0
  res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
622
0
  ASSERT_EQ(res, "after");
623
0
}
624
625
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlDropTable),
626
0
          YbAdminSnapshotScheduleTestWithYsql) {
627
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
628
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
629
630
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
631
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
632
633
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
634
0
  ASSERT_OK(conn.Execute("DROP TABLE test_table"));
635
636
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
637
0
  ASSERT_OK(restore_status);
638
639
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>(
640
0
      "SELECT value FROM test_table WHERE key = 1"));
641
0
  ASSERT_EQ(res, "before");
642
0
  ASSERT_OK(conn.Execute("UPDATE test_table SET value = 'after'"));
643
0
  res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table WHERE key = 1"));
644
0
  ASSERT_EQ(res, "after");
645
0
}
646
647
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlDropIndex),
648
0
          YbAdminSnapshotScheduleTestWithYsql) {
649
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
650
651
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
652
653
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
654
0
  ASSERT_OK(conn.Execute("CREATE INDEX test_table_idx ON test_table (value)"));
655
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
656
657
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
658
659
0
  ASSERT_OK(conn.Execute("DROP INDEX test_table_idx"));
660
661
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
662
0
  ASSERT_OK(restore_status);
663
664
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
665
0
  ASSERT_EQ(res, "before");
666
0
  ASSERT_NOK(conn.Execute("CREATE INDEX test_table_idx ON test_table (value)"));
667
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, 'after')"));
668
0
  res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table WHERE key = 2"));
669
0
  ASSERT_EQ(res, "after");
670
0
}
671
672
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlAddColumn),
673
0
          YbAdminSnapshotScheduleTestWithYsql) {
674
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
675
676
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
677
678
0
  LOG(INFO) << "Create table 'test_table' and insert a row";
679
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
680
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
681
682
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
683
0
  LOG(INFO) << "Time to restore back: " << time;
684
0
  LOG(INFO) << "Alter table test_table -> Add 'value2' column";
685
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ADD COLUMN value2 TEXT"));
686
0
  ASSERT_OK(conn.Execute("UPDATE test_table SET value = 'now'"));
687
0
  ASSERT_OK(conn.Execute("UPDATE test_table SET value2 = 'now2'"));
688
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value2 FROM test_table"));
689
0
  ASSERT_EQ(res, "now2");
690
691
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
692
0
  ASSERT_OK(restore_status);
693
0
  LOG(INFO) << "Select data from table after restore";
694
0
  res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
695
0
  ASSERT_EQ(res, "before");
696
0
  LOG(INFO) << "Insert data to the table after restore";
697
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, 'one more')"));
698
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (3, 'again one more', 'new_value')"));
699
0
  auto result_status = conn.FetchValue<std::string>("SELECT value2 FROM test_table");
700
0
  ASSERT_EQ(result_status.ok(), false);
701
0
}
702
703
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlDeleteColumn),
704
0
          YbAdminSnapshotScheduleTestWithYsql) {
705
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
706
707
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
708
0
  LOG(INFO) << "Create table 'test_table' and insert a row";
709
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
710
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
711
712
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
713
0
  LOG(INFO) << "Time to restore back: " << time;
714
0
  LOG(INFO) << "Alter table 'test_table' -> Drop 'value' column";
715
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table DROP COLUMN value"));
716
0
  auto drop_result_status = conn.FetchValue<std::string>("SELECT value FROM test_table");
717
0
  ASSERT_EQ(drop_result_status.ok(), false);
718
0
  LOG(INFO) << "Reading Rows";
719
0
  auto select_res = ASSERT_RESULT(conn.FetchValue<int>("SELECT * FROM test_table"));
720
0
  LOG(INFO) << "Read result: " << select_res;
721
0
  ASSERT_EQ(select_res, 1);
722
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (2, 'new_value')"));
723
724
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
725
0
  ASSERT_OK(restore_status);
726
0
  LOG(INFO) << "Select data from table after restore";
727
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
728
0
  ASSERT_EQ(res, "before");
729
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, 'next value')"));
730
0
}
731
732
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlRenameTable),
733
0
          YbAdminSnapshotScheduleTestWithYsql) {
734
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
735
736
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
737
0
  LOG(INFO) << "Create table 'test_table' and insert a row";
738
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
739
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
740
741
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
742
0
  LOG(INFO) << "Time to restore back: " << time;
743
0
  LOG(INFO) << "Alter table 'test_table' -> Rename table to 'new_table'";
744
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table RENAME TO new_table"));
745
0
  auto renamed_result = conn.FetchValue<std::string>("SELECT value FROM new_table");
746
0
  ASSERT_EQ(renamed_result.ok(), true);
747
0
  LOG(INFO) << "Reading Rows";
748
0
  auto select_result = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM new_table"));
749
0
  LOG(INFO) << "Read result: " << select_result;
750
0
  ASSERT_EQ(select_result, "before");
751
0
  auto result_with_old_name = conn.FetchValue<std::string>("SELECT value FROM test_table");
752
0
  ASSERT_EQ(result_with_old_name.ok(), false);
753
754
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
755
0
  ASSERT_OK(restore_status);
756
0
  LOG(INFO) << "Select data from table after restore";
757
0
  auto renamed_result_after_restore = conn.FetchValue<std::string>("SELECT value FROM new_table");
758
0
  ASSERT_EQ(renamed_result_after_restore.ok(), false);
759
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
760
0
  ASSERT_EQ(res, "before");
761
0
  LOG(INFO) << "Insert data to table after restore";
762
0
  ASSERT_NOK(conn.Execute("INSERT INTO new_table VALUES (2, 'new value')"));
763
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, 'new value')"));
764
0
}
765
766
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlRenameColumn),
767
0
          YbAdminSnapshotScheduleTestWithYsql) {
768
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
769
770
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
771
0
  LOG(INFO) << "Create table 'test_table' and insert a row";
772
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
773
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
774
775
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
776
0
  LOG(INFO) << "Time to restore back: " << time;
777
0
  LOG(INFO) << "Alter table 'test_table' -> Rename 'value' column to 'value2'";
778
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table RENAME COLUMN value TO value2"));
779
0
  auto result_with_old_name = conn.FetchValue<std::string>("SELECT value FROM test_table");
780
0
  ASSERT_EQ(result_with_old_name.ok(), false);
781
0
  auto renamed_result = conn.FetchValue<std::string>("SELECT value2 FROM test_table");
782
0
  ASSERT_EQ(renamed_result.ok(), true);
783
0
  LOG(INFO) << "Reading Rows";
784
0
  auto select_res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value2 FROM test_table"));
785
0
  LOG(INFO) << "Read result: " << select_res;
786
0
  ASSERT_EQ(select_res, "before");
787
788
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
789
0
  ASSERT_OK(restore_status);
790
0
  LOG(INFO) << "Select data from table after restore";
791
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
792
0
  ASSERT_EQ(res, "before");
793
0
  LOG(INFO) << "Insert data to table after restore";
794
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table(key, value2) VALUES (2, 'new_value')"));
795
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table(key, value) VALUES (2, 'new_value')"));
796
0
}
797
798
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlSetDefault),
799
0
          YbAdminSnapshotScheduleTestWithYsql) {
800
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
801
802
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
803
0
  LOG(INFO) << "Create table and insert a row";
804
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
805
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'before')"));
806
807
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
808
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
809
810
0
  LOG(INFO) << "Alter table and set a default value to the value column";
811
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ALTER COLUMN value SET DEFAULT 'default_value'"));
812
813
0
  LOG(INFO) << "Insert a row without providing a value for the default column";
814
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2)"));
815
816
0
  LOG(INFO) << "Fetch the row inserted above and verify default value is inserted correctly";
817
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table "
818
0
      "WHERE key=2"));
819
0
  ASSERT_EQ(res, "default_value");
820
821
0
  LOG(INFO) << "Perform a Restore to the time noted above";
822
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
823
0
  ASSERT_OK(restore_status);
824
825
0
  LOG(INFO) << "Insert a new row and verify that the default clause is no longer present";
826
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (3)"));
827
0
  res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table WHERE key=3"));
828
0
  ASSERT_EQ(res, "");
829
830
0
  LOG(INFO) << "Verify that the row with key=2 is no longer present after restore";
831
0
  auto result_status = conn.FetchValue<std::string>("SELECT * FROM test_table where key=2");
832
0
  ASSERT_EQ(result_status.ok(), false);
833
0
}
834
835
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlDropDefault),
836
0
          YbAdminSnapshotScheduleTestWithYsql) {
837
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
838
839
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
840
841
0
  LOG(INFO) << "Create table with default on the value column and insert a row";
842
0
  ASSERT_OK(conn.Execute(
843
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT default('default_value'))"));
844
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1)"));
845
846
0
  LOG(INFO) << "Verify default value is set correctly";
847
0
  auto res =
848
0
      ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table WHERE key=1"));
849
0
  ASSERT_EQ(res, "default_value");
850
851
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
852
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
853
854
0
  LOG(INFO) << "Alter table and drop the default value on the column value";
855
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ALTER COLUMN value DROP DEFAULT"));
856
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2)"));
857
858
0
  LOG(INFO) << "Verify default is dropped correctly";
859
0
  auto res2 =
860
0
      ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table where key=2"));
861
0
  ASSERT_EQ(res2, "");
862
863
0
  LOG(INFO) << "Perform a Restore to the time noted above";
864
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
865
0
  ASSERT_OK(restore_status);
866
867
0
  LOG(INFO) << "Insert a row and verify that the default clause is still present";
868
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (3)"));
869
0
  auto res3 =
870
0
      ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table where key=3"));
871
0
  ASSERT_EQ(res3, "default_value");
872
873
0
  LOG(INFO) << "Verify that the row with key=2 is no longer present after restore";
874
0
  auto result_status = conn.FetchValue<std::string>("SELECT * FROM test_table where key=2");
875
0
  ASSERT_EQ(result_status.ok(), false);
876
0
}
877
878
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlSetNotNull),
879
0
          YbAdminSnapshotScheduleTestWithYsql) {
880
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
881
882
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
883
884
0
  LOG(INFO) << "Create a table and insert data";
885
0
  ASSERT_OK(conn.Execute(
886
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
887
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'Before')"));
888
889
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
890
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
891
892
0
  LOG(INFO) << "Alter table and set not null";
893
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ALTER COLUMN value SET NOT NULL"));
894
895
0
  LOG(INFO) << "Insert null value in the not null column and assert failure";
896
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (2)"));
897
898
0
  LOG(INFO) << "Perform a Restore to the time noted above";
899
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
900
0
  ASSERT_OK(restore_status);
901
902
0
  LOG(INFO) << "Insert rows with null values and verify it goes through successfully";
903
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2)"));
904
0
  auto res3 =
905
0
      ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table where key=2"));
906
0
  ASSERT_EQ(res3, "");
907
0
}
908
909
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlDropNotNull),
910
0
          YbAdminSnapshotScheduleTestWithYsql) {
911
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
912
913
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
914
915
0
  LOG(INFO) << "Create a table with not null clause and insert data";
916
0
  ASSERT_OK(conn.Execute(
917
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT NOT NULL)"));
918
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'Before')"));
919
920
0
  LOG(INFO) << "Verify failure on null insertion";
921
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (2)"));
922
923
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
924
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
925
926
0
  LOG(INFO) << "Alter table and drop not null clause";
927
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ALTER COLUMN value DROP NOT NULL"));
928
929
0
  LOG(INFO) << "Insert null values and verify success";
930
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2)"));
931
932
0
  LOG(INFO) << "Perform a Restore to the time noted above";
933
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
934
0
  ASSERT_OK(restore_status);
935
936
0
  LOG(INFO) << "Verify failure on null insertion since the drop is restored via PITR";
937
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (3)"));
938
0
}
939
940
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlAlterTableAddPK),
941
0
          YbAdminSnapshotScheduleTestWithYsql) {
942
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
943
944
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
945
946
0
  LOG(INFO) << "Create a table and insert data";
947
0
  ASSERT_OK(conn.Execute(
948
0
      "CREATE TABLE test_table (key INT, value TEXT)"));
949
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'BeforePK')"));
950
951
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
952
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
953
954
0
  LOG(INFO) << "Alter table and add primary key constraint";
955
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ADD PRIMARY KEY (key)"));
956
957
0
  LOG(INFO) << "Verify Primary key constraint added";
958
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table(value) VALUES (1, 'AfterPK')"));
959
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table(value) VALUES ('DuringPK')"));
960
961
0
  LOG(INFO) << "Perform a Restore to the time noted above";
962
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
963
0
  ASSERT_OK(restore_status);
964
965
0
  LOG(INFO) << "Verify Primary key constraint no longer exists";
966
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table(value) VALUES ('AfterPITR')"));
967
968
0
  LOG(INFO) << "Insert a row with key=1 and verify that it succeeds.";
969
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'AfterPKRemoval')"));
970
0
}
971
972
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlAlterTableAddFK),
973
0
          YbAdminSnapshotScheduleTestWithYsql) {
974
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
975
976
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
977
978
0
  LOG(INFO) << "Create tables and insert data";
979
0
  ASSERT_OK(conn.Execute(
980
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
981
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'BeforeFK')"));
982
0
  ASSERT_OK(conn.Execute(
983
0
      "CREATE TABLE test_table_2 (key_id1 INT PRIMARY KEY, key_id2 INT)"));
984
985
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
986
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
987
988
0
  LOG(INFO) << "Alter table 2 and add Foreign key constraint";
989
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table_2 ADD CONSTRAINT fk2 "
990
0
      "FOREIGN KEY (key_id2) REFERENCES test_table(key)"));
991
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table_2 VALUES (1, 2)"));
992
993
0
  LOG(INFO) << "Perform a Restore to the time noted above";
994
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
995
0
  ASSERT_OK(restore_status);
996
997
0
  LOG(INFO) << "Verify Foreign key no longer exists post PITR";
998
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table_2 VALUES (1, 2)"));
999
0
  ASSERT_OK(conn.Execute("DROP TABLE test_table"));
1000
0
}
1001
1002
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlAlterTableSetOwner),
1003
0
          YbAdminSnapshotScheduleTestWithYsql) {
1004
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
1005
1006
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
1007
0
  LOG(INFO) << "Create user user1";
1008
0
  ASSERT_OK(conn.Execute("CREATE USER user1"));
1009
1010
0
  LOG(INFO) << "Create user user2";
1011
0
  ASSERT_OK(conn.Execute("CREATE USER user2"));
1012
1013
0
  LOG(INFO) << "Set Session authorization to user1";
1014
0
  ASSERT_OK(conn.Execute("SET SESSION AUTHORIZATION user1"));
1015
1016
0
  LOG(INFO) << "Create table with user1 as the owner";
1017
0
  ASSERT_OK(conn.Execute(
1018
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
1019
1020
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1021
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
1022
1023
0
  LOG(INFO) << "Set session authorization to super user";
1024
0
  ASSERT_OK(conn.Execute("SET SESSION AUTHORIZATION yugabyte"));
1025
1026
0
  LOG(INFO) << "Alter table and set owner of the table to user2";
1027
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table OWNER TO user2"));
1028
1029
0
  LOG(INFO) << "Set session authorization to user2";
1030
0
  ASSERT_OK(conn.Execute("SET SESSION AUTHORIZATION user2"));
1031
1032
0
  LOG(INFO) << "Verify user session is set correctly";
1033
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table RENAME key TO key_new"));
1034
1035
0
  LOG(INFO) << "Perform a Restore to the time noted above";
1036
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
1037
0
  ASSERT_OK(restore_status);
1038
1039
0
  LOG(INFO) << "Set session authorization to user2";
1040
0
  ASSERT_OK(conn.Execute("SET SESSION AUTHORIZATION user2"));
1041
1042
0
  LOG(INFO) << "Verify user2 is no longer the owner of the table post PITR and "
1043
0
               "is unable to perform writes on the table";
1044
0
  ASSERT_NOK(conn.Execute("ALTER TABLE test_table RENAME key TO key_new2"));
1045
1046
0
  LOG(INFO) << "Set session authorization to user1";
1047
0
  ASSERT_OK(conn.Execute("SET SESSION AUTHORIZATION user1"));
1048
1049
0
  LOG(INFO) << "Verify user1 is able to perform write operations on the table ";
1050
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table RENAME key TO key_new3"));
1051
0
}
1052
1053
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlAddUniqueConstraint),
1054
0
          YbAdminSnapshotScheduleTestWithYsql) {
1055
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
1056
1057
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
1058
1059
0
  LOG(INFO) << "Create table and insert data";
1060
0
  ASSERT_OK(conn.Execute(
1061
0
      "CREATE TABLE test_table (key INT, value TEXT)"));
1062
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'ABC')"));
1063
1064
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1065
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
1066
1067
0
  LOG(INFO) << "Alter table add unique constraint";
1068
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ADD CONSTRAINT uniquecst UNIQUE (value)"));
1069
1070
0
  LOG(INFO) << "Verify Unique constraint added";
1071
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (2, 'ABC')"));
1072
1073
0
  LOG(INFO) << "Perform a Restore to the time noted above";
1074
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
1075
0
  ASSERT_OK(restore_status);
1076
1077
0
  LOG(INFO) << "Verify unique constraint is no longer present";
1078
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, 'ABC')"));
1079
0
}
1080
1081
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlDropUniqueConstraint),
1082
0
          YbAdminSnapshotScheduleTestWithYsql) {
1083
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
1084
1085
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
1086
1087
0
  LOG(INFO) << "Create table and insert data";
1088
0
  ASSERT_OK(conn.Execute(
1089
0
      "CREATE TABLE test_table (key INT, value TEXT)"));
1090
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, 'ABC')"));
1091
1092
0
  LOG(INFO) << "Add unique constraint to the table";
1093
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ADD CONSTRAINT uniquecst UNIQUE (value)"));
1094
1095
0
  LOG(INFO) << "Verify unique constraint added";
1096
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (2, 'ABC')"));
1097
1098
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1099
0
  LOG(INFO) << "Time noted to to restore the database " << time;
1100
1101
0
  LOG(INFO) << "Drop Unique constraint";
1102
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table DROP CONSTRAINT uniquecst"));
1103
1104
0
  LOG(INFO) << "Verify unique constraint is dropped";
1105
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, 'ABC')"));
1106
1107
0
  LOG(INFO) << "Perform a Restore to the time noted above";
1108
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
1109
0
  ASSERT_OK(restore_status);
1110
1111
0
  LOG(INFO) << "Verify that the unique constraint is present and drop is restored";
1112
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (3, 'ABC')"));
1113
1114
0
  LOG(INFO) << "Verify that insertion of a row satisfying the unique constraint works";
1115
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (3, 'DEF')"));
1116
1117
0
  LOG(INFO) << "Verify that the row with key=2 is no longer present after restore";
1118
0
  auto result_status = conn.FetchValue<std::string>("SELECT * FROM test_table where key=2");
1119
0
  ASSERT_EQ(result_status.ok(), false);
1120
0
}
1121
1122
1123
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlAddCheckConstraint),
1124
0
          YbAdminSnapshotScheduleTestWithYsql) {
1125
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
1126
1127
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
1128
1129
0
  LOG(INFO) << "Create table and insert data";
1130
0
  ASSERT_OK(conn.Execute(
1131
0
      "CREATE TABLE test_table (key INT, value TEXT)"));
1132
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (150, 'ABC')"));
1133
1134
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1135
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
1136
1137
0
  LOG(INFO) << "Alter table and add check constraint";
1138
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table ADD CONSTRAINT check_1 CHECK (key > 100)"));
1139
1140
0
  LOG(INFO) << "Verify Check constraint added";
1141
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (2, 'XYZ')"));
1142
1143
0
  LOG(INFO) << "Perform a Restore to the time noted above";
1144
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
1145
0
  ASSERT_OK(restore_status);
1146
1147
0
  LOG(INFO) << "Verify check constraint is removed post PITR";
1148
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, 'PQR')"));
1149
0
}
1150
1151
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlDropCheckConstraint),
1152
0
          YbAdminSnapshotScheduleTestWithYsql) {
1153
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
1154
1155
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
1156
1157
0
  LOG(INFO) << "Create table and insert data";
1158
0
  ASSERT_OK(conn.Execute(
1159
0
      "CREATE TABLE test_table (key INT, value TEXT, CONSTRAINT con1 CHECK (key > 100))"));
1160
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (101, 'WithCon1')"));
1161
1162
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1163
0
  LOG(INFO) << "Time noted to to restore the database: " << time;
1164
1165
0
  LOG(INFO) << "Alter table and drop the check constraint";
1166
0
  ASSERT_OK(conn.Execute("ALTER TABLE test_table DROP CONSTRAINT con1"));
1167
1168
0
  LOG(INFO) << "Verify check constraint is dropped";
1169
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, 'Constraint_Dropped')"));
1170
1171
0
  LOG(INFO) << "Perform a Restore to the time noted above";
1172
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
1173
0
  ASSERT_OK(restore_status);
1174
1175
0
  LOG(INFO) << "Verify drop constraint is undone post PITR";
1176
0
  ASSERT_NOK(conn.Execute("INSERT INTO test_table VALUES (3, 'With_Constraint')"));
1177
1178
0
  LOG(INFO) << "Verify insertion of a row satisfying the constraint post restore";
1179
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (102, 'After_PITR')"));
1180
1181
0
  LOG(INFO) << "Verify that the row with key=2 is no longer present after restore";
1182
0
  auto result_status = conn.FetchValue<std::string>("SELECT * FROM test_table where key=2");
1183
0
  ASSERT_EQ(result_status.ok(), false);
1184
0
}
1185
1186
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlSequenceDelete),
1187
0
          YbAdminSnapshotScheduleTestWithYsql) {
1188
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
1189
1190
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
1191
0
  LOG(INFO) << "Create table 'test_table'";
1192
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value INT)"));
1193
0
  LOG(INFO) << "Create Sequence 'value_data'";
1194
0
  ASSERT_OK(conn.Execute("CREATE SEQUENCE value_data INCREMENT 5 OWNED BY test_table.value"));
1195
0
  LOG(INFO) << "Insert some rows to 'test_table'";
1196
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, nextval('value_data'))"));
1197
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, nextval('value_data'))"));
1198
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (3, nextval('value_data'))"));
1199
0
  LOG(INFO) << "Reading Rows";
1200
0
  auto res = ASSERT_RESULT(conn.FetchValue<int32_t>("SELECT value FROM test_table where key=3"));
1201
0
  LOG(INFO) << "Select result " << res;
1202
0
  ASSERT_EQ(res, 11);
1203
1204
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1205
1206
0
  LOG(INFO) << "Time to restore back " << time;
1207
0
  LOG(INFO) << "Deleting last row";
1208
0
  ASSERT_OK(conn.Execute("DELETE FROM test_table where key=3"));
1209
1210
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
1211
0
  ASSERT_OK(restore_status);
1212
1213
0
  LOG(INFO) << "Select data from 'test_table' after restore";
1214
0
  res = ASSERT_RESULT(conn.FetchValue<int32_t>("SELECT value FROM test_table where key=3"));
1215
0
  LOG(INFO) << "Select result " << res;
1216
0
  ASSERT_EQ(res, 11);
1217
0
  LOG(INFO) << "Insert a row into 'test_table' and validate";
1218
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (4, nextval('value_data'))"));
1219
0
  res = ASSERT_RESULT(conn.FetchValue<int32_t>("SELECT value FROM test_table where key=4"));
1220
0
  LOG(INFO) << "Select result " << res;
1221
0
  ASSERT_EQ(res, 16);
1222
0
}
1223
1224
TEST_F_EX(YbAdminSnapshotScheduleTest, YB_DISABLE_TEST_IN_TSAN(PgsqlSequenceInsert),
1225
0
          YbAdminSnapshotScheduleTestWithYsql) {
1226
0
  auto schedule_id = ASSERT_RESULT(PreparePg());
1227
1228
0
  auto conn = ASSERT_RESULT(PgConnect(client::kTableName.namespace_name()));
1229
0
  LOG(INFO) << "Create table 'test_table'";
1230
0
  ASSERT_OK(conn.Execute("CREATE TABLE test_table (key INT PRIMARY KEY, value INT)"));
1231
0
  LOG(INFO) << "Create Sequence 'value_data'";
1232
0
  ASSERT_OK(conn.Execute("CREATE SEQUENCE value_data INCREMENT 5 OWNED BY test_table.value"));
1233
0
  LOG(INFO) << "Insert some rows to 'test_table'";
1234
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (1, nextval('value_data'))"));
1235
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (2, nextval('value_data'))"));
1236
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (3, nextval('value_data'))"));
1237
0
  LOG(INFO) << "Reading Rows";
1238
0
  auto res = ASSERT_RESULT(conn.FetchValue<int32_t>("SELECT value FROM test_table where key=3"));
1239
0
  LOG(INFO) << "Select result " << res;
1240
0
  ASSERT_EQ(res, 11);
1241
1242
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1243
1244
0
  LOG(INFO) << "Time to restore back " << time;
1245
0
  LOG(INFO) << "Inserting new row in 'test_table'";
1246
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (4, nextval('value_data'))"));
1247
0
  LOG(INFO) << "Reading Rows from 'test_table'";
1248
0
  res = ASSERT_RESULT(conn.FetchValue<int32_t>("SELECT value FROM test_table where key=4"));
1249
0
  LOG(INFO) << "Select result " << res;
1250
0
  ASSERT_EQ(res, 16);
1251
1252
0
  auto restore_status = RestoreSnapshotSchedule(schedule_id, time);
1253
0
  ASSERT_OK(restore_status);
1254
1255
0
  LOG(INFO) << "Select row from 'test_table' after restore";
1256
0
  auto result_status = conn.FetchValue<int32_t>("SELECT value FROM test_table where key=4");
1257
0
  ASSERT_EQ(result_status.ok(), false);
1258
1259
0
  res = ASSERT_RESULT(conn.FetchValue<int32_t>("SELECT value FROM test_table where key=3"));
1260
0
  LOG(INFO) << "Select result " << res;
1261
0
  ASSERT_EQ(res, 11);
1262
0
  LOG(INFO) << "Insert a row into 'test_table' and validate";
1263
0
  ASSERT_OK(conn.Execute("INSERT INTO test_table VALUES (4, nextval('value_data'))"));
1264
  // Here value should be 21 instead of 16 as previous insert has value 16
1265
0
  res = ASSERT_RESULT(conn.FetchValue<int32_t>("SELECT value FROM test_table where key=4"));
1266
0
  LOG(INFO) << "Select result " << res;
1267
0
  ASSERT_EQ(res, 21);
1268
0
}
1269
1270
0
TEST_F(YbAdminSnapshotScheduleTest, UndeleteIndex) {
1271
0
  auto schedule_id = ASSERT_RESULT(PrepareCql());
1272
1273
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1274
1275
0
  ASSERT_OK(conn.ExecuteQuery(
1276
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT) "
1277
0
      "WITH transactions = { 'enabled' : true }"));
1278
0
  ASSERT_OK(conn.ExecuteQuery("CREATE UNIQUE INDEX test_table_idx ON test_table (value)"));
1279
1280
0
  ASSERT_OK(conn.ExecuteQuery("INSERT INTO test_table (key, value) VALUES (1, 'value')"));
1281
1282
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1283
1284
0
  ASSERT_OK(conn.ExecuteQuery("DROP INDEX test_table_idx"));
1285
1286
0
  ASSERT_OK(conn.ExecuteQuery("INSERT INTO test_table (key, value) VALUES (3, 'value')"));
1287
1288
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1289
1290
0
  ASSERT_NOK(conn.ExecuteQuery("INSERT INTO test_table (key, value) VALUES (5, 'value')"));
1291
1292
0
  auto res = ASSERT_RESULT(conn.FetchValue<int32_t>(
1293
0
      "SELECT key FROM test_table WHERE value = 'value'"));
1294
1295
0
  ASSERT_EQ(res, 1);
1296
0
}
1297
1298
// This test is for schema version patching after restore.
1299
// Consider the following scenario, w/o patching:
1300
//
1301
// 1) Create table.
1302
// 2) Add text column to table. Schema version - 1.
1303
// 3) Insert values into table. Each CQL proxy suppose schema version 1 for this table.
1304
// 4) Restore to time between (1) and (2). Schema version - 0.
1305
// 5) Add int column to table. Schema version - 1.
1306
// 6) Try insert values with wrong type into table.
1307
//
1308
// So table has schema version 1, but new column is INT.
1309
// CQL proxy suppose schema version is also 1, but the last column is TEXT.
1310
0
TEST_F(YbAdminSnapshotScheduleTest, AlterTable) {
1311
0
  const auto kKeys = Range(10);
1312
1313
0
  auto schedule_id = ASSERT_RESULT(PrepareCql());
1314
1315
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1316
1317
0
  ASSERT_OK(conn.ExecuteQuery(
1318
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
1319
1320
0
  for (auto key : kKeys) {
1321
0
    ASSERT_OK(conn.ExecuteQuery(Format(
1322
0
        "INSERT INTO test_table (key, value) VALUES ($0, 'A')", key)));
1323
0
  }
1324
1325
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1326
1327
0
  ASSERT_OK(conn.ExecuteQuery(
1328
0
      "ALTER TABLE test_table ADD value2 TEXT"));
1329
1330
0
  for (auto key : kKeys) {
1331
0
    ASSERT_OK(conn.ExecuteQuery(Format(
1332
0
        "INSERT INTO test_table (key, value, value2) VALUES ($0, 'B', 'X')", key)));
1333
0
  }
1334
1335
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1336
1337
0
  ASSERT_OK(conn.ExecuteQuery(
1338
0
      "ALTER TABLE test_table ADD value2 INT"));
1339
1340
0
  for (auto key : kKeys) {
1341
    // It would succeed on some TServers if we would not refresh metadata after restore.
1342
    // But it should not succeed because of last column type.
1343
0
    ASSERT_NOK(conn.ExecuteQuery(Format(
1344
0
        "INSERT INTO test_table (key, value, value2) VALUES ($0, 'D', 'Y')", key)));
1345
0
  }
1346
0
}
1347
1348
0
TEST_F(YbAdminSnapshotScheduleTest, TestVerifyRestorationLogic) {
1349
0
  const auto kKeys = Range(10);
1350
1351
0
  auto schedule_id = ASSERT_RESULT(PrepareCql());
1352
1353
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1354
1355
0
  ASSERT_OK(conn.ExecuteQuery(
1356
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT)"));
1357
1358
0
  for (auto key : kKeys) {
1359
0
    ASSERT_OK(conn.ExecuteQuery(Format(
1360
0
        "INSERT INTO test_table (key, value) VALUES ($0, 'A')", key)));
1361
0
  }
1362
1363
0
  ASSERT_OK(conn.ExecuteQuery("DROP TABLE test_table"));
1364
1365
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1366
1367
0
  ASSERT_OK(conn.ExecuteQuery(
1368
0
      "CREATE TABLE test_table1 (key INT PRIMARY KEY)"));
1369
1370
0
  for (auto key : kKeys) {
1371
0
    ASSERT_OK(conn.ExecuteQuery(Format(
1372
0
        "INSERT INTO test_table1 (key) VALUES ($0)", key)));
1373
0
  }
1374
1375
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1376
1377
  // Reconnect because of caching issues with YCQL.
1378
0
  conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1379
1380
0
  ASSERT_NOK(conn.ExecuteQuery("SELECT * from test_table"));
1381
1382
0
  ASSERT_OK(WaitFor([&conn]() -> Result<bool> {
1383
0
    auto res = conn.ExecuteQuery("SELECT * from test_table1");
1384
0
    if (res.ok()) {
1385
0
      return false;
1386
0
    }
1387
0
    return true;
1388
0
  }, 30s * kTimeMultiplier, "Wait for table to be deleted"));
1389
0
}
1390
1391
0
TEST_F(YbAdminSnapshotScheduleTest, TestGCHiddenTables) {
1392
0
  const auto interval = 15s;
1393
0
  const auto retention = 30s * kTimeMultiplier;
1394
0
  auto schedule_id = ASSERT_RESULT(PrepareQl(interval, retention));
1395
1396
0
  auto session = client_->NewSession();
1397
0
  LOG(INFO) << "Create table";
1398
0
  ASSERT_NO_FATALS(client::kv_table_test::CreateTable(
1399
0
      client::Transactional::kTrue, 3, client_.get(), &table_));
1400
1401
0
  LOG(INFO) << "Write values";
1402
0
  constexpr int kMinKey = 1;
1403
0
  constexpr int kMaxKey = 100;
1404
0
  for (int i = kMinKey; i <= kMaxKey; ++i) {
1405
0
    ASSERT_OK(client::kv_table_test::WriteRow(&table_, session, i, -i));
1406
0
  }
1407
1408
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1409
1410
0
  LOG(INFO) << "Delete table";
1411
0
  ASSERT_OK(client_->DeleteTable(client::kTableName));
1412
1413
0
  ASSERT_NOK(client::kv_table_test::WriteRow(&table_, session, kMinKey, 0));
1414
1415
0
  LOG(INFO) << "Restore schedule";
1416
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1417
1418
0
  ASSERT_OK(table_.Open(client::kTableName, client_.get()));
1419
1420
0
  LOG(INFO) << "Reading rows";
1421
0
  auto rows = ASSERT_RESULT(client::kv_table_test::SelectAllRows(&table_, session));
1422
0
  LOG(INFO) << "Rows: " << AsString(rows);
1423
0
  ASSERT_EQ(rows.size(), kMaxKey - kMinKey + 1);
1424
0
  for (int i = kMinKey; i <= kMaxKey; ++i) {
1425
0
    ASSERT_EQ(rows[i], -i);
1426
0
  }
1427
1428
  // Wait for snapshot schedule retention time and verify that GC
1429
  // for the table isn't initiated.
1430
0
  SleepFor(2*retention);
1431
1432
0
  Timestamp time1(ASSERT_RESULT(WallClock()->Now()).time_point);
1433
1434
0
  LOG(INFO) << "Delete table again.";
1435
0
  ASSERT_OK(client_->DeleteTable(client::kTableName));
1436
1437
0
  ASSERT_NOK(client::kv_table_test::WriteRow(&table_, session, kMinKey, 0));
1438
1439
0
  LOG(INFO) << "Restore schedule again.";
1440
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time1));
1441
1442
0
  ASSERT_OK(table_.Open(client::kTableName, client_.get()));
1443
1444
0
  LOG(INFO) << "Reading rows again.";
1445
0
  rows = ASSERT_RESULT(client::kv_table_test::SelectAllRows(&table_, session));
1446
0
  LOG(INFO) << "Rows: " << AsString(rows);
1447
0
  ASSERT_EQ(rows.size(), kMaxKey - kMinKey + 1);
1448
0
  for (int i = kMinKey; i <= kMaxKey; ++i) {
1449
0
    ASSERT_EQ(rows[i], -i);
1450
0
  }
1451
1452
  // Write and verify the extra row.
1453
0
  constexpr int kExtraKey = kMaxKey + 1;
1454
0
  ASSERT_OK(client::kv_table_test::WriteRow(&table_, session, kExtraKey, -kExtraKey));
1455
0
  auto extra_value = ASSERT_RESULT(client::kv_table_test::SelectRow(&table_, session, kExtraKey));
1456
0
  ASSERT_EQ(extra_value, -kExtraKey);
1457
0
}
1458
1459
class YbAdminSnapshotConsistentRestoreTest : public YbAdminSnapshotScheduleTest {
1460
 public:
1461
0
  virtual std::vector<std::string> ExtraTSFlags() {
1462
0
    return { "--consistent_restore=true", "--TEST_tablet_delay_restore_ms=0" };
1463
0
  }
1464
};
1465
1466
0
CHECKED_STATUS WaitWrites(int num, std::atomic<int>* current) {
1467
0
  auto stop = current->load() + num;
1468
0
  return WaitFor([current, stop] { return current->load() >= stop; },
1469
0
                 20s, Format("Wait $0 ($1) writes", stop, num));
1470
0
}
1471
1472
YB_DEFINE_ENUM(KeyState, (kNone)(kMissing)(kBeforeMissing)(kAfterMissing));
1473
1474
// Check that restoration is consistent across tablets.
1475
// Concurrently write keys and store event stream, i.e when we started or finished to write key.
1476
// After restore we fetch all keys and see what keys were removed during restore.
1477
// So for each such key we could find what keys were written strictly before it,
1478
// i.e. before restore. Or strictly after, i.e. after restore.
1479
// Then we check that key is not marked as before and after restore simultaneously.
1480
0
TEST_F_EX(YbAdminSnapshotScheduleTest, ConsistentRestore, YbAdminSnapshotConsistentRestoreTest) {
1481
0
  auto schedule_id = ASSERT_RESULT(PrepareCql());
1482
1483
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1484
1485
0
  ASSERT_OK(conn.ExecuteQuery("CREATE TABLE test_table (k1 INT PRIMARY KEY)"));
1486
1487
0
  struct EventData {
1488
0
    int key;
1489
0
    bool finished;
1490
0
  };
1491
1492
0
  std::atomic<int> written{0};
1493
0
  TestThreadHolder thread_holder;
1494
1495
0
  std::vector<EventData> events;
1496
1497
0
  thread_holder.AddThreadFunctor([&conn, &written, &events, &stop = thread_holder.stop_flag()] {
1498
0
    auto prepared = ASSERT_RESULT(conn.Prepare("INSERT INTO test_table (k1) VALUES (?)"));
1499
0
    std::vector<std::pair<int, CassandraFuture>> futures;
1500
0
    int key = 0;
1501
0
    constexpr int kBlock = 10;
1502
0
    while (!stop.load() || !futures.empty()) {
1503
0
      auto filter = [&events, &written](auto& key_and_future) {
1504
0
        if (!key_and_future.second.Ready()) {
1505
0
          return false;
1506
0
        }
1507
0
        auto write_status = key_and_future.second.Wait();
1508
0
        if (write_status.ok()) {
1509
0
          events.push_back(EventData{.key = key_and_future.first, .finished = true});
1510
0
          ++written;
1511
0
        } else {
1512
0
          LOG(WARNING) << "Write failed: " << write_status;
1513
0
        }
1514
0
        return true;
1515
0
      };
1516
0
      ASSERT_NO_FATALS(EraseIf(filter, &futures));
1517
0
      if (futures.size() < kBlock && !stop.load()) {
1518
0
        auto write_key = ++key;
1519
0
        auto stmt = prepared.Bind();
1520
0
        stmt.Bind(0, write_key);
1521
0
        futures.emplace_back(write_key, conn.ExecuteGetFuture(stmt));
1522
0
        events.push_back(EventData{.key = write_key, .finished = false});
1523
0
      }
1524
0
      std::this_thread::sleep_for(10ms);
1525
0
    }
1526
0
  });
1527
1528
0
  {
1529
0
    auto se = ScopeExit([&thread_holder] {
1530
0
      thread_holder.Stop();
1531
0
    });
1532
0
    ASSERT_OK(WaitWrites(50, &written));
1533
1534
0
    Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1535
1536
0
    ASSERT_OK(WaitWrites(10, &written));
1537
1538
0
    ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1539
1540
0
    ASSERT_OK(WaitWrites(10, &written));
1541
0
  }
1542
1543
0
  struct KeyData {
1544
0
    KeyState state;
1545
0
    ssize_t start = -1;
1546
0
    ssize_t finish = -1;
1547
0
    ssize_t set_by = -1;
1548
0
  };
1549
1550
0
  std::vector<KeyData> keys;
1551
1552
0
  for (;;) {
1553
0
    keys.clear();
1554
0
    auto result = conn.ExecuteWithResult("SELECT * FROM test_table");
1555
0
    if (!result.ok()) {
1556
0
      LOG(WARNING) << "Select failed: " << result.status();
1557
0
      continue;
1558
0
    }
1559
1560
0
    auto iter = result->CreateIterator();
1561
0
    while (iter.Next()) {
1562
0
      auto row = iter.Row();
1563
0
      int key = row.Value(0).As<int32_t>();
1564
0
      keys.resize(std::max<size_t>(keys.size(), key + 1), {.state = KeyState::kMissing});
1565
0
      keys[key].state = KeyState::kNone;
1566
0
    }
1567
0
    break;
1568
0
  }
1569
1570
0
  for (size_t i = 0; i != events.size(); ++i) {
1571
0
    auto& event = events[i];
1572
0
    auto& key_data = keys[event.key];
1573
0
    if (key_data.state == KeyState::kMissing) {
1574
0
      (event.finished ? key_data.finish : key_data.start) = i;
1575
0
    }
1576
0
  }
1577
1578
0
  for (size_t key = 1; key != keys.size(); ++key) {
1579
0
    if (keys[key].state != KeyState::kMissing || keys[key].finish == -1) {
1580
0
      continue;
1581
0
    }
1582
0
    for (auto set_state : {KeyState::kBeforeMissing, KeyState::kAfterMissing}) {
1583
0
      auto begin = set_state == KeyState::kBeforeMissing ? 0 : keys[key].finish + 1;
1584
0
      auto end = set_state == KeyState::kBeforeMissing ? keys[key].start : events.size();
1585
0
      for (size_t i = begin; i != end; ++i) {
1586
0
        auto& event = events[i];
1587
0
        if (keys[event.key].state == KeyState::kMissing ||
1588
0
            (event.finished != (set_state == KeyState::kBeforeMissing))) {
1589
0
          continue;
1590
0
        }
1591
0
        if (keys[event.key].state == KeyState::kNone) {
1592
0
          keys[event.key].state = set_state;
1593
0
          keys[event.key].set_by = key;
1594
0
        } else if (keys[event.key].state != set_state) {
1595
0
          FAIL() << "Key " << event.key << " already marked as " << keys[event.key].state
1596
0
                 << ", while trying to set: " << set_state << " with " << key << ", prev set: "
1597
0
                 << keys[event.key].set_by;
1598
0
        }
1599
0
      }
1600
0
    }
1601
0
  }
1602
0
}
1603
1604
// Write multiple transactions and restore.
1605
// Then check that we don't have partially applied transaction.
1606
0
TEST_F_EX(YbAdminSnapshotScheduleTest, ConsistentTxnRestore, YbAdminSnapshotConsistentRestoreTest) {
1607
0
  constexpr int kBatchSize = 10;
1608
0
  auto schedule_id = ASSERT_RESULT(PrepareCql());
1609
1610
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1611
1612
0
  ASSERT_OK(conn.ExecuteQuery("CREATE TABLE test_table (k1 INT PRIMARY KEY)"
1613
0
                              "WITH transactions = { 'enabled' : true }"));
1614
0
  TestThreadHolder thread_holder;
1615
0
  thread_holder.AddThreadFunctor([&stop = thread_holder.stop_flag(), &conn] {
1616
0
    const int kConcurrency = 10;
1617
0
    std::string expr = "BEGIN TRANSACTION ";
1618
0
    for (ATTRIBUTE_UNUSED int i : Range(kBatchSize)) {
1619
0
      expr += "INSERT INTO test_table (k1) VALUES (?); ";
1620
0
    }
1621
0
    expr += "END TRANSACTION;";
1622
0
    auto prepared = ASSERT_RESULT(conn.Prepare(expr));
1623
0
    int base = 0;
1624
0
    std::vector<CassandraFuture> futures;
1625
0
    while (!stop.load(std::memory_order_acquire)) {
1626
0
      auto filter = [](CassandraFuture& future) {
1627
0
        if (!future.Ready()) {
1628
0
          return false;
1629
0
        }
1630
0
        auto status = future.Wait();
1631
0
        if (!status.ok() && !status.IsTimedOut()) {
1632
0
          EXPECT_OK(status);
1633
0
        }
1634
0
        return true;
1635
0
      };
1636
0
      ASSERT_NO_FATALS(EraseIf(filter, &futures));
1637
0
      if (futures.size() < kConcurrency) {
1638
0
        auto stmt = prepared.Bind();
1639
0
        for (int i : Range(kBatchSize)) {
1640
0
          stmt.Bind(i, base + i);
1641
0
        }
1642
0
        base += kBatchSize;
1643
0
        futures.push_back(conn.ExecuteGetFuture(stmt));
1644
0
      }
1645
0
    }
1646
0
  });
1647
1648
0
  std::this_thread::sleep_for(250ms);
1649
1650
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1651
1652
0
  std::this_thread::sleep_for(250ms);
1653
1654
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1655
1656
0
  thread_holder.WaitAndStop(250ms);
1657
1658
0
  std::vector<int> keys;
1659
0
  for (;;) {
1660
0
    keys.clear();
1661
0
    auto result = conn.ExecuteWithResult("SELECT * FROM test_table");
1662
0
    if (!result.ok()) {
1663
0
      LOG(WARNING) << "Select failed: " << result.status();
1664
0
      continue;
1665
0
    }
1666
1667
0
    auto iter = result->CreateIterator();
1668
0
    while (iter.Next()) {
1669
0
      auto row = iter.Row();
1670
0
      int key = row.Value(0).As<int32_t>();
1671
0
      keys.push_back(key);
1672
0
    }
1673
0
    break;
1674
0
  }
1675
1676
0
  std::sort(keys.begin(), keys.end());
1677
  // Check that we have whole batches only.
1678
  // Actually this check is little bit relaxed, but it is enough to catch the bug.
1679
0
  for (size_t i : Range(keys.size())) {
1680
0
    ASSERT_EQ(keys[i] % kBatchSize, i % kBatchSize)
1681
0
        << "i: " << i << ", key: " << keys[i] << ", batch: "
1682
0
        << AsString(RangeOfSize<size_t>((i / kBatchSize - 1) * kBatchSize, kBatchSize * 3)[keys]);
1683
0
  }
1684
0
}
1685
1686
class YbAdminSnapshotConsistentRestoreFailoverTest : public YbAdminSnapshotScheduleTest {
1687
 public:
1688
0
  std::vector<std::string> ExtraTSFlags() override {
1689
0
    return { "--consistent_restore=true" };
1690
0
  }
1691
1692
0
  std::vector<std::string> ExtraMasterFlags() override {
1693
0
    return { "--TEST_skip_sending_restore_finished=true" };
1694
0
  }
1695
};
1696
1697
TEST_F_EX(YbAdminSnapshotScheduleTest, ConsistentRestoreFailover,
1698
0
          YbAdminSnapshotConsistentRestoreFailoverTest) {
1699
0
  auto schedule_id = ASSERT_RESULT(PrepareCql());
1700
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1701
1702
0
  ASSERT_OK(conn.ExecuteQuery("CREATE TABLE test_table (k1 INT PRIMARY KEY)"));
1703
0
  auto expr = "INSERT INTO test_table (k1) VALUES ($0)";
1704
0
  ASSERT_OK(conn.ExecuteQueryFormat(expr, 1));
1705
1706
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1707
1708
0
  ASSERT_OK(conn.ExecuteQueryFormat(expr, 2));
1709
1710
0
  auto restoration_id = ASSERT_RESULT(StartRestoreSnapshotSchedule(schedule_id, time));
1711
1712
0
  ASSERT_OK(WaitRestorationDone(restoration_id, 40s));
1713
1714
0
  for (auto* master : cluster_->master_daemons()) {
1715
0
    master->Shutdown();
1716
0
    ASSERT_OK(master->Restart());
1717
0
  }
1718
1719
0
  ASSERT_OK(conn.ExecuteQueryFormat(expr, 3));
1720
1721
0
  auto rows = ASSERT_RESULT(conn.ExecuteAndRenderToString("SELECT * FROM test_table"));
1722
0
  ASSERT_EQ(rows, "1;3");
1723
0
}
1724
1725
0
TEST_F(YbAdminSnapshotScheduleTest, DropKeyspaceAndSchedule) {
1726
0
  auto schedule_id = ASSERT_RESULT(PrepareCql(kInterval, kInterval));
1727
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1728
0
  ASSERT_OK(conn.ExecuteQuery(
1729
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT) "
1730
0
      "WITH transactions = { 'enabled' : true }"));
1731
1732
0
  ASSERT_OK(conn.ExecuteQuery("INSERT INTO test_table (key, value) VALUES (1, 'before')"));
1733
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1734
0
  ASSERT_OK(conn.ExecuteQuery("INSERT INTO test_table (key, value) VALUES (1, 'after')"));
1735
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1736
0
  auto res = ASSERT_RESULT(conn.FetchValue<std::string>("SELECT value FROM test_table"));
1737
0
  ASSERT_EQ(res, "before");
1738
0
  ASSERT_OK(conn.ExecuteQuery("DROP TABLE test_table"));
1739
  // Wait until table completely removed, because of schedule retention.
1740
0
  std::this_thread::sleep_for(kInterval * 3);
1741
0
  ASSERT_NOK(conn.ExecuteQuery(Format("DROP KEYSPACE $0", client::kTableName.namespace_name())));
1742
0
  ASSERT_OK(DeleteSnapshotSchedule(schedule_id));
1743
0
  ASSERT_OK(conn.ExecuteQuery(Format("DROP KEYSPACE $0", client::kTableName.namespace_name())));
1744
0
}
1745
1746
0
TEST_F(YbAdminSnapshotScheduleTest, DeleteIndexOnRestore) {
1747
0
  auto schedule_id = ASSERT_RESULT(PrepareCql(kInterval, kInterval * 4));
1748
1749
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1750
1751
0
  ASSERT_OK(conn.ExecuteQuery(
1752
0
      "CREATE TABLE test_table (key INT PRIMARY KEY, value TEXT) "
1753
0
      "WITH transactions = { 'enabled' : true }"));
1754
1755
0
  for (int i = 0; i != 3; ++i) {
1756
0
    LOG(INFO) << "Iteration: " << i;
1757
0
    ASSERT_OK(conn.ExecuteQuery("INSERT INTO test_table (key, value) VALUES (1, 'value')"));
1758
0
    Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1759
0
    ASSERT_OK(conn.ExecuteQuery("CREATE UNIQUE INDEX test_table_idx ON test_table (value)"));
1760
0
    std::this_thread::sleep_for(kInterval * 2);
1761
0
    ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1762
0
  }
1763
1764
0
  auto snapshots = ASSERT_RESULT(ListSnapshots());
1765
0
  LOG(INFO) << "Snapshots:\n" << common::PrettyWriteRapidJsonToString(snapshots);
1766
0
  std::string id = ASSERT_RESULT(Get(snapshots[0], "id")).get().GetString();
1767
0
  ASSERT_OK(WaitFor([this, &id]() -> Result<bool> {
1768
0
    auto snapshots = VERIFY_RESULT(ListSnapshots());
1769
0
    LOG(INFO) << "Snapshots:\n" << common::PrettyWriteRapidJsonToString(snapshots);
1770
0
    auto current_id = VERIFY_RESULT(Get(snapshots[0], "id")).get().GetString();
1771
0
    return current_id != id;
1772
0
  }, kInterval * 3, "Wait first snapshot to be deleted"));
1773
0
}
1774
1775
class YbAdminRestoreAfterSplitTest : public YbAdminSnapshotScheduleTest {
1776
0
  std::vector<std::string> ExtraMasterFlags() override {
1777
0
    return YbAdminSnapshotScheduleTest::ExtraMasterFlags();
1778
0
  }
1779
};
1780
1781
0
TEST_F_EX(YbAdminSnapshotScheduleTest, RestoreAfterSplit, YbAdminRestoreAfterSplitTest) {
1782
0
  auto schedule_id = ASSERT_RESULT(PrepareCql());
1783
1784
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1785
1786
0
  ASSERT_OK(conn.ExecuteQueryFormat(
1787
0
      "CREATE TABLE $0 (key INT PRIMARY KEY, value TEXT) "
1788
0
      "WITH tablets = 1 AND transactions = { 'enabled' : true }", client::kTableName.table_name()));
1789
1790
0
  auto insert_pattern = Format(
1791
0
      "INSERT INTO $0 (key, value) VALUES (1, '$$0')", client::kTableName.table_name());
1792
0
  ASSERT_OK(conn.ExecuteQueryFormat(insert_pattern, "before"));
1793
0
  Timestamp time(ASSERT_RESULT(WallClock()->Now()).time_point);
1794
0
  ASSERT_OK(conn.ExecuteQueryFormat(insert_pattern, "after"));
1795
1796
0
  {
1797
0
    auto tablets_obj = ASSERT_RESULT(ListTablets());
1798
0
    auto tablets = tablets_obj.GetArray();
1799
0
    ASSERT_EQ(tablets.Size(), 1);
1800
0
    auto tablet_id = ASSERT_RESULT(Get(tablets[0], "id")).get().GetString();
1801
0
    LOG(INFO) << "Tablet id: " << tablet_id;
1802
0
    ASSERT_OK(CallAdmin("split_tablet", tablet_id));
1803
0
  }
1804
1805
0
  std::this_thread::sleep_for(kCleanupSplitTabletsInterval * 5);
1806
1807
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time));
1808
1809
0
  LOG(INFO) << "Reading";
1810
0
  auto select_expr = Format("SELECT * FROM $0", client::kTableName.table_name());
1811
0
  auto rows = ASSERT_RESULT(conn.ExecuteAndRenderToString(select_expr));
1812
0
  ASSERT_EQ(rows, "1,before");
1813
1814
0
  ASSERT_OK(conn.ExecuteQueryFormat(insert_pattern, "final"));
1815
0
  rows = ASSERT_RESULT(conn.ExecuteAndRenderToString(select_expr));
1816
0
  ASSERT_EQ(rows, "1,final");
1817
1818
0
  auto tablets_size = ASSERT_RESULT(ListTablets()).GetArray().Size();
1819
0
  ASSERT_EQ(tablets_size, 1);
1820
0
}
1821
1822
0
TEST_F(YbAdminSnapshotScheduleTest, ConsecutiveRestore) {
1823
0
  const auto retention = kInterval * 5 * kTimeMultiplier;
1824
0
  auto schedule_id = ASSERT_RESULT(PrepareCql(kInterval, retention));
1825
1826
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1827
1828
0
  std::this_thread::sleep_for(FLAGS_max_clock_skew_usec * 1us);
1829
1830
0
  Timestamp time1(ASSERT_RESULT(WallClock()->Now()).time_point);
1831
0
  LOG(INFO) << "Time1: " << time1;
1832
1833
0
  ASSERT_OK(conn.ExecuteQueryFormat(
1834
0
      "CREATE TABLE $0 (key INT PRIMARY KEY, value TEXT) WITH tablets = 1",
1835
0
      client::kTableName.table_name()));
1836
1837
0
  auto insert_pattern = Format(
1838
0
      "INSERT INTO $0 (key, value) VALUES (1, '$$0')", client::kTableName.table_name());
1839
0
  ASSERT_OK(conn.ExecuteQueryFormat(insert_pattern, "before"));
1840
0
  Timestamp time2(ASSERT_RESULT(WallClock()->Now()).time_point);
1841
0
  ASSERT_OK(conn.ExecuteQueryFormat(insert_pattern, "after"));
1842
1843
0
  auto select_expr = Format("SELECT * FROM $0", client::kTableName.table_name());
1844
0
  auto rows = ASSERT_RESULT(conn.ExecuteAndRenderToString(select_expr));
1845
0
  ASSERT_EQ(rows, "1,after");
1846
1847
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time1));
1848
1849
0
  std::this_thread::sleep_for(3s * kTimeMultiplier);
1850
1851
0
  ASSERT_NOK(conn.ExecuteAndRenderToString(select_expr));
1852
1853
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time2));
1854
1855
0
  rows = ASSERT_RESULT(conn.ExecuteAndRenderToString(select_expr));
1856
0
  ASSERT_EQ(rows, "1,before");
1857
1858
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time1));
1859
1860
0
  ASSERT_OK(WaitTabletsCleaned(CoarseMonoClock::now() + retention + kInterval));
1861
0
}
1862
1863
class YbAdminSnapshotScheduleTestWithoutConsecutiveRestore : public YbAdminSnapshotScheduleTest {
1864
0
  std::vector<std::string> ExtraMasterFlags() override {
1865
    // To speed up tests.
1866
0
    return { "--snapshot_coordinator_cleanup_delay_ms=1000",
1867
0
             "--snapshot_coordinator_poll_interval_ms=500",
1868
0
             "--enable_automatic_tablet_splitting=true",
1869
0
             "--enable_transactional_ddl_gc=false",
1870
0
             "--allow_consecutive_restore=false" };
1871
0
  }
1872
};
1873
1874
0
TEST_F(YbAdminSnapshotScheduleTestWithoutConsecutiveRestore, DisallowConsecutiveRestore) {
1875
0
  const auto retention = kInterval * 5 * kTimeMultiplier;
1876
0
  auto schedule_id = ASSERT_RESULT(PrepareCql(kInterval, retention));
1877
1878
0
  auto conn = ASSERT_RESULT(CqlConnect(client::kTableName.namespace_name()));
1879
1880
0
  std::this_thread::sleep_for(FLAGS_max_clock_skew_usec * 1us);
1881
1882
0
  Timestamp time1(ASSERT_RESULT(WallClock()->Now()).time_point);
1883
0
  LOG(INFO) << "Time1: " << time1;
1884
1885
0
  ASSERT_OK(conn.ExecuteQueryFormat(
1886
0
      "CREATE TABLE $0 (key INT PRIMARY KEY, value TEXT) WITH tablets = 1",
1887
0
      client::kTableName.table_name()));
1888
1889
0
  auto insert_pattern = Format(
1890
0
      "INSERT INTO $0 (key, value) VALUES (1, '$$0')", client::kTableName.table_name());
1891
0
  ASSERT_OK(conn.ExecuteQueryFormat(insert_pattern, "before"));
1892
0
  Timestamp time2(ASSERT_RESULT(WallClock()->Now()).time_point);
1893
0
  ASSERT_OK(conn.ExecuteQueryFormat(insert_pattern, "after"));
1894
1895
0
  auto select_expr = Format("SELECT * FROM $0", client::kTableName.table_name());
1896
0
  auto rows = ASSERT_RESULT(conn.ExecuteAndRenderToString(select_expr));
1897
0
  ASSERT_EQ(rows, "1,after");
1898
1899
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time1));
1900
1901
0
  std::this_thread::sleep_for(3s * kTimeMultiplier);
1902
1903
0
  auto s = conn.ExecuteAndRenderToString(select_expr);
1904
0
  ASSERT_NOK(s);
1905
0
  ASSERT_STR_CONTAINS(s.status().message().ToBuffer(), "Object Not Found");
1906
1907
0
  Status s2 = RestoreSnapshotSchedule(schedule_id, time2);
1908
0
  ASSERT_NOK(s2);
1909
0
  ASSERT_STR_CONTAINS(
1910
0
      s2.message().ToBuffer(), "Cannot restore before the previous restoration time");
1911
1912
0
  Timestamp time3(ASSERT_RESULT(WallClock()->Now()).time_point);
1913
0
  LOG(INFO) << "Time3: " << time1;
1914
1915
0
  ASSERT_OK(conn.ExecuteQueryFormat(
1916
0
      "CREATE TABLE $0 (key INT PRIMARY KEY, value TEXT) WITH tablets = 1",
1917
0
      client::kTableName.table_name()));
1918
1919
0
  ASSERT_OK(conn.ExecuteQueryFormat(insert_pattern, "after"));
1920
1921
0
  rows = ASSERT_RESULT(conn.ExecuteAndRenderToString(select_expr));
1922
0
  ASSERT_EQ(rows, "1,after");
1923
1924
0
  ASSERT_OK(RestoreSnapshotSchedule(schedule_id, time3));
1925
1926
0
  std::this_thread::sleep_for(3s * kTimeMultiplier);
1927
1928
0
  s = conn.ExecuteAndRenderToString(select_expr);
1929
0
  ASSERT_NOK(s);
1930
0
  ASSERT_STR_CONTAINS(s.status().message().ToBuffer(), "Object Not Found");
1931
0
}
1932
1933
class YbAdminSnapshotScheduleTestWithLB : public YbAdminSnapshotScheduleTest {
1934
0
  std::vector<std::string> ExtraMasterFlags() override {
1935
0
    std::vector<std::string> flags;
1936
0
    flags = YbAdminSnapshotScheduleTest::ExtraMasterFlags();
1937
0
    flags.push_back("--enable_load_balancing=true");
1938
0
    flags.push_back("--TEST_load_balancer_skip_inactive_tablets=false");
1939
1940
0
    return flags;
1941
0
  }
1942
1943
 public:
1944
0
  void WaitForLoadBalanceCompletion(yb::MonoDelta timeout) {
1945
0
    ASSERT_OK(WaitFor([&]() -> Result<bool> {
1946
0
      bool is_idle = VERIFY_RESULT(client_->IsLoadBalancerIdle());
1947
0
      return !is_idle;
1948
0
    }, timeout, "IsLoadBalancerActive"));
1949
1950
0
    ASSERT_OK(WaitFor([&]() -> Result<bool> {
1951
0
      return client_->IsLoadBalancerIdle();
1952
0
    }, timeout, "IsLoadBalancerIdle"));
1953
0
  }
1954
1955
0
  void WaitForLoadToBeBalanced(yb::MonoDelta timeout) {
1956
0
    ASSERT_OK(WaitFor([&]() -> Result<bool> {
1957
0
      std::vector<uint32_t> tserver_loads;
1958
0
      for (size_t i = 0; i != cluster_->num_tablet_servers(); ++i) {
1959
0
        auto proxy = cluster_->GetTServerProxy<tserver::TabletServerServiceProxy>(i);
1960
0
        tserver::ListTabletsRequestPB req;
1961
0
        tserver::ListTabletsResponsePB resp;
1962
0
        rpc::RpcController controller;
1963
0
        controller.set_timeout(timeout);
1964
0
        RETURN_NOT_OK(proxy.ListTablets(req, &resp, &controller));
1965
0
        int tablet_count = 0;
1966
0
        for (const auto& tablet : resp.status_and_schema()) {
1967
0
          if (tablet.tablet_status().table_type() == TableType::TRANSACTION_STATUS_TABLE_TYPE) {
1968
0
            continue;
1969
0
          }
1970
0
          if (tablet.tablet_status().namespace_name() == client::kTableName.namespace_name()) {
1971
0
            if (tablet.tablet_status().tablet_data_state() != tablet::TABLET_DATA_TOMBSTONED) {
1972
0
              ++tablet_count;
1973
0
            }
1974
0
          }
1975
0
        }
1976
0
        LOG(INFO) << "For TS " << cluster_->tablet_server(i)->id() << ", load: " << tablet_count;
1977
0
        tserver_loads.push_back(tablet_count);
1978
0
      }
1979
1980
0
      return integration_tests::AreLoadsBalanced(tserver_loads);
1981
0
    }, timeout, "Are loads balanced"));
1982
0
  }
1983
};
1984
1985
0
TEST_F(YbAdminSnapshotScheduleTestWithLB, TestLBHiddenTables) {
1986
  // Create a schedule.
1987
0
  auto schedule_id = ASSERT_RESULT(PrepareCql());
1988
1989
  // Create a table with 8 tablets.
1990
0
  LOG(INFO) << "Create table " << client::kTableName.table_name() << " with 8 tablets";
1991
0
  ASSERT_NO_FATALS(client::kv_table_test::CreateTable(
1992
0
      client::Transactional::kTrue, 8, client_.get(), &table_));
1993
1994
  // Drop the table so that it becomes Hidden.
1995
0
  LOG(INFO) << "Hiding table " << client::kTableName.table_name();
1996
0
  ASSERT_OK(client_->DeleteTable(client::kTableName));
1997
1998
  // Add a tserver and wait for LB to balance the load.
1999
0
  LOG(INFO) << "Adding a fourth tablet server";
2000
0
  std::vector<std::string> ts_flags = ExtraTSFlags();
2001
0
  ASSERT_OK(cluster_->AddTabletServer(true, ts_flags));
2002
0
  ASSERT_OK(cluster_->WaitForTabletServerCount(4, 30s));
2003
2004
  // Wait for LB to be idle.
2005
0
  WaitForLoadBalanceCompletion(30s * kTimeMultiplier * 10);
2006
2007
  // Validate loads are balanced.
2008
0
  WaitForLoadToBeBalanced(30s * kTimeMultiplier * 10);
2009
0
}
2010
2011
}  // namespace tools
2012
}  // namespace yb