YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/yql/pgwrapper/pg_index_backfill-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
#include <cmath>
14
#include <map>
15
#include <string>
16
#include <vector>
17
18
#include "yb/client/table_info.h"
19
20
#include "yb/common/schema.h"
21
22
#include "yb/integration-tests/backfill-test-util.h"
23
24
#include "yb/util/async_util.h"
25
#include "yb/util/backoff_waiter.h"
26
#include "yb/util/format.h"
27
#include "yb/util/monotime.h"
28
#include "yb/util/status_format.h"
29
#include "yb/util/test_thread_holder.h"
30
#include "yb/util/test_util.h"
31
#include "yb/util/tsan_util.h"
32
33
#include "yb/yql/pgwrapper/libpq_test_base.h"
34
#include "yb/yql/pgwrapper/libpq_utils.h"
35
36
using namespace std::chrono_literals;
37
38
namespace yb {
39
namespace pgwrapper {
40
41
namespace {
42
43
constexpr auto kColoDbName = "colodb";
44
constexpr auto kDatabaseName = "yugabyte";
45
constexpr auto kIndexName = "iii";
46
constexpr auto kTableName = "ttt";
47
const client::YBTableName kYBTableName(YQLDatabase::YQL_DATABASE_PGSQL, kDatabaseName, kTableName);
48
49
} // namespace
50
51
YB_DEFINE_ENUM(IndexStateFlag, (kIndIsLive)(kIndIsReady)(kIndIsValid));
52
typedef EnumBitSet<IndexStateFlag> IndexStateFlags;
53
54
class PgIndexBackfillTest : public LibPqTestBase {
55
 public:
56
0
  void SetUp() override {
57
0
    LibPqTestBase::SetUp();
58
59
0
    conn_ = std::make_unique<PGConn>(ASSERT_RESULT(ConnectToDB(kDatabaseName)));
60
0
  }
61
62
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
63
0
    options->extra_master_flags.push_back("--ysql_disable_index_backfill=false");
64
0
    options->extra_master_flags.push_back(
65
0
        Format("--ysql_num_shards_per_tserver=$0", kTabletsPerServer));
66
0
    options->extra_tserver_flags.push_back("--ysql_disable_index_backfill=false");
67
0
    options->extra_tserver_flags.push_back(
68
0
        Format("--ysql_num_shards_per_tserver=$0", kTabletsPerServer));
69
0
  }
70
71
 protected:
72
  bool HasClientTimedOut(const Status& s);
73
  void TestSimpleBackfill(const std::string& table_create_suffix = "");
74
  void TestLargeBackfill(const int num_rows);
75
  void TestRetainDeleteMarkers(const std::string& db_name);
76
  const int kTabletsPerServer = 8;
77
78
  std::unique_ptr<PGConn> conn_;
79
};
80
81
namespace {
82
83
// A copy of the same function in pg_libpq-test.cc.  Eventually, issue #6868 should provide a way to
84
// do this easily for both this file and that.
85
Result<string> GetTableIdByTableName(
86
0
    client::YBClient* client, const string& namespace_name, const string& table_name) {
87
0
  const auto tables = VERIFY_RESULT(client->ListTables());
88
0
  for (const auto& t : tables) {
89
0
    if (t.namespace_name() == namespace_name && t.table_name() == table_name) {
90
0
      return t.table_id();
91
0
    }
92
0
  }
93
0
  return STATUS(NotFound, "The table does not exist");
94
0
}
95
96
0
Result<int> TotalBackfillRpcMetric(ExternalMiniCluster* cluster, const char* type) {
97
0
  int total_rpc_calls = 0;
98
0
  constexpr auto metric_name = "handler_latency_yb_tserver_TabletServerAdminService_BackfillIndex";
99
0
  for (auto ts : cluster->tserver_daemons()) {
100
0
    auto val = VERIFY_RESULT(ts->GetInt64Metric("server", "yb.tabletserver", metric_name, type));
101
0
    total_rpc_calls += val;
102
0
    VLOG(1) << ts->bind_host() << " for " << type << " returned " << val;
103
0
  }
104
0
  return total_rpc_calls;
105
0
}
106
107
0
Result<int> TotalBackfillRpcCalls(ExternalMiniCluster* cluster) {
108
0
  return TotalBackfillRpcMetric(cluster, "total_count");
109
0
}
110
111
0
Result<double> AvgBackfillRpcLatencyInMicros(ExternalMiniCluster* cluster) {
112
0
  auto num_calls = VERIFY_RESULT(TotalBackfillRpcMetric(cluster, "total_count"));
113
0
  double total_latency = VERIFY_RESULT(TotalBackfillRpcMetric(cluster, "total_sum"));
114
0
  return total_latency / num_calls;
115
0
}
116
117
} // namespace
118
119
0
bool PgIndexBackfillTest::HasClientTimedOut(const Status& s) {
120
0
  if (!s.IsNetworkError()) {
121
0
    return false;
122
0
  }
123
124
  // The client timeout is set using the same backfill_index_client_rpc_timeout_ms for
125
  // postgres-tserver RPC and tserver-master RPC.  Since they are the same value, it _may_ be
126
  // possible for either timeout message to show up, so accept either, even though the
127
  // postgres-tserver timeout is far more likely to show up.
128
  //
129
  // The first is postgres-tserver; the second is tserver-master.
130
0
  const std::string msg = s.message().ToBuffer();
131
0
  return msg.find("Timed out: BackfillIndex RPC") != std::string::npos ||
132
0
         msg.find("Timed out waiting for Backfill Index") != std::string::npos;
133
0
}
134
135
0
void PgIndexBackfillTest::TestSimpleBackfill(const std::string& table_create_suffix) {
136
0
  ASSERT_OK(conn_->ExecuteFormat(
137
0
    "CREATE TABLE $0 (c char, i int, p point) $1",
138
0
    kTableName,
139
0
    table_create_suffix));
140
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES ('a', 0, '(1, 2)')", kTableName));
141
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES ('y', -5, '(0, -2)')", kTableName));
142
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES ('b', 100, '(868, 9843)')", kTableName));
143
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX ON $0 (c ASC)", kTableName));
144
145
  // Index scan to verify contents of index table.
146
0
  const std::string query = Format("SELECT * FROM $0 ORDER BY c", kTableName);
147
0
  ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
148
0
  PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
149
0
  ASSERT_EQ(PQntuples(res.get()), 3);
150
0
  ASSERT_EQ(PQnfields(res.get()), 3);
151
0
  std::array<int, 3> values = {
152
0
    ASSERT_RESULT(GetInt32(res.get(), 0, 1)),
153
0
    ASSERT_RESULT(GetInt32(res.get(), 1, 1)),
154
0
    ASSERT_RESULT(GetInt32(res.get(), 2, 1)),
155
0
  };
156
0
  ASSERT_EQ(values[0], 0);
157
0
  ASSERT_EQ(values[1], 100);
158
0
  ASSERT_EQ(values[2], -5);
159
0
}
160
161
// Checks that retain_delete_markers is false after index creation.
162
0
void PgIndexBackfillTest::TestRetainDeleteMarkers(const std::string& db_name) {
163
0
  auto client = ASSERT_RESULT(cluster_->CreateClient());
164
165
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
166
0
  const auto index_name = "ttt_idx";
167
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX $0 ON $1 (i ASC)", index_name, kTableName));
168
169
  // Verify that retain_delete_markers was set properly in the index table schema.
170
0
  const std::string table_id = ASSERT_RESULT(GetTableIdByTableName(
171
0
      client.get(), db_name, index_name));
172
0
  auto table_info = std::make_shared<client::YBTableInfo>();
173
0
  {
174
0
    Synchronizer sync;
175
0
    ASSERT_OK(client->GetTableSchemaById(table_id, table_info, sync.AsStatusCallback()));
176
0
    ASSERT_OK(sync.Wait());
177
0
  }
178
179
0
  ASSERT_EQ(table_info->schema.version(), 0);
180
0
  ASSERT_FALSE(table_info->schema.table_properties().retain_delete_markers());
181
0
}
182
183
0
void PgIndexBackfillTest::TestLargeBackfill(const int num_rows) {
184
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
185
186
  // Insert bunch of rows.
187
0
  ASSERT_OK(conn_->ExecuteFormat(
188
0
      "INSERT INTO $0 VALUES (generate_series(1, $1))",
189
0
      kTableName,
190
0
      num_rows));
191
192
  // Create index.
193
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX ON $0 (i ASC)", kTableName));
194
195
  // All rows should be in the index.
196
0
  const std::string query = Format(
197
0
      "SELECT COUNT(*) FROM $0 WHERE i > 0",
198
0
      kTableName);
199
0
  ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
200
0
  PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
201
0
  ASSERT_EQ(PQntuples(res.get()), 1);
202
0
  ASSERT_EQ(PQnfields(res.get()), 1);
203
0
  auto actual_num_rows = ASSERT_RESULT(GetInt64(res.get(), 0, 0));
204
0
  ASSERT_EQ(actual_num_rows, num_rows);
205
0
}
206
207
// Make sure that backfill works.
208
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(Simple)) {
209
0
  TestSimpleBackfill();
210
0
}
211
212
// Make sure that partial indexes work for index backfill.
213
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(Partial)) {
214
0
  constexpr int kNumRows = 7;
215
216
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int, j int)", kTableName));
217
0
  ASSERT_OK(conn_->ExecuteFormat(
218
0
      "INSERT INTO $0 VALUES (generate_series(1, $1), generate_series(-1, -$1, -1))",
219
0
      kTableName,
220
0
      kNumRows));
221
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX ON $0 (i ASC) WHERE j > -5", kTableName));
222
223
  // Index scan to verify contents of index table.
224
0
  {
225
0
    const std::string query = Format("SELECT j FROM $0 WHERE j > -3 ORDER BY i", kTableName);
226
0
    ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
227
0
    PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
228
0
    ASSERT_EQ(PQntuples(res.get()), 2);
229
0
    ASSERT_EQ(PQnfields(res.get()), 1);
230
0
    std::array<int, 2> values = {
231
0
      ASSERT_RESULT(GetInt32(res.get(), 0, 0)),
232
0
      ASSERT_RESULT(GetInt32(res.get(), 1, 0)),
233
0
    };
234
0
    ASSERT_EQ(values[0], -1);
235
0
    ASSERT_EQ(values[1], -2);
236
0
  }
237
0
  {
238
0
    const std::string query = Format(
239
0
        "SELECT i FROM $0 WHERE j > -5 ORDER BY i DESC LIMIT 2",
240
0
        kTableName);
241
0
    ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
242
0
    PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
243
0
    ASSERT_EQ(PQntuples(res.get()), 2);
244
0
    ASSERT_EQ(PQnfields(res.get()), 1);
245
0
    std::array<int, 2> values = {
246
0
      ASSERT_RESULT(GetInt32(res.get(), 0, 0)),
247
0
      ASSERT_RESULT(GetInt32(res.get(), 1, 0)),
248
0
    };
249
0
    ASSERT_EQ(values[0], 4);
250
0
    ASSERT_EQ(values[1], 3);
251
0
  }
252
0
}
253
254
// Make sure that expression indexes work for index backfill.
255
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(Expression)) {
256
0
  constexpr int kNumRows = 9;
257
258
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int, j int)", kTableName));
259
0
  ASSERT_OK(conn_->ExecuteFormat(
260
0
      "INSERT INTO $0 VALUES (generate_series(1, $1), generate_series(11, 10 + $1))",
261
0
      kTableName,
262
0
      kNumRows));
263
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX ON $0 ((j % i))", kTableName));
264
265
  // Index scan to verify contents of index table.
266
0
  const std::string query = Format(
267
0
      "SELECT j, i, j % i as mod FROM $0 WHERE j % i = 2 ORDER BY i",
268
0
      kTableName);
269
0
  ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
270
0
  PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
271
0
  ASSERT_EQ(PQntuples(res.get()), 2);
272
0
  ASSERT_EQ(PQnfields(res.get()), 3);
273
0
  std::array<std::array<int, 3>, 2> values = {{
274
0
    {
275
0
      ASSERT_RESULT(GetInt32(res.get(), 0, 0)),
276
0
      ASSERT_RESULT(GetInt32(res.get(), 0, 1)),
277
0
      ASSERT_RESULT(GetInt32(res.get(), 0, 2)),
278
0
    },
279
0
    {
280
0
      ASSERT_RESULT(GetInt32(res.get(), 1, 0)),
281
0
      ASSERT_RESULT(GetInt32(res.get(), 1, 1)),
282
0
      ASSERT_RESULT(GetInt32(res.get(), 1, 2)),
283
0
    },
284
0
  }};
285
0
  ASSERT_EQ(values[0][0], 14);
286
0
  ASSERT_EQ(values[0][1], 4);
287
0
  ASSERT_EQ(values[0][2], 2);
288
0
  ASSERT_EQ(values[1][0], 18);
289
0
  ASSERT_EQ(values[1][1], 8);
290
0
  ASSERT_EQ(values[1][2], 2);
291
0
}
292
293
// Make sure that unique indexes work when index backfill is enabled.
294
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(Unique)) {
295
0
  constexpr int kNumRows = 3;
296
297
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int, j int)", kTableName));
298
0
  ASSERT_OK(conn_->ExecuteFormat(
299
0
      "INSERT INTO $0 VALUES (generate_series(1, $1), generate_series(11, 10 + $1))",
300
0
      kTableName,
301
0
      kNumRows));
302
  // Add row that would make j not unique.
303
0
  ASSERT_OK(conn_->ExecuteFormat(
304
0
      "INSERT INTO $0 VALUES (99, 11)",
305
0
      kTableName,
306
0
      kNumRows));
307
308
  // Create unique index without failure.
309
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE UNIQUE INDEX ON $0 (i ASC)", kTableName));
310
  // Index scan to verify contents of index table.
311
0
  const std::string query = Format(
312
0
      "SELECT * FROM $0 ORDER BY i",
313
0
      kTableName);
314
0
  ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
315
0
  PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
316
0
  ASSERT_EQ(PQntuples(res.get()), 4);
317
0
  ASSERT_EQ(PQnfields(res.get()), 2);
318
319
  // Create unique index with failure.
320
0
  Status status = conn_->ExecuteFormat("CREATE UNIQUE INDEX ON $0 (j ASC)", kTableName);
321
0
  ASSERT_NOK(status);
322
0
  const std::string msg = status.message().ToBuffer();
323
0
  ASSERT_TRUE(msg.find("duplicate key value violates unique constraint") != std::string::npos)
324
0
      << status;
325
0
}
326
327
// Make sure that indexes created in postgres nested DDL work and skip backfill (optimization).
328
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(NestedDdl)) {
329
0
  auto client = ASSERT_RESULT(cluster_->CreateClient());
330
0
  constexpr int kNumRows = 3;
331
332
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int, j int, UNIQUE (j))", kTableName));
333
334
  // Make sure that the index create was not multi-stage.
335
0
  const std::string table_id = ASSERT_RESULT(GetTableIdByTableName(
336
0
      client.get(), kDatabaseName, kTableName));
337
0
  std::shared_ptr<client::YBTableInfo> table_info = std::make_shared<client::YBTableInfo>();
338
0
  Synchronizer sync;
339
0
  ASSERT_OK(client->GetTableSchemaById(table_id, table_info, sync.AsStatusCallback()));
340
0
  ASSERT_OK(sync.Wait());
341
0
  ASSERT_EQ(table_info->schema.version(), 1);
342
343
0
  ASSERT_OK(conn_->ExecuteFormat(
344
0
      "INSERT INTO $0 VALUES (generate_series(1, $1), generate_series(11, 10 + $1))",
345
0
      kTableName,
346
0
      kNumRows));
347
348
  // Add row that violates unique constraint on j.
349
0
  Status status = conn_->ExecuteFormat(
350
0
      "INSERT INTO $0 VALUES (99, 11)",
351
0
      kTableName,
352
0
      kNumRows);
353
0
  ASSERT_NOK(status);
354
0
  const std::string msg = status.message().ToBuffer();
355
0
  ASSERT_TRUE(msg.find("duplicate key value") != std::string::npos) << status;
356
0
}
357
358
// Make sure that drop index works when index backfill is enabled (skips online schema migration for
359
// now)
360
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(Drop)) {
361
0
  constexpr int kNumRows = 5;
362
363
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int, j int)", kTableName));
364
0
  ASSERT_OK(conn_->ExecuteFormat(
365
0
      "INSERT INTO $0 VALUES (generate_series(1, $1), generate_series(11, 10 + $1))",
366
0
      kTableName,
367
0
      kNumRows));
368
369
  // Create index.
370
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX $0 ON $1 (i ASC)", kIndexName, kTableName));
371
372
  // Drop index.
373
0
  ASSERT_OK(conn_->ExecuteFormat("DROP INDEX $0", kIndexName));
374
375
  // Ensure index is not used for scan.
376
0
  const std::string query = Format(
377
0
      "SELECT * FROM $0 ORDER BY i",
378
0
      kTableName);
379
0
  ASSERT_FALSE(ASSERT_RESULT(conn_->HasIndexScan(query)));
380
0
}
381
382
// Make sure deletes to nonexistent rows look like noops to clients.  This may seem too obvious to
383
// necessitate a test, but logic for backfill is special in that it wants nonexistent index deletes
384
// to be applied for the backfill process to use them.  This test guards against that logic being
385
// implemented incorrectly.
386
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(NonexistentDelete)) {
387
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int PRIMARY KEY)", kTableName));
388
389
  // Delete to nonexistent row should return no rows.
390
0
  PGResultPtr res = ASSERT_RESULT(conn_->FetchFormat(
391
0
      "DELETE FROM $0 WHERE i = 1 RETURNING i",
392
0
      kTableName));
393
0
  ASSERT_EQ(PQntuples(res.get()), 0);
394
0
  ASSERT_EQ(PQnfields(res.get()), 1);
395
0
}
396
397
// Make sure that index backfill on large tables backfills all data.
398
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(Large)) {
399
0
  constexpr int kNumRows = 10000;
400
0
  TestLargeBackfill(kNumRows);
401
0
  auto expected_calls = cluster_->num_tablet_servers() * kTabletsPerServer;
402
0
  auto actual_calls = ASSERT_RESULT(TotalBackfillRpcCalls(cluster_.get()));
403
0
  ASSERT_GE(actual_calls, expected_calls);
404
0
}
405
406
class PgIndexBackfillTestChunking : public PgIndexBackfillTest {
407
 protected:
408
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
409
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
410
0
    options->extra_tserver_flags.push_back(
411
0
        Format("--TEST_backfill_paging_size=$0", kBatchSize));
412
0
    options->extra_tserver_flags.push_back(
413
0
        Format("--backfill_index_write_batch_size=$0", kBatchSize));
414
0
    options->extra_tserver_flags.push_back(
415
0
        Format("--ysql_prefetch_limit=$0", kPrefetchSize));
416
0
  }
417
  const int kBatchSize = 200;
418
  const int kPrefetchSize = 128;
419
};
420
421
// Set batch size and prefetch limit such that:
422
// Each tablet requires multiple RPC calls from the master to complete backfill.
423
//     Also, set the ysql_prefetch_size small to ensure that each of these
424
//     `BACKFILL INDEX` calls will fetch data from the tserver at least 2 times.
425
// Fetch metrics to ensure that there have been > num_tablets rpc's.
426
TEST_F_EX(
427
0
    PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(BackfillInChunks), PgIndexBackfillTestChunking) {
428
0
  constexpr int kNumRows = 10000;
429
0
  TestLargeBackfill(kNumRows);
430
431
0
  const size_t effective_batch_size =
432
0
      static_cast<size_t>(kPrefetchSize * ceil(1.0 * kBatchSize / kPrefetchSize));
433
0
  const size_t min_expected_calls =
434
0
      static_cast<size_t>(ceil(1.0 * kNumRows / effective_batch_size));
435
0
  auto actual_calls = ASSERT_RESULT(TotalBackfillRpcCalls(cluster_.get()));
436
0
  LOG(INFO) << "Had " << actual_calls << " backfill rpc calls. "
437
0
            << "Expected at least " << kNumRows << "/" << effective_batch_size << " = "
438
0
            << min_expected_calls;
439
0
  ASSERT_GE(actual_calls, min_expected_calls);
440
0
}
441
442
class PgIndexBackfillTestThrottled : public PgIndexBackfillTest {
443
 protected:
444
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
445
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
446
0
    options->extra_master_flags.push_back(
447
0
        Format("--ysql_index_backfill_rpc_timeout_ms=$0", kBackfillRpcDeadlineLargeMs));
448
449
0
    options->extra_tserver_flags.push_back("--ysql_prefetch_limit=100");
450
0
    options->extra_tserver_flags.push_back("--backfill_index_write_batch_size=100");
451
0
    options->extra_tserver_flags.push_back(
452
0
        Format("--backfill_index_rate_rows_per_sec=$0", kBackfillRateRowsPerSec));
453
0
    options->extra_tserver_flags.push_back(
454
0
        Format("--num_concurrent_backfills_allowed=$0", kNumConcurrentBackfills));
455
0
  }
456
457
 protected:
458
  const int kBackfillRateRowsPerSec = 100;
459
  const int kNumConcurrentBackfills = 1;
460
  const int kBackfillRpcDeadlineLargeMs = 10 * 60 * 1000;
461
};
462
463
// Set the backfill batch size and backfill rate
464
// Check that the time taken to backfill is no less than what is expected.
465
TEST_F_EX(
466
0
    PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(ThrottledBackfill), PgIndexBackfillTestThrottled) {
467
0
  constexpr int kNumRows = 10000;
468
0
  auto start_time = CoarseMonoClock::Now();
469
0
  TestLargeBackfill(kNumRows);
470
0
  auto end_time = CoarseMonoClock::Now();
471
0
  auto expected_time = MonoDelta::FromSeconds(
472
0
      kNumRows * 1.0 /
473
0
      (cluster_->num_tablet_servers() * kNumConcurrentBackfills * kBackfillRateRowsPerSec));
474
0
  ASSERT_GE(MonoDelta{end_time - start_time}, expected_time);
475
476
  // Expect only 1 call per tablet
477
0
  const size_t expected_calls = cluster_->num_tablet_servers() * kTabletsPerServer;
478
0
  auto actual_calls = ASSERT_RESULT(TotalBackfillRpcCalls(cluster_.get()));
479
0
  ASSERT_EQ(actual_calls, expected_calls);
480
481
0
  auto avg_rpc_latency_usec = ASSERT_RESULT(AvgBackfillRpcLatencyInMicros(cluster_.get()));
482
0
  LOG(INFO) << "Avg backfill latency was " << avg_rpc_latency_usec << " us";
483
0
  ASSERT_LE(avg_rpc_latency_usec, kBackfillRpcDeadlineLargeMs * 1000);
484
0
}
485
486
class PgIndexBackfillTestDeadlines : public PgIndexBackfillTest {
487
 protected:
488
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
489
0
    options->extra_master_flags.push_back("--ysql_disable_index_backfill=false");
490
0
    options->extra_master_flags.push_back(
491
0
        Format("--ysql_num_shards_per_tserver=$0", kTabletsPerServer));
492
0
    options->extra_master_flags.push_back(
493
0
        Format("--ysql_index_backfill_rpc_timeout_ms=$0", kBackfillRpcDeadlineSmallMs));
494
0
    options->extra_master_flags.push_back(
495
0
        Format("--backfill_index_timeout_grace_margin_ms=$0", kBackfillRpcDeadlineSmallMs / 2));
496
497
0
    options->extra_tserver_flags.push_back("--ysql_disable_index_backfill=false");
498
0
    options->extra_tserver_flags.push_back(
499
0
        Format("--ysql_num_shards_per_tserver=$0", kTabletsPerServer));
500
0
    options->extra_tserver_flags.push_back("--ysql_prefetch_limit=100");
501
0
    options->extra_tserver_flags.push_back("--backfill_index_write_batch_size=100");
502
0
    options->extra_tserver_flags.push_back(
503
0
        Format("--backfill_index_rate_rows_per_sec=$0", kBackfillRateRowsPerSec));
504
0
    options->extra_tserver_flags.push_back(
505
0
        Format("--num_concurrent_backfills_allowed=$0", kNumConcurrentBackfills));
506
0
  }
507
508
 protected:
509
  const int kBackfillRpcDeadlineSmallMs = 10000;
510
  const int kBackfillRateRowsPerSec = 100;
511
  const int kNumConcurrentBackfills = 1;
512
  const int kTabletsPerServer = 1;
513
};
514
515
// Set the backfill batch size, backfill rate and a low timeout for backfill rpc.
516
// Ensure that the backfill is completed. And that the avg rpc latency is
517
// below what is set as the timeout.
518
TEST_F_EX(
519
    PgIndexBackfillTest,
520
    YB_DISABLE_TEST_IN_TSAN(BackfillRespectsDeadline),
521
0
    PgIndexBackfillTestDeadlines) {
522
0
  constexpr int kNumRows = 10000;
523
0
  TestLargeBackfill(kNumRows);
524
525
0
  const size_t num_tablets = cluster_->num_tablet_servers() * kTabletsPerServer;
526
0
  const size_t min_expected_calls = static_cast<size_t>(
527
0
      ceil(kNumRows / (kBackfillRpcDeadlineSmallMs * kBackfillRateRowsPerSec * 0.001)));
528
0
  ASSERT_GT(min_expected_calls, num_tablets);
529
0
  auto actual_calls = ASSERT_RESULT(TotalBackfillRpcCalls(cluster_.get()));
530
0
  ASSERT_GE(actual_calls, num_tablets);
531
0
  ASSERT_GE(actual_calls, min_expected_calls);
532
533
0
  auto avg_rpc_latency_usec = ASSERT_RESULT(AvgBackfillRpcLatencyInMicros(cluster_.get()));
534
0
  LOG(INFO) << "Avg backfill latency was " << avg_rpc_latency_usec << " us";
535
0
  ASSERT_LE(avg_rpc_latency_usec, kBackfillRpcDeadlineSmallMs * 1000);
536
0
}
537
538
// Make sure that CREATE INDEX NONCONCURRENTLY doesn't use backfill.
539
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(Nonconcurrent)) {
540
0
  auto client = ASSERT_RESULT(cluster_->CreateClient());
541
542
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
543
0
  const std::string table_id = ASSERT_RESULT(GetTableIdByTableName(
544
0
      client.get(), kDatabaseName, kTableName));
545
546
  // To determine whether the index uses backfill or not, look at the table schema version before
547
  // and after.  We can't look at the DocDB index permissions because
548
  // - if backfill is skipped, index_permissions is unset, and the default value is
549
  //   INDEX_PERM_READ_WRITE_AND_DELETE
550
  // - if backfill is used, index_permissions is INDEX_PERM_READ_WRITE_AND_DELETE
551
  // - GetTableSchemaById offers no way to see whether the default value for index permissions is
552
  //   set
553
0
  std::shared_ptr<client::YBTableInfo> info = std::make_shared<client::YBTableInfo>();
554
0
  {
555
0
    Synchronizer sync;
556
0
    ASSERT_OK(client->GetTableSchemaById(table_id, info, sync.AsStatusCallback()));
557
0
    ASSERT_OK(sync.Wait());
558
0
  }
559
0
  ASSERT_EQ(info->schema.version(), 0);
560
561
0
  ASSERT_OK(conn_->ExecuteFormat(
562
0
      "CREATE INDEX NONCONCURRENTLY $0 ON $1 (i)",
563
0
      kIndexName,
564
0
      kTableName));
565
566
  // If the index used backfill, it would have incremented the table schema version by two or three:
567
  // - add index info with INDEX_PERM_DELETE_ONLY
568
  // - update to INDEX_PERM_DO_BACKFILL (as part of issue #6218)
569
  // - update to INDEX_PERM_READ_WRITE_AND_DELETE
570
  // If the index did not use backfill, it would have incremented the table schema version by one:
571
  // - add index info with no DocDB permission (default INDEX_PERM_READ_WRITE_AND_DELETE)
572
  // Expect that it did not use backfill.
573
0
  {
574
0
    Synchronizer sync;
575
0
    ASSERT_OK(client->GetTableSchemaById(table_id, info, sync.AsStatusCallback()));
576
0
    ASSERT_OK(sync.Wait());
577
0
  }
578
0
  ASSERT_EQ(info->schema.version(), 1);
579
0
}
580
581
class PgIndexBackfillTestSimultaneously : public PgIndexBackfillTest {
582
 public:
583
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
584
0
    options->extra_tserver_flags.push_back(
585
0
        Format("--ysql_pg_conf_csv=yb_index_state_flags_update_delay=$0",
586
0
               kIndexStateFlagsUpdateDelay.ToMilliseconds()));
587
0
  }
588
 protected:
589
#ifdef NDEBUG // release build; see issue #6238
590
  const MonoDelta kIndexStateFlagsUpdateDelay = 5s;
591
#else // NDEBUG
592
  const MonoDelta kIndexStateFlagsUpdateDelay = 1s;
593
#endif // NDEBUG
594
};
595
596
// Test simultaneous CREATE INDEX.
597
TEST_F_EX(PgIndexBackfillTest,
598
          YB_DISABLE_TEST_IN_TSAN(CreateIndexSimultaneously),
599
0
          PgIndexBackfillTestSimultaneously) {
600
0
  const std::string query = Format("SELECT * FROM $0 WHERE i = $1", kTableName, 7);
601
0
  constexpr int kNumRows = 10;
602
0
  constexpr int kNumThreads = 5;
603
0
  int expected_schema_version = 0;
604
0
  TestThreadHolder thread_holder;
605
606
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
607
0
  ASSERT_OK(conn_->ExecuteFormat(
608
0
      "INSERT INTO $0 VALUES (generate_series(1, $1))",
609
0
      kTableName,
610
0
      kNumRows));
611
612
0
  std::array<Status, kNumThreads> statuses;
613
0
  for (int i = 0; i < kNumThreads; ++i) {
614
0
    thread_holder.AddThreadFunctor([i, this, &statuses] {
615
0
      LOG(INFO) << "Begin thread " << i;
616
0
      PGConn create_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
617
0
      statuses[i] = MoveStatus(create_conn.ExecuteFormat(
618
0
          "CREATE INDEX $0 ON $1 (i)",
619
0
          kIndexName, kTableName));
620
0
    });
621
0
  }
622
0
  thread_holder.JoinAll();
623
624
0
  LOG(INFO) << "Inspecting statuses";
625
0
  int num_ok = 0;
626
0
  ASSERT_EQ(statuses.size(), kNumThreads);
627
0
  for (const auto& status : statuses) {
628
0
    if (status.ok()) {
629
0
      num_ok++;
630
0
      LOG(INFO) << "got ok status";
631
      // Success index creations do two schema changes:
632
      // - add index with INDEX_PERM_WRITE_AND_DELETE
633
      // - transition to success INDEX_PERM_READ_WRITE_AND_DELETE
634
      // TODO(jason): change this when closing #6218 because DO_BACKFILL permission will add another
635
      // schema version.
636
0
      expected_schema_version += 2;
637
0
    } else {
638
0
      ASSERT_TRUE(status.IsNetworkError()) << status;
639
0
      const std::string msg = status.message().ToBuffer();
640
0
      const std::string relation_already_exists_msg = Format(
641
0
          "relation \"$0\" already exists", kIndexName);
642
0
      const std::vector<std::string> allowed_msgs{
643
0
        "Catalog Version Mismatch",
644
0
        "Conflicts with higher priority transaction",
645
0
        "Restart read required",
646
0
        "Transaction aborted",
647
0
        "Transaction metadata missing",
648
0
        "Unknown transaction, could be recently aborted",
649
0
        relation_already_exists_msg,
650
0
      };
651
0
      ASSERT_TRUE(std::find_if(
652
0
          std::begin(allowed_msgs),
653
0
          std::end(allowed_msgs),
654
0
          [&msg] (const std::string allowed_msg) {
655
0
            return msg.find(allowed_msg) != std::string::npos;
656
0
          }) != std::end(allowed_msgs))
657
0
        << status;
658
0
      LOG(INFO) << "ignoring conflict error: " << status.message().ToBuffer();
659
0
      if (msg.find("Restart read required") == std::string::npos
660
0
          && msg.find(relation_already_exists_msg) == std::string::npos) {
661
        // Failed index creations do two schema changes:
662
        // - add index with INDEX_PERM_WRITE_AND_DELETE
663
        // - remove index because of DDL transaction rollback ("Table transaction failed, deleting")
664
0
        expected_schema_version += 2;
665
0
      } else {
666
        // If the DocDB index was never created in the first place, it incurs no schema changes.
667
0
      }
668
0
    }
669
0
  }
670
0
  ASSERT_EQ(num_ok, 1) << "only one CREATE INDEX should succeed";
671
672
0
  LOG(INFO) << "Checking postgres schema";
673
0
  {
674
    // Check number of indexes.
675
0
    PGResultPtr res = ASSERT_RESULT(conn_->FetchFormat(
676
0
        "SELECT indexname FROM pg_indexes WHERE tablename = '$0'", kTableName));
677
0
    ASSERT_EQ(PQntuples(res.get()), 1);
678
0
    const std::string actual = ASSERT_RESULT(GetString(res.get(), 0, 0));
679
0
    ASSERT_EQ(actual, kIndexName);
680
681
    // Check whether index is public using index scan.
682
0
    ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
683
0
  }
684
0
  LOG(INFO) << "Checking DocDB schema";
685
0
  std::vector<TableId> orphaned_docdb_index_ids;
686
0
  {
687
0
    auto client = ASSERT_RESULT(cluster_->CreateClient());
688
0
    const std::string table_id = ASSERT_RESULT(GetTableIdByTableName(
689
0
        client.get(), kDatabaseName, kTableName));
690
0
    std::shared_ptr<client::YBTableInfo> table_info = std::make_shared<client::YBTableInfo>();
691
0
    Synchronizer sync;
692
0
    ASSERT_OK(client->GetTableSchemaById(table_id, table_info, sync.AsStatusCallback()));
693
0
    ASSERT_OK(sync.Wait());
694
695
    // Check number of DocDB indexes.  Normally, failed indexes should be cleaned up ("Table
696
    // transaction failed, deleting"), but in the event of an unexpected issue, they may not be.
697
    // (Not necessarily a fatal issue because the postgres schema is good.)
698
0
    auto num_docdb_indexes = table_info->index_map.size();
699
0
    if (num_docdb_indexes > 1) {
700
0
      LOG(INFO) << "found " << num_docdb_indexes << " DocDB indexes";
701
      // These failed indexes not getting rolled back mean one less schema change each.  Therefore,
702
      // adjust the expected schema version.
703
0
      auto num_failed_docdb_indexes = num_docdb_indexes - 1;
704
0
      expected_schema_version -= num_failed_docdb_indexes;
705
0
    }
706
707
    // Check index permissions.  Also collect orphaned DocDB indexes.
708
0
    int num_rwd = 0;
709
0
    for (const auto& pair : table_info->index_map) {
710
0
      VLOG(1) << "table id: " << pair.first;
711
0
      IndexPermissions perm = pair.second.index_permissions();
712
0
      if (perm == IndexPermissions::INDEX_PERM_READ_WRITE_AND_DELETE) {
713
0
        num_rwd++;
714
0
      } else {
715
0
        ASSERT_EQ(perm, IndexPermissions::INDEX_PERM_WRITE_AND_DELETE);
716
0
        orphaned_docdb_index_ids.emplace_back(pair.first);
717
0
      }
718
0
    }
719
0
    ASSERT_EQ(num_rwd, 1)
720
0
        << "found " << num_rwd << " fully created (readable) DocDB indexes: expected " << 1;
721
722
    // Check schema version.
723
0
    ASSERT_EQ(table_info->schema.version(), expected_schema_version)
724
0
        << "got indexed table schema version " << table_info->schema.version()
725
0
        << ": expected " << expected_schema_version;
726
    // At least one index must have tried to create but gotten aborted, resulting in +1 or +2
727
    // catalog version bump.  The 2 below is for the successfully created index.
728
0
    ASSERT_GT(expected_schema_version, 2);
729
0
  }
730
731
0
  LOG(INFO) << "Checking if index still works";
732
0
  {
733
0
    ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
734
0
    PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
735
0
    ASSERT_EQ(PQntuples(res.get()), 1);
736
0
    int32_t value = ASSERT_RESULT(GetInt32(res.get(), 0, 0));
737
0
    ASSERT_EQ(value, 7);
738
0
  }
739
0
}
740
741
// Make sure that backfill works in a tablegroup.
742
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(Tablegroup)) {
743
0
  const std::string kTablegroupName = "test_tgroup";
744
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLEGROUP $0", kTablegroupName));
745
746
0
  TestSimpleBackfill(Format("TABLEGROUP $0", kTablegroupName));
747
0
}
748
749
// Test that retain_delete_markers is properly set after index backfill.
750
0
TEST_F(PgIndexBackfillTest, YB_DISABLE_TEST_IN_TSAN(RetainDeleteMarkers)) {
751
0
  TestRetainDeleteMarkers(kDatabaseName);
752
0
}
753
754
// Override the index backfill test to do alter slowly.
755
class PgIndexBackfillAlterSlowly : public PgIndexBackfillTest {
756
 public:
757
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
758
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
759
0
    options->extra_tserver_flags.push_back("--TEST_alter_schema_delay_ms=10000");
760
0
  }
761
};
762
763
// Test whether IsCreateTableDone works when creating an index with backfill enabled.  See issue
764
// #6234.
765
TEST_F_EX(PgIndexBackfillTest,
766
          YB_DISABLE_TEST_IN_TSAN(IsCreateTableDone),
767
0
          PgIndexBackfillAlterSlowly) {
768
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
769
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX ON $0 (i)", kTableName));
770
0
}
771
772
// Override the index backfill test to have different HBA config:
773
// 1. if any user tries to access the authdb database, enforce md5 auth
774
// 2. if the postgres user tries to access the yugabyte database, allow it
775
// 3. if the yugabyte user tries to access the yugabyte database, allow it
776
// 4. otherwise, disallow it
777
class PgIndexBackfillAuth : public PgIndexBackfillTest {
778
 public:
779
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
780
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
781
0
    options->extra_tserver_flags.push_back(Format(
782
0
        "--ysql_hba_conf="
783
0
        "host $0 all all md5,"
784
0
        "host $1 postgres all trust,"
785
0
        "host $1 yugabyte all trust",
786
0
        kAuthDbName,
787
0
        kDatabaseName));
788
0
  }
789
790
  const std::string kAuthDbName = "authdb";
791
};
792
793
// Test backfill on clusters where the yugabyte role has authentication enabled.
794
TEST_F_EX(PgIndexBackfillTest,
795
          YB_DISABLE_TEST_IN_TSAN(Auth),
796
0
          PgIndexBackfillAuth) {
797
0
  LOG(INFO) << "create " << this->kAuthDbName << " database";
798
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE DATABASE $0", this->kAuthDbName));
799
800
0
  LOG(INFO) << "backfill table on " << this->kAuthDbName << " database";
801
0
  {
802
0
    const std::string& host = pg_ts->bind_host();
803
0
    const uint16_t port = pg_ts->pgsql_rpc_port();
804
805
0
    PGConn auth_conn = ASSERT_RESULT(ConnectUsingString(Format(
806
0
        "user=$0 password=$1 host=$2 port=$3 dbname=$4",
807
0
        "yugabyte",
808
0
        "yugabyte",
809
0
        host,
810
0
        port,
811
0
        this->kAuthDbName)));
812
0
    ASSERT_OK(auth_conn.ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
813
0
    ASSERT_OK(auth_conn.ExecuteFormat("CREATE INDEX ON $0 (i)", kTableName));
814
0
  }
815
0
}
816
817
// Override the index backfill test to have HBA config with local trust:
818
// 1. if any user tries to connect over ip, trust
819
// 2. if any user tries to connect over unix-domain socket, trust
820
class PgIndexBackfillLocalTrust : public PgIndexBackfillTest {
821
 public:
822
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
823
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
824
0
    options->extra_tserver_flags.push_back(Format(
825
0
        "--ysql_hba_conf="
826
0
        "host $0 all all trust,"
827
0
        "local $0 all trust",
828
0
        kDatabaseName));
829
0
  }
830
};
831
832
// Make sure backfill works when there exists user-defined HBA configuration with "local".
833
// This is for issue (#7705).
834
TEST_F_EX(PgIndexBackfillTest,
835
          YB_DISABLE_TEST_IN_TSAN(LocalTrustSimple),
836
0
          PgIndexBackfillLocalTrust) {
837
0
  TestSimpleBackfill();
838
0
}
839
840
// Override the index backfill test to disable transparent retries on cache version mismatch.
841
class PgIndexBackfillNoRetry : public PgIndexBackfillTest {
842
 public:
843
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
844
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
845
0
    options->extra_tserver_flags.push_back(
846
0
        "--TEST_ysql_disable_transparent_cache_refresh_retry=true");
847
0
  }
848
};
849
850
TEST_F_EX(PgIndexBackfillTest,
851
          YB_DISABLE_TEST_IN_TSAN(DropNoRetry),
852
0
          PgIndexBackfillNoRetry) {
853
0
  constexpr int kNumRows = 5;
854
855
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int, j int)", kTableName));
856
0
  ASSERT_OK(conn_->ExecuteFormat(
857
0
      "INSERT INTO $0 VALUES (generate_series(1, $1), generate_series(11, 10 + $1))",
858
0
      kTableName,
859
0
      kNumRows));
860
861
  // Create index.
862
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX $0 ON $1 (i ASC)", kIndexName, kTableName));
863
864
  // Update the table cache entry for the indexed table.
865
0
  ASSERT_OK(conn_->FetchFormat("SELECT * FROM $0", kTableName));
866
867
  // Drop index.
868
0
  ASSERT_OK(conn_->ExecuteFormat("DROP INDEX $0", kIndexName));
869
870
  // Ensure that there is no schema version mismatch for the indexed table.  This is because the
871
  // above `DROP INDEX` should have invalidated the corresponding table cache entry.  (There also
872
  // should be no catalog version mismatch because it is updated for the same session after DDL.)
873
0
  ASSERT_OK(conn_->FetchFormat("SELECT * FROM $0", kTableName));
874
0
}
875
876
// Override the index backfill test to have delays for testing snapshot too old.
877
class PgIndexBackfillSnapshotTooOld : public PgIndexBackfillTest {
878
 public:
879
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
880
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
881
0
    options->extra_tserver_flags.push_back("--TEST_slowdown_backfill_by_ms=10000");
882
0
    options->extra_tserver_flags.push_back(
883
0
        "--ysql_pg_conf_csv=yb_index_state_flags_update_delay=0");
884
0
    options->extra_tserver_flags.push_back("--timestamp_history_retention_interval_sec=3");
885
0
  }
886
};
887
888
// Make sure that index backfill doesn't care about snapshot too old.  Force a situation where the
889
// indexed table scan for backfill would occur after the committed history cutoff.  A compaction is
890
// needed to update this committed history cutoff, and the retention period needs to be low enough
891
// so that the cutoff is ahead of backfill's safe read time.  See issue #6333.
892
TEST_F_EX(PgIndexBackfillTest,
893
          YB_DISABLE_TEST_IN_TSAN(SnapshotTooOld),
894
0
          PgIndexBackfillSnapshotTooOld) {
895
0
  auto client = ASSERT_RESULT(cluster_->CreateClient());
896
0
  constexpr int kTimeoutSec = 3;
897
0
  TestThreadHolder thread_holder;
898
899
  // (Make it one tablet for simplicity.)
900
0
  LOG(INFO) << "Create table...";
901
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (c char) SPLIT INTO 1 TABLETS", kTableName));
902
903
0
  LOG(INFO) << "Get table id for indexed table...";
904
0
  const std::string table_id = ASSERT_RESULT(GetTableIdByTableName(
905
0
      client.get(), kDatabaseName, kTableName));
906
907
  // Insert something so that reading it would trigger snapshot too old.
908
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES ('s')", kTableName));
909
910
  // conn_ should be used by at most one thread for thread safety.
911
0
  thread_holder.AddThreadFunctor([this] {
912
0
    LOG(INFO) << "Begin create thread";
913
0
    LOG(INFO) << "Create index...";
914
0
    Status s = conn_->ExecuteFormat("CREATE INDEX $0 ON $1 (c)", kIndexName, kTableName);
915
0
    if (!s.ok()) {
916
      // We are doomed to fail the test.  Before that, let's see if it turns out to be "snapshot too
917
      // old" or some other unexpected error.
918
0
      ASSERT_TRUE(s.IsNetworkError()) << "got unexpected error: " << s;
919
0
      ASSERT_TRUE(s.message().ToBuffer().find("Snapshot too old") != std::string::npos)
920
0
          << "got unexpected error: " << s;
921
      // It is "snapshot too old".  Fail now.
922
0
      FAIL() << "got snapshot too old: " << s;
923
0
    }
924
0
  });
925
0
  thread_holder.AddThreadFunctor([&client, &table_id] {
926
0
    LOG(INFO) << "Begin compact thread";
927
    // Sleep until we are in the interval
928
    //   (read_time + history_retention_interval, read_time + slowdown_backfill)
929
    // = (read_time + 3s, read_time + 10s)
930
    // Choose read_time + 5s.
931
0
    LOG(INFO) << "Sleep...";
932
0
    SleepFor(1s); // approximate setup time before getting to the backfill stage
933
0
    SleepFor(5s);
934
935
0
    LOG(INFO) << "Flush and compact indexed table...";
936
0
    ASSERT_OK(client->FlushTables(
937
0
        {table_id},
938
0
        false /* add_indexes */,
939
0
        kTimeoutSec,
940
0
        false /* is_compaction */));
941
0
    ASSERT_OK(client->FlushTables(
942
0
        {table_id},
943
0
        false /* add_indexes */,
944
0
        kTimeoutSec,
945
0
        true /* is_compaction */));
946
0
  });
947
0
  thread_holder.JoinAll();
948
0
}
949
950
// Override the index backfill test to have slower backfill-related operations
951
class PgIndexBackfillSlow : public PgIndexBackfillTest {
952
 public:
953
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
954
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
955
0
    options->extra_master_flags.push_back(Format(
956
0
        "--TEST_slowdown_backfill_alter_table_rpcs_ms=$0",
957
0
        kBackfillAlterTableDelay.ToMilliseconds()));
958
0
    options->extra_tserver_flags.push_back(Format(
959
0
        "--ysql_pg_conf_csv=yb_index_state_flags_update_delay=$0",
960
0
        kIndexStateFlagsUpdateDelay.ToMilliseconds()));
961
0
    options->extra_tserver_flags.push_back(Format(
962
0
        "--TEST_slowdown_backfill_by_ms=$0",
963
0
        kBackfillDelay.ToMilliseconds()));
964
0
  }
965
966
 protected:
967
  Result<bool> IsAtTargetIndexStateFlags(
968
      const std::string& index_name,
969
0
      const IndexStateFlags& target_index_state_flags) {
970
0
    Result<IndexStateFlags> res = GetIndexStateFlags(index_name);
971
0
    IndexStateFlags actual_index_state_flags;
972
0
    if (res.ok()) {
973
0
      actual_index_state_flags = res.get();
974
0
    } else if (res.status().IsNotFound()) {
975
0
      LOG(WARNING) << res.status();
976
0
      return false;
977
0
    } else {
978
0
      return res.status();
979
0
    }
980
981
0
    if (actual_index_state_flags < target_index_state_flags) {
982
0
      LOG(INFO) << index_name
983
0
                << " not yet at target index state flags "
984
0
                << ToString(target_index_state_flags);
985
0
      return false;
986
0
    } else if (actual_index_state_flags > target_index_state_flags) {
987
0
      return STATUS(RuntimeError,
988
0
                    Format("$0 exceeded target index state flags $1",
989
0
                           index_name,
990
0
                           target_index_state_flags));
991
0
    }
992
0
    return true;
993
0
  }
994
995
  CHECKED_STATUS WaitForBackfillSafeTime(
996
0
      const client::YBTableName& table_name, const std::string& index_name) {
997
0
    LOG(INFO) << "Waiting for pg_index indislive to be true";
998
0
    RETURN_NOT_OK(WaitFor(
999
0
        [this, &index_name] {
1000
0
          return IsAtTargetIndexStateFlags(index_name, IndexStateFlags{IndexStateFlag::kIndIsLive});
1001
0
        },
1002
0
        kCreateIndexStartupGracePeriod,
1003
0
        "Wait for pg_index indislive=true",
1004
0
        MonoDelta::FromMilliseconds(test_util::kDefaultInitialWaitMs),
1005
0
        test_util::kDefaultWaitDelayMultiplier,
1006
0
        kMaxDelay));
1007
1008
0
    LOG(INFO) << "Waiting for pg_index indisready to be true";
1009
0
    RETURN_NOT_OK(WaitFor(
1010
0
        [this, &index_name] {
1011
0
          return IsAtTargetIndexStateFlags(
1012
0
              index_name, IndexStateFlags{IndexStateFlag::kIndIsLive, IndexStateFlag::kIndIsReady});
1013
0
        },
1014
0
        kIndexStateFlagsUpdateDelay + kIndexStateFlagsUpdateGracePeriod,
1015
0
        "Wait for pg_index indisready=true",
1016
0
        kIndexStateFlagsUpdateDelay - kMaxDelay /* initial_delay */,
1017
0
        test_util::kDefaultWaitDelayMultiplier,
1018
0
        kMaxDelay));
1019
1020
0
    LOG(INFO) << "Waiting till (approx) the end of the delay after committing indisready true";
1021
0
    SleepFor(kIndexStateFlagsUpdateDelay);
1022
1023
0
    auto client = VERIFY_RESULT(cluster_->CreateClient());
1024
0
    const std::string table_id = VERIFY_RESULT(
1025
0
        GetTableIdByTableName(client.get(), table_name.namespace_name(), table_name.table_name()));
1026
0
    RETURN_NOT_OK(WaitForBackfillSafeTimeOn(
1027
0
        cluster_->GetLeaderMasterProxy<master::MasterDdlProxy>(), table_id));
1028
1029
0
    return Status::OK();
1030
0
  }
1031
1032
  // buffer times for unexpected situations.
1033
  const MonoDelta kCreateIndexStartupGracePeriod = 30s;
1034
  const MonoDelta kIndexStateFlagsUpdateGracePeriod = 5s;
1035
1036
  // gflag delay times.
1037
  const MonoDelta kBackfillAlterTableDelay = 0s;
1038
  const MonoDelta kBackfillDelay = RegularBuildVsSanitizers(3s, 7s);
1039
  const MonoDelta kIndexStateFlagsUpdateDelay = RegularBuildVsSanitizers(3s, 7s);
1040
1041
  // maximum delay between checks on index state flags.
1042
  const MonoDelta kMaxDelay = 100ms;
1043
1044
  TestThreadHolder thread_holder_;
1045
1046
 private:
1047
0
  Result<IndexStateFlags> GetIndexStateFlags(const std::string& index_name) {
1048
0
    const std::string quoted_index_name = PqEscapeLiteral(index_name);
1049
1050
0
    PGResultPtr res = VERIFY_RESULT(conn_->FetchFormat(
1051
0
        "SELECT indislive, indisready, indisvalid"
1052
0
        " FROM pg_class INNER JOIN pg_index ON pg_class.oid = pg_index.indexrelid"
1053
0
        " WHERE pg_class.relname = $0",
1054
0
        quoted_index_name));
1055
0
    if (PQntuples(res.get()) == 0) {
1056
0
      return STATUS_FORMAT(NotFound, "$0 not found in pg_class and/or pg_index", quoted_index_name);
1057
0
    }
1058
0
    if (int num_cols = PQnfields(res.get()) != 3) {
1059
0
      return STATUS_FORMAT(Corruption, "got unexpected number of columns: $0", num_cols);
1060
0
    }
1061
1062
0
    IndexStateFlags index_state_flags;
1063
0
    if (VERIFY_RESULT(GetBool(res.get(), 0, 0))) {
1064
0
      index_state_flags.Set(IndexStateFlag::kIndIsLive);
1065
0
    }
1066
0
    if (VERIFY_RESULT(GetBool(res.get(), 0, 1))) {
1067
0
      index_state_flags.Set(IndexStateFlag::kIndIsReady);
1068
0
    }
1069
0
    if (VERIFY_RESULT(GetBool(res.get(), 0, 2))) {
1070
0
      index_state_flags.Set(IndexStateFlag::kIndIsValid);
1071
0
    }
1072
1073
0
    return index_state_flags;
1074
0
  }
1075
};
1076
1077
// Make sure that read time (and write time) for backfill works.  Simulate the following:
1078
//   Session A                                    Session B
1079
//   --------------------------                   ---------------------------------
1080
//   CREATE INDEX
1081
//   - indislive
1082
//   - indisready
1083
//   - backfill
1084
//     - get safe time for read
1085
//                                                UPDATE a row of the indexed table
1086
//     - do the actual backfill
1087
//   - indisvalid
1088
// The backfill should use the values before update when writing to the index.  The update should
1089
// write and delete to the index because of permissions.  Since backfill writes with an ancient
1090
// timestamp, the update should appear to have happened after the backfill.
1091
TEST_F_EX(PgIndexBackfillTest,
1092
          YB_DISABLE_TEST_IN_TSAN(ReadTime),
1093
0
          PgIndexBackfillSlow) {
1094
0
  ASSERT_OK(conn_->ExecuteFormat(
1095
0
      "CREATE TABLE $0 (i int, j int, PRIMARY KEY (i ASC))", kTableName));
1096
0
  ASSERT_OK(conn_->ExecuteFormat(
1097
0
      "INSERT INTO $0 VALUES (generate_series(0, 5), generate_series(10, 15))", kTableName));
1098
1099
  // conn_ should be used by at most one thread for thread safety.
1100
0
  thread_holder_.AddThreadFunctor([this] {
1101
0
    LOG(INFO) << "Begin create thread";
1102
0
    PGConn create_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
1103
0
    ASSERT_OK(create_conn.ExecuteFormat("CREATE INDEX $0 ON $1 (j ASC)", kIndexName, kTableName));
1104
0
  });
1105
0
  thread_holder_.AddThreadFunctor([this] {
1106
0
    LOG(INFO) << "Begin write thread";
1107
0
    ASSERT_OK(WaitForBackfillSafeTime(kYBTableName, kIndexName));
1108
1109
0
    LOG(INFO) << "Updating row";
1110
0
    ASSERT_OK(conn_->ExecuteFormat("UPDATE $0 SET j = j + 100 WHERE i = 3", kTableName));
1111
0
    LOG(INFO) << "Done updating row";
1112
1113
    // It should still be in the backfill stage, hopefully before the actual backfill started.
1114
0
    ASSERT_TRUE(ASSERT_RESULT(IsAtTargetIndexStateFlags(
1115
0
        kIndexName, IndexStateFlags{IndexStateFlag::kIndIsLive, IndexStateFlag::kIndIsReady})));
1116
0
  });
1117
0
  thread_holder_.JoinAll();
1118
1119
  // Index scan to verify contents of index table.
1120
0
  const std::string query = Format("SELECT * FROM $0 WHERE j = 113", kTableName);
1121
0
  ASSERT_OK(WaitFor(
1122
0
      [this, &query] {
1123
0
        return conn_->HasIndexScan(query);
1124
0
      },
1125
0
      kIndexStateFlagsUpdateGracePeriod + kIndexStateFlagsUpdateDelay,
1126
0
      "Wait for IndexScan"));
1127
0
  PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
1128
0
  int lines = PQntuples(res.get());
1129
0
  ASSERT_EQ(1, lines);
1130
0
  int columns = PQnfields(res.get());
1131
0
  ASSERT_EQ(2, columns);
1132
0
  int32_t key = ASSERT_RESULT(GetInt32(res.get(), 0, 0));
1133
0
  ASSERT_EQ(key, 3);
1134
  // Make sure that the update is visible.
1135
0
  int32_t value = ASSERT_RESULT(GetInt32(res.get(), 0, 1));
1136
0
  ASSERT_EQ(value, 113);
1137
0
}
1138
1139
// Make sure that updates at each stage of multi-stage CREATE INDEX work.  Simulate the following:
1140
//   Session A                                    Session B
1141
//   --------------------------                   ---------------------------------
1142
//   CREATE INDEX
1143
//   - indislive
1144
//                                                UPDATE a row of the indexed table
1145
//   - indisready
1146
//                                                UPDATE a row of the indexed table
1147
//   - indisvalid
1148
//                                                UPDATE a row of the indexed table
1149
// Updates should succeed and get written to the index.
1150
TEST_F_EX(PgIndexBackfillTest,
1151
          YB_DISABLE_TEST_IN_TSAN(Permissions),
1152
0
          PgIndexBackfillSlow) {
1153
0
  const CoarseDuration kThreadWaitTime = 60s;
1154
0
  const std::array<std::pair<IndexStateFlags, int>, 3> index_state_flags_key_pairs = {
1155
0
    std::make_pair(IndexStateFlags{IndexStateFlag::kIndIsLive}, 2),
1156
0
    std::make_pair(IndexStateFlags{IndexStateFlag::kIndIsLive, IndexStateFlag::kIndIsReady}, 3),
1157
0
    std::make_pair(IndexStateFlags{
1158
0
        IndexStateFlag::kIndIsLive,
1159
0
        IndexStateFlag::kIndIsReady,
1160
0
        IndexStateFlag::kIndIsValid,
1161
0
      }, 4),
1162
0
  };
1163
0
  std::atomic<int> updates(0);
1164
1165
0
  ASSERT_OK(conn_->ExecuteFormat(
1166
0
      "CREATE TABLE $0 (i int, j int, PRIMARY KEY (i ASC))", kTableName));
1167
0
  ASSERT_OK(conn_->ExecuteFormat(
1168
0
      "INSERT INTO $0 VALUES (generate_series(0, 5), generate_series(10, 15))", kTableName));
1169
1170
  // conn_ should be used by at most one thread for thread safety.
1171
0
  thread_holder_.AddThreadFunctor([this] {
1172
0
    LOG(INFO) << "Begin create thread";
1173
0
    PGConn create_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
1174
0
    ASSERT_OK(create_conn.ExecuteFormat("CREATE INDEX $0 ON $1 (j ASC)", kIndexName, kTableName));
1175
0
  });
1176
0
  thread_holder_.AddThreadFunctor([this, &index_state_flags_key_pairs, &updates] {
1177
0
    LOG(INFO) << "Begin write thread";
1178
0
    for (const auto& pair : index_state_flags_key_pairs) {
1179
0
      const IndexStateFlags& index_state_flags = pair.first;
1180
0
      int key = pair.second;
1181
1182
0
      MonoDelta timeout;
1183
0
      if (index_state_flags == IndexStateFlags{IndexStateFlag::kIndIsLive}) {
1184
0
        timeout = kCreateIndexStartupGracePeriod;
1185
0
      } else if (index_state_flags == IndexStateFlags{
1186
0
            IndexStateFlag::kIndIsLive,
1187
0
            IndexStateFlag::kIndIsReady,
1188
0
          }) {
1189
0
        timeout = kIndexStateFlagsUpdateDelay + kIndexStateFlagsUpdateGracePeriod;
1190
0
      } else {
1191
0
        ASSERT_TRUE((index_state_flags == IndexStateFlags{
1192
0
            IndexStateFlag::kIndIsLive,
1193
0
            IndexStateFlag::kIndIsReady,
1194
0
            IndexStateFlag::kIndIsValid,
1195
0
          }));
1196
0
        timeout = ((kIndexStateFlagsUpdateDelay + kIndexStateFlagsUpdateGracePeriod)
1197
0
                   + (kBackfillAlterTableDelay * 2)
1198
0
                   + (kBackfillDelay + kIndexStateFlagsUpdateGracePeriod));
1199
0
      }
1200
0
      ASSERT_OK(WaitFor(
1201
0
          [this, &index_state_flags] {
1202
0
            return IsAtTargetIndexStateFlags(kIndexName, index_state_flags);
1203
0
          },
1204
0
          timeout,
1205
0
          Format("get index state flags: $0", index_state_flags)));
1206
0
      LOG(INFO) << "running UPDATE on i = " << key;
1207
0
      ASSERT_OK(conn_->ExecuteFormat("UPDATE $0 SET j = j + 100 WHERE i = $1", kTableName, key));
1208
0
      LOG(INFO) << "done running UPDATE on i = " << key;
1209
1210
      // Make sure permission didn't change yet.
1211
0
      ASSERT_TRUE(ASSERT_RESULT(IsAtTargetIndexStateFlags(kIndexName, index_state_flags)));
1212
0
      updates++;
1213
0
    }
1214
0
  });
1215
0
  thread_holder_.WaitAndStop(kThreadWaitTime);
1216
1217
0
  ASSERT_EQ(updates.load(std::memory_order_acquire), index_state_flags_key_pairs.size());
1218
1219
0
  for (const auto& pair : index_state_flags_key_pairs) {
1220
0
    int key = pair.second;
1221
1222
    // Verify contents of index table.
1223
0
    const std::string query = Format(
1224
0
        "WITH j_idx AS (SELECT * FROM $0 ORDER BY j) SELECT j FROM j_idx WHERE i = $1",
1225
0
        kTableName,
1226
0
        key);
1227
0
    ASSERT_OK(WaitFor(
1228
0
        [this, &query] {
1229
0
          return conn_->HasIndexScan(query);
1230
0
        },
1231
0
        kIndexStateFlagsUpdateGracePeriod + kIndexStateFlagsUpdateDelay,
1232
0
        "Wait for IndexScan"));
1233
0
    PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
1234
0
    int lines = PQntuples(res.get());
1235
0
    ASSERT_EQ(1, lines);
1236
0
    int columns = PQnfields(res.get());
1237
0
    ASSERT_EQ(1, columns);
1238
    // Make sure that the update is visible.
1239
0
    int value = ASSERT_RESULT(GetInt32(res.get(), 0, 0));
1240
0
    ASSERT_EQ(value, key + 110);
1241
0
  }
1242
0
}
1243
1244
// Make sure that writes during CREATE UNIQUE INDEX don't cause unique duplicate row errors to be
1245
// thrown.  Simulate the following:
1246
//   Session A                                    Session B
1247
//   --------------------------                   ---------------------------------
1248
//                                                INSERT a row to the indexed table
1249
//   CREATE UNIQUE INDEX
1250
//                                                INSERT a row to the indexed table
1251
//   - indislive
1252
//                                                INSERT a row to the indexed table
1253
//   - indisready
1254
//                                                INSERT a row to the indexed table
1255
//   - backfill
1256
//                                                INSERT a row to the indexed table
1257
//   - indisvalid
1258
//                                                INSERT a row to the indexed table
1259
// Particularly pay attention to the insert between indisready and backfill.  The insert
1260
// should cause a write to go to the index.  Backfill should choose a read time after this write, so
1261
// it should try to backfill this same row.  Rather than conflicting when we see the row already
1262
// exists in the index during backfill, check whether the rows match, and don't error if they do.
1263
TEST_F_EX(PgIndexBackfillTest,
1264
          YB_DISABLE_TEST_IN_TSAN(CreateUniqueIndexWithOnlineWrites),
1265
0
          PgIndexBackfillSlow) {
1266
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
1267
1268
  // Start a thread that continuously inserts distinct values.  The hope is that this would cause
1269
  // inserts to happen at all permissions.
1270
0
  thread_holder_.AddThreadFunctor([this, &stop = thread_holder_.stop_flag()] {
1271
0
    LOG(INFO) << "Begin write thread";
1272
0
    PGConn insert_conn = ASSERT_RESULT(Connect());
1273
0
    int i = 0;
1274
0
    while (!stop.load(std::memory_order_acquire)) {
1275
0
      Status status = insert_conn.ExecuteFormat("INSERT INTO $0 VALUES ($1)", kTableName, ++i);
1276
0
      if (!status.ok()) {
1277
        // Ignore transient errors that likely occur when changing index permissions.
1278
        // TODO(jason): no longer expect schema version mismatch errors after closing issue #3979.
1279
0
        ASSERT_TRUE(status.IsNetworkError()) << status;
1280
0
        std::string msg = status.message().ToBuffer();
1281
0
        const std::vector<std::string> allowed_msgs{
1282
0
          "Errors occurred while reaching out to the tablet servers",
1283
0
          "Resource unavailable : RocksDB",
1284
0
          "schema version mismatch",
1285
0
          "Transaction aborted",
1286
0
          "expired or aborted by a conflict",
1287
0
          "Transaction was recently aborted",
1288
0
        };
1289
0
        ASSERT_TRUE(std::find_if(
1290
0
            std::begin(allowed_msgs),
1291
0
            std::end(allowed_msgs),
1292
0
            [&msg] (const std::string allowed_msg) {
1293
0
              return msg.find(allowed_msg) != std::string::npos;
1294
0
            }) != std::end(allowed_msgs))
1295
0
          << status;
1296
0
        LOG(WARNING) << "ignoring transient error: " << status.message().ToBuffer();
1297
0
      }
1298
0
    }
1299
0
  });
1300
1301
  // Create unique index (should not complain about duplicate row).
1302
0
  LOG(INFO) << "Create unique index...";
1303
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE UNIQUE INDEX ON $0 (i ASC)", kTableName));
1304
1305
0
  thread_holder_.Stop();
1306
0
}
1307
1308
// Simulate the following:
1309
//   Session A                                    Session B
1310
//   ------------------------------------         -------------------------------------------
1311
//   CREATE TABLE (i, j, PRIMARY KEY (i))
1312
//                                                INSERT (1, 'a')
1313
//   CREATE UNIQUE INDEX (j)
1314
//   - DELETE_ONLY perm
1315
//                                                DELETE (1, 'a')
1316
//                                                (delete (1, 'a') to index)
1317
//                                                INSERT (2, 'a')
1318
//   - WRITE_DELETE perm
1319
//   - BACKFILL perm
1320
//     - get safe time for read
1321
//                                                INSERT (3, 'a')
1322
//                                                (insert (3, 'a') to index)
1323
//     - do the actual backfill
1324
//                                                (insert (2, 'a') to index--detect conflict)
1325
//   - READ_WRITE_DELETE perm
1326
// This test is for issue #6208.
1327
TEST_F_EX(PgIndexBackfillTest,
1328
          YB_DISABLE_TEST_IN_TSAN(CreateUniqueIndexWriteAfterSafeTime),
1329
0
          PgIndexBackfillSlow) {
1330
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int, j char, PRIMARY KEY (i))", kTableName));
1331
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES (1, 'a')", kTableName));
1332
1333
  // conn_ should be used by at most one thread for thread safety.
1334
0
  thread_holder_.AddThreadFunctor([this] {
1335
0
    LOG(INFO) << "Begin create thread";
1336
0
    LOG(INFO) << "Creating index...";
1337
0
    PGConn create_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
1338
0
    Status s = create_conn.ExecuteFormat(
1339
0
        "CREATE UNIQUE INDEX $0 ON $1 (j ASC)", kIndexName, kTableName);
1340
0
    ASSERT_NOK(s);
1341
0
    ASSERT_TRUE(s.IsNetworkError());
1342
0
    ASSERT_TRUE(s.message().ToBuffer().find("duplicate key value") != std::string::npos) << s;
1343
0
  });
1344
0
  thread_holder_.AddThreadFunctor([this] {
1345
0
    LOG(INFO) << "Begin write thread";
1346
0
    {
1347
0
      const IndexStateFlags index_state_flags{IndexStateFlag::kIndIsLive};
1348
1349
0
      LOG(INFO) << "Wait for indislive index state flag";
1350
0
      ASSERT_OK(WaitFor(
1351
0
          [this, &index_state_flags] {
1352
0
            return IsAtTargetIndexStateFlags(kIndexName, index_state_flags);
1353
0
          },
1354
0
          kCreateIndexStartupGracePeriod,
1355
0
          Format("get index state flags: $0", index_state_flags)));
1356
1357
0
      LOG(INFO) << "Do delete and insert";
1358
0
      ASSERT_OK(conn_->ExecuteFormat("DELETE FROM $0 WHERE i = 1", kTableName));
1359
0
      ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES (2, 'a')", kTableName));
1360
1361
0
      LOG(INFO) << "Check we're not yet at indisready index state flag";
1362
0
      ASSERT_TRUE(ASSERT_RESULT(IsAtTargetIndexStateFlags(kIndexName, index_state_flags)));
1363
0
    }
1364
1365
0
    {
1366
0
      const IndexStateFlags index_state_flags{
1367
0
        IndexStateFlag::kIndIsLive,
1368
0
        IndexStateFlag::kIndIsReady,
1369
0
      };
1370
1371
0
      LOG(INFO) << "Wait for indisready index state flag";
1372
0
      ASSERT_OK(WaitFor(
1373
0
          [this, &index_state_flags] {
1374
0
            return IsAtTargetIndexStateFlags(kIndexName, index_state_flags);
1375
0
          },
1376
0
          kIndexStateFlagsUpdateDelay + kIndexStateFlagsUpdateGracePeriod,
1377
0
          Format("get index state flags: $0", index_state_flags)));
1378
0
    }
1379
1380
0
    {
1381
0
      LOG(INFO) << "Wait for backfill (approx)";
1382
0
      SleepFor(kIndexStateFlagsUpdateDelay);
1383
1384
0
      LOG(INFO) << "Wait to get safe time for backfill (approx)";
1385
0
      SleepFor(kBackfillDelay / 2);
1386
1387
0
      LOG(INFO) << "Do insert";
1388
0
      CoarseBackoffWaiter waiter(
1389
0
          CoarseMonoClock::Now() + (kBackfillDelay / 2 + kIndexStateFlagsUpdateGracePeriod),
1390
0
          CoarseMonoClock::Duration::max());
1391
0
      while (true) {
1392
0
        Status status = conn_->ExecuteFormat("INSERT INTO $0 VALUES (3, 'a')", kTableName);
1393
0
        LOG(INFO) << "Got " << yb::ToString(status);
1394
0
        if (status.ok()) {
1395
0
          break;
1396
0
        } else {
1397
0
          ASSERT_FALSE(status.IsIllegalState() &&
1398
0
                       status.message().ToBuffer().find("Duplicate value") != std::string::npos)
1399
0
              << "The insert should come before backfill, so it should not cause duplicate"
1400
0
              << " conflict.";
1401
0
          ASSERT_TRUE(waiter.Wait());
1402
0
        }
1403
0
      }
1404
0
    }
1405
0
  });
1406
0
  thread_holder_.JoinAll();
1407
1408
  // Check.
1409
0
  {
1410
0
    CoarseBackoffWaiter waiter(CoarseMonoClock::Now() + 10s, CoarseMonoClock::Duration::max());
1411
0
    while (true) {
1412
0
      Result<PGResultPtr> result = conn_->FetchFormat("SELECT count(*) FROM $0", kTableName);
1413
0
      if (result.ok()) {
1414
0
        PGResultPtr res = std::move(*result);
1415
0
        const int64_t main_table_size = ASSERT_RESULT(GetInt64(res.get(), 0, 0));
1416
0
        ASSERT_EQ(main_table_size, 2);
1417
0
        break;
1418
0
      }
1419
0
      ASSERT_TRUE(result.status().IsQLError()) << result.status();
1420
0
      ASSERT_TRUE(result.status().message().ToBuffer().find("schema version mismatch")
1421
0
                  != std::string::npos) << result.status();
1422
0
      ASSERT_TRUE(waiter.Wait());
1423
0
    }
1424
0
  }
1425
0
}
1426
1427
// Simulate the following:
1428
//   Session A                                    Session B
1429
//   ------------------------------------         -------------------------------------------
1430
//   CREATE TABLE (i, j, PRIMARY KEY (i))
1431
//                                                INSERT (1, 'a')
1432
//   CREATE UNIQUE INDEX (j)
1433
//   - indislive
1434
//   - indisready
1435
//   - backfill stage
1436
//     - get safe time for read
1437
//                                                DELETE (1, 'a')
1438
//                                                (delete (1, 'a') to index)
1439
//     - do the actual backfill
1440
//       (insert (1, 'a') to index)
1441
//   - indisvalid
1442
// This test is for issue #6811.  Remember, backfilled rows get written with write time = safe time,
1443
// so they should have an MVCC timestamp lower than that of the deletion.  If deletes to the index
1444
// aren't written, then this test will always fail because the backfilled row has no delete to cover
1445
// it.  If deletes to the index aren't retained, then this test will fail if compactions get rid of
1446
// the delete before the backfilled row gets written.
1447
TEST_F_EX(PgIndexBackfillTest,
1448
          YB_DISABLE_TEST_IN_TSAN(RetainDeletes),
1449
0
          PgIndexBackfillSlow) {
1450
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int, j char, PRIMARY KEY (i))", kTableName));
1451
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES (1, 'a')", kTableName));
1452
1453
  // conn_ should be used by at most one thread for thread safety.
1454
0
  thread_holder_.AddThreadFunctor([this] {
1455
0
    LOG(INFO) << "Begin create thread";
1456
0
    LOG(INFO) << "Creating index";
1457
0
    PGConn create_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
1458
0
    ASSERT_OK(create_conn.ExecuteFormat(
1459
0
        "CREATE UNIQUE INDEX $0 ON $1 (j ASC)", kIndexName, kTableName));
1460
0
  });
1461
0
  thread_holder_.AddThreadFunctor([this] {
1462
0
    LOG(INFO) << "Begin write thread";
1463
0
    ASSERT_OK(WaitForBackfillSafeTime(kYBTableName, kIndexName));
1464
1465
0
    LOG(INFO) << "Deleting row";
1466
0
    ASSERT_OK(conn_->ExecuteFormat("DELETE FROM $0 WHERE i = 1", kTableName));
1467
1468
    // It should still be in the backfill stage, hopefully before the actual backfill started.
1469
0
    ASSERT_TRUE(ASSERT_RESULT(IsAtTargetIndexStateFlags(
1470
0
        kIndexName, IndexStateFlags{IndexStateFlag::kIndIsLive, IndexStateFlag::kIndIsReady})));
1471
0
  });
1472
0
  thread_holder_.JoinAll();
1473
1474
  // Check.
1475
0
  const Result<PGResultPtr>& result = conn_->FetchFormat(
1476
0
      "SELECT count(*) FROM $0 WHERE j = 'a'", kTableName);
1477
0
  if (result.ok()) {
1478
0
    auto count = ASSERT_RESULT(GetInt64(result.get().get(), 0, 0));
1479
0
    ASSERT_EQ(count, 0);
1480
0
  } else if (result.status().IsNetworkError()) {
1481
0
    Status s = result.status();
1482
0
    const std::string msg = s.message().ToBuffer();
1483
0
    if (msg.find("Given ybctid is not associated with any row in table") == std::string::npos) {
1484
0
      FAIL() << "unexpected status: " << s;
1485
0
    }
1486
0
    FAIL() << "delete to index was not present by the time backfill happened: " << s;
1487
0
  } else {
1488
0
    Status s = result.status();
1489
0
    FAIL() << "unexpected status: " << s;
1490
0
  }
1491
0
}
1492
1493
TEST_F_EX(PgIndexBackfillTest,
1494
          YB_DISABLE_TEST_IN_TSAN(IndexScanVisibility),
1495
0
          PgIndexBackfillSlow) {
1496
0
  ExternalTabletServer* diff_ts = cluster_->tablet_server(1);
1497
  // Make sure default tserver is 0.  At the time of writing, this is set in
1498
  // PgWrapperTestBase::SetUp.
1499
0
  ASSERT_NE(pg_ts, diff_ts);
1500
1501
0
  LOG(INFO) << "Create connection to run CREATE INDEX";
1502
0
  PGConn create_index_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
1503
0
  LOG(INFO) << "Create connection to the same tablet server as the one running CREATE INDEX";
1504
0
  PGConn same_ts_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
1505
0
  LOG(INFO) << "Create connection to a different tablet server from the one running CREATE INDEX";
1506
0
  PGConn diff_ts_conn = ASSERT_RESULT(PGConn::Connect(
1507
0
      HostPort(diff_ts->bind_host(), diff_ts->pgsql_rpc_port()),
1508
0
      kDatabaseName));
1509
1510
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
1511
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES (1)", kTableName));
1512
1513
0
  thread_holder_.AddThreadFunctor([this, &same_ts_conn, &diff_ts_conn] {
1514
0
    LOG(INFO) << "Begin select thread";
1515
0
    ASSERT_OK(WaitForBackfillSafeTime(kYBTableName, kIndexName));
1516
1517
0
    LOG(INFO) << "Load DocDB table/index schemas to pggate cache for the other connections";
1518
0
    ASSERT_RESULT(same_ts_conn.FetchFormat("SELECT * FROM $0 WHERE i = 2", kTableName));
1519
0
    ASSERT_RESULT(diff_ts_conn.FetchFormat("SELECT * FROM $0 WHERE i = 2", kTableName));
1520
0
  });
1521
1522
0
  LOG(INFO) << "Create index...";
1523
0
  ASSERT_OK(create_index_conn.ExecuteFormat("CREATE INDEX $0 ON $1 (i)", kIndexName, kTableName));
1524
0
  ASSERT_TRUE(thread_holder_.stop_flag())
1525
0
      << "select thread did not finish by the time CREATE INDEX ended";
1526
0
  CoarseTimePoint start_time = CoarseMonoClock::Now();
1527
1528
0
  LOG(INFO) << "Check for index scan...";
1529
0
  const std::string query = Format("SELECT * FROM $0 WHERE i = 2", kTableName);
1530
  // The session that ran CREATE INDEX should immediately be ready for index scan.
1531
0
  ASSERT_TRUE(ASSERT_RESULT(create_index_conn.HasIndexScan(query)));
1532
  // Eventually, the other sessions should see the index as public.  They may take some time because
1533
  // they don't know about the latest catalog update until
1534
  // 1. master sends catalog version through heartbeat to tserver
1535
  // 2. tserver shares catalog version to postgres through shared memory
1536
  // Another avenue to learn that the index is public is to send a request to tserver and get a
1537
  // schema version mismatch on the indexed table.  Since HasIndexScan uses EXPLAIN, it doesn't hit
1538
  // tserver, so postgres will be unaware until catalog version is updated in shared memory.  Expect
1539
  // 0s-1s since default heartbeat period is 1s (see flag heartbeat_interval_ms).
1540
0
  ASSERT_OK(WaitFor(
1541
0
      [&query, &same_ts_conn, &diff_ts_conn]() -> Result<bool> {
1542
0
        bool same_ts_has_index_scan = VERIFY_RESULT(same_ts_conn.HasIndexScan(query));
1543
0
        bool diff_ts_has_index_scan = VERIFY_RESULT(diff_ts_conn.HasIndexScan(query));
1544
0
        LOG(INFO) << "same_ts_has_index_scan: " << same_ts_has_index_scan
1545
0
                  << ", "
1546
0
                  << "diff_ts_has_index_scan: " << diff_ts_has_index_scan;
1547
0
        return same_ts_has_index_scan && diff_ts_has_index_scan;
1548
0
      },
1549
0
      kIndexStateFlagsUpdateGracePeriod + kIndexStateFlagsUpdateDelay,
1550
0
      "Wait for IndexScan"));
1551
0
  LOG(INFO) << "It took " << yb::ToString(CoarseMonoClock::Now() - start_time)
1552
0
            << " for other sessions to notice that the index became public";
1553
0
}
1554
1555
// Override the index backfill slow test to have smaller WaitUntilIndexPermissionsAtLeast deadline.
1556
class PgIndexBackfillSlowClientDeadline : public PgIndexBackfillSlow {
1557
 public:
1558
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
1559
0
    PgIndexBackfillSlow::UpdateMiniClusterOptions(options);
1560
0
    options->extra_tserver_flags.push_back(Format(
1561
0
        "--backfill_index_client_rpc_timeout_ms=$0",
1562
0
        (kBackfillDelay / 2).ToMilliseconds()));
1563
0
  }
1564
};
1565
1566
// Make sure that the postgres timeout when waiting for backfill to finish causes the index to not
1567
// become public.  Simulate the following:
1568
//   CREATE INDEX
1569
//   - indislive
1570
//   - indisready
1571
//   - backfill
1572
//     - get safe time for read
1573
//   - (timeout)
1574
TEST_F_EX(PgIndexBackfillTest,
1575
          YB_DISABLE_TEST_IN_TSAN(WaitBackfillTimeout),
1576
0
          PgIndexBackfillSlowClientDeadline) {
1577
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
1578
0
  Status status = conn_->ExecuteFormat("CREATE INDEX ON $0 (i)", kTableName);
1579
0
  ASSERT_TRUE(HasClientTimedOut(status)) << status;
1580
1581
  // Make sure that the index is not public.
1582
0
  ASSERT_FALSE(ASSERT_RESULT(conn_->HasIndexScan(Format(
1583
0
      "SELECT * FROM $0 WHERE i = 1",
1584
0
      kTableName))));
1585
0
}
1586
1587
// Make sure that you can still drop an index that failed to fully create.
1588
TEST_F_EX(PgIndexBackfillTest,
1589
          YB_DISABLE_TEST_IN_TSAN(DropAfterFail),
1590
0
          PgIndexBackfillSlowClientDeadline) {
1591
0
  auto client = ASSERT_RESULT(cluster_->CreateClient());
1592
0
  google::protobuf::RepeatedPtrField<master::TabletLocationsPB> tablets;
1593
1594
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
1595
0
  Status status = conn_->ExecuteFormat("CREATE INDEX $0 ON $1 (i)", kIndexName, kTableName);
1596
0
  ASSERT_TRUE(HasClientTimedOut(status)) << status;
1597
1598
  // Make sure that the index exists in DocDB metadata.
1599
0
  auto tables = ASSERT_RESULT(client->ListTables());
1600
0
  bool found = false;
1601
0
  for (const auto& table : tables) {
1602
0
    if (table.namespace_name() == kDatabaseName && table.table_name() == kIndexName) {
1603
0
      found = true;
1604
0
      break;
1605
0
    }
1606
0
  }
1607
0
  ASSERT_TRUE(found);
1608
1609
0
  ASSERT_OK(conn_->ExecuteFormat("DROP INDEX $0", kIndexName));
1610
1611
  // Make sure that the index is gone.
1612
  // Check postgres metadata.
1613
0
  auto res = ASSERT_RESULT(conn_->FetchFormat(
1614
0
      "SELECT COUNT(*) FROM pg_class WHERE relname = '$0'", kIndexName));
1615
0
  int64_t value = ASSERT_RESULT(GetInt64(res.get(), 0, 0));
1616
0
  ASSERT_EQ(value, 0);
1617
  // Check DocDB metadata.
1618
0
  tables = ASSERT_RESULT(client->ListTables());
1619
0
  for (const auto& table : tables) {
1620
0
    ASSERT_FALSE(table.namespace_name() == kDatabaseName && table.table_name() == kIndexName);
1621
0
  }
1622
0
}
1623
1624
// Override the index backfill slow test class to have a 30s BackfillIndex client timeout.
1625
class PgIndexBackfillFastClientTimeout : public PgIndexBackfillSlow {
1626
 public:
1627
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
1628
0
    PgIndexBackfillSlow::UpdateMiniClusterOptions(options);
1629
0
    options->extra_tserver_flags.push_back("--backfill_index_client_rpc_timeout_ms=30000");
1630
0
  }
1631
};
1632
1633
// Make sure that DROP INDEX during backfill is handled well.  Simulate the following:
1634
//   Session A                                    Session B
1635
//   --------------------------                   ----------------------
1636
//   CREATE INDEX
1637
//   - indislive
1638
//   - indisready
1639
//   - backfill
1640
//     - get safe time for read
1641
//                                                DROP INDEX
1642
TEST_F_EX(PgIndexBackfillTest,
1643
          YB_DISABLE_TEST_IN_TSAN(DropWhileBackfilling),
1644
0
          PgIndexBackfillFastClientTimeout) {
1645
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
1646
1647
  // conn_ should be used by at most one thread for thread safety.
1648
0
  thread_holder_.AddThreadFunctor([this] {
1649
0
    LOG(INFO) << "Begin create thread";
1650
0
    PGConn create_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
1651
0
    Status status = create_conn.ExecuteFormat("CREATE INDEX $0 ON $1 (i)", kIndexName, kTableName);
1652
    // Expect timeout because
1653
    // DROP INDEX is currently not online and removes the index info from the indexed table
1654
    // ==> the WaitUntilIndexPermissionsAtLeast will keep failing and retrying GetTableSchema on the
1655
    // index.
1656
0
    ASSERT_TRUE(HasClientTimedOut(status)) << status;
1657
0
  });
1658
0
  thread_holder_.AddThreadFunctor([this] {
1659
0
    LOG(INFO) << "Begin drop thread";
1660
0
    ASSERT_OK(WaitForBackfillSafeTime(kYBTableName, kIndexName));
1661
1662
0
    LOG(INFO) << "Drop index";
1663
0
    ASSERT_OK(conn_->ExecuteFormat("DROP INDEX $0", kIndexName));
1664
0
  });
1665
0
  thread_holder_.JoinAll();
1666
0
}
1667
1668
// Override the index backfill test class to have a default client admin timeout one second smaller
1669
// than backfill delay.  Also, ensure client backfill timeout is high, and set num_tablets to 1 to
1670
// make the test finish more quickly.
1671
class PgIndexBackfillFastDefaultClientTimeout : public PgIndexBackfillTest {
1672
 public:
1673
0
  void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override {
1674
0
    PgIndexBackfillTest::UpdateMiniClusterOptions(options);
1675
0
    options->extra_tserver_flags.push_back(Format(
1676
0
        "--TEST_slowdown_backfill_by_ms=$0",
1677
0
        kBackfillDelay.ToMilliseconds()));
1678
0
    options->extra_tserver_flags.push_back(Format(
1679
0
        "--yb_client_admin_operation_timeout_sec=$0", (kBackfillDelay - 1s).ToSeconds()));
1680
0
    options->extra_tserver_flags.push_back("--backfill_index_client_rpc_timeout_ms=60000"); // 1m
1681
0
    options->extra_tserver_flags.push_back("--ysql_num_tablets=1");
1682
0
  }
1683
 protected:
1684
  const MonoDelta kBackfillDelay = RegularBuildVsSanitizers(7s, 14s);
1685
};
1686
1687
// Simply create table and index.  The CREATE INDEX should not timeout during backfill because the
1688
// BackfillIndex request from postgres should use the backfill_index_client_rpc_timeout_ms timeout
1689
// (default 60m) rather than the small yb_client_admin_operation_timeout_sec.
1690
TEST_F_EX(PgIndexBackfillTest,
1691
          YB_DISABLE_TEST_IN_TSAN(LowerDefaultClientTimeout),
1692
0
          PgIndexBackfillFastDefaultClientTimeout) {
1693
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
1694
  // This should not time out.
1695
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX ON $0 (i)", kTableName));
1696
0
}
1697
1698
// Override the index backfill fast client timeout test class to have more than one master.
1699
class PgIndexBackfillMultiMaster : public PgIndexBackfillFastClientTimeout {
1700
 public:
1701
0
  int GetNumMasters() const override { return 3; }
1702
};
1703
1704
// Make sure that master leader change during backfill causes the index to not become public and
1705
// doesn't cause any weird hangups or other issues.  Simulate the following:
1706
//   Session A                                    Session B
1707
//   --------------------------                   ----------------------
1708
//   CREATE INDEX
1709
//   - indislive
1710
//   - indisready
1711
//   - backfill
1712
//     - get safe time for read
1713
//                                                master leader stepdown
1714
// TODO(jason): update this test when handling master leader changes during backfill (issue #6218).
1715
TEST_F_EX(PgIndexBackfillTest,
1716
          YB_DISABLE_TEST_IN_TSAN(MasterLeaderStepdown),
1717
0
          PgIndexBackfillMultiMaster) {
1718
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
1719
1720
  // conn_ should be used by at most one thread for thread safety.
1721
0
  thread_holder_.AddThreadFunctor([this] {
1722
0
    LOG(INFO) << "Begin create thread";
1723
0
    PGConn create_conn = ASSERT_RESULT(ConnectToDB(kDatabaseName));
1724
    // The CREATE INDEX should get master leader change during backfill so that its
1725
    // WaitUntilIndexPermissionsAtLeast call starts querying the new leader.  Since the new leader
1726
    // will be inactive at the WRITE_AND_DELETE docdb permission, it will wait until the deadline,
1727
    // which is set to 30s.
1728
0
    Status status = create_conn.ExecuteFormat("CREATE INDEX $0 ON $1 (i)", kIndexName, kTableName);
1729
0
    ASSERT_TRUE(HasClientTimedOut(status)) << status;
1730
0
  });
1731
0
  thread_holder_.AddThreadFunctor([this] {
1732
0
    LOG(INFO) << "Begin master leader stepdown thread";
1733
0
    ASSERT_OK(WaitForBackfillSafeTime(kYBTableName, kIndexName));
1734
1735
0
    LOG(INFO) << "Doing master leader stepdown";
1736
0
    tserver::TabletServerErrorPB::Code error_code;
1737
0
    ASSERT_OK(cluster_->StepDownMasterLeader(&error_code));
1738
1739
    // It should still be in the backfill stage, hopefully before the actual backfill started.
1740
0
    ASSERT_TRUE(ASSERT_RESULT(IsAtTargetIndexStateFlags(
1741
0
        kIndexName, IndexStateFlags{IndexStateFlag::kIndIsLive, IndexStateFlag::kIndIsReady})));
1742
0
  });
1743
0
  thread_holder_.JoinAll();
1744
0
}
1745
1746
// Override the index backfill test class to use colocated tables.
1747
class PgIndexBackfillColocated : public PgIndexBackfillTest {
1748
 public:
1749
0
  void SetUp() override {
1750
0
    LibPqTestBase::SetUp();
1751
1752
0
    PGConn conn_init = ASSERT_RESULT(Connect());
1753
0
    ASSERT_OK(conn_init.ExecuteFormat("CREATE DATABASE $0 WITH colocated = true", kColoDbName));
1754
1755
0
    conn_ = std::make_unique<PGConn>(ASSERT_RESULT(ConnectToDB(kColoDbName)));
1756
0
  }
1757
};
1758
1759
// Make sure that backfill works when colocation is on.
1760
TEST_F_EX(PgIndexBackfillTest,
1761
          YB_DISABLE_TEST_IN_TSAN(ColocatedSimple),
1762
0
          PgIndexBackfillColocated) {
1763
0
  TestSimpleBackfill();
1764
0
}
1765
1766
// Make sure that backfill works when there are multiple colocated tables.
1767
TEST_F_EX(PgIndexBackfillTest,
1768
          YB_DISABLE_TEST_IN_TSAN(ColocatedMultipleTables),
1769
0
          PgIndexBackfillColocated) {
1770
  // Create two tables with the index on the second table.
1771
0
  const std::string kOtherTable = "yyy";
1772
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kOtherTable));
1773
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES (100)", kOtherTable));
1774
1775
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE TABLE $0 (i int)", kTableName));
1776
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES (200)", kTableName));
1777
0
  ASSERT_OK(conn_->ExecuteFormat("INSERT INTO $0 VALUES (300)", kTableName));
1778
0
  ASSERT_OK(conn_->ExecuteFormat("CREATE INDEX ON $0 (i ASC)", kTableName));
1779
1780
  // Index scan to verify contents of index table.
1781
0
  const std::string query = Format("SELECT COUNT(*) FROM $0 WHERE i > 0", kTableName);
1782
0
  ASSERT_TRUE(ASSERT_RESULT(conn_->HasIndexScan(query)));
1783
0
  PGResultPtr res = ASSERT_RESULT(conn_->Fetch(query));
1784
0
  auto count = ASSERT_RESULT(GetInt64(res.get(), 0, 0));
1785
0
  ASSERT_EQ(count, 2);
1786
0
}
1787
1788
// Test that retain_delete_markers is properly set after index backfill for a colocated table.
1789
TEST_F_EX(PgIndexBackfillTest,
1790
          YB_DISABLE_TEST_IN_TSAN(ColocatedRetainDeleteMarkers),
1791
0
          PgIndexBackfillColocated) {
1792
0
  TestRetainDeleteMarkers(kColoDbName);
1793
0
}
1794
1795
} // namespace pgwrapper
1796
} // namespace yb