couchbase 3.4.1 → 3.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/couchbase/CMakeLists.txt +2 -0
  4. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +4 -0
  5. data/ext/couchbase/core/cluster_options.hxx +0 -1
  6. data/ext/couchbase/core/config_profile.cxx +23 -1
  7. data/ext/couchbase/core/config_profile.hxx +2 -12
  8. data/ext/couchbase/core/impl/analytics.cxx +236 -0
  9. data/ext/couchbase/core/impl/cluster.cxx +0 -1
  10. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +5 -3
  11. data/ext/couchbase/core/impl/query.cxx +5 -5
  12. data/ext/couchbase/core/io/dns_client.cxx +225 -0
  13. data/ext/couchbase/core/io/dns_client.hxx +19 -188
  14. data/ext/couchbase/core/transactions/active_transaction_record.hxx +2 -2
  15. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +3 -0
  16. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +1 -1
  17. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +12 -12
  18. data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +7 -1
  19. data/ext/couchbase/core/transactions/transaction_context.cxx +1 -0
  20. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +144 -155
  21. data/ext/couchbase/core/utils/connection_string.cxx +10 -3
  22. data/ext/couchbase/core/utils/connection_string.hxx +3 -3
  23. data/ext/couchbase/couchbase/analytics_error_context.hxx +143 -0
  24. data/ext/couchbase/couchbase/analytics_meta_data.hxx +155 -0
  25. data/ext/couchbase/couchbase/analytics_metrics.hxx +163 -0
  26. data/ext/couchbase/couchbase/analytics_options.hxx +359 -0
  27. data/ext/couchbase/couchbase/analytics_result.hxx +102 -0
  28. data/ext/couchbase/couchbase/analytics_scan_consistency.hxx +46 -0
  29. data/ext/couchbase/couchbase/analytics_status.hxx +41 -0
  30. data/ext/couchbase/couchbase/analytics_warning.hxx +85 -0
  31. data/ext/couchbase/couchbase/cluster.hxx +33 -0
  32. data/ext/couchbase/couchbase/fmt/analytics_status.hxx +76 -0
  33. data/ext/couchbase/couchbase/query_options.hxx +0 -1
  34. data/ext/couchbase/couchbase/scope.hxx +33 -0
  35. data/ext/couchbase/couchbase/transactions/attempt_context.hxx +1 -1
  36. data/ext/couchbase/test/CMakeLists.txt +1 -2
  37. data/ext/couchbase/test/test_helper.hxx +1 -1
  38. data/ext/couchbase/test/test_integration_analytics.cxx +289 -13
  39. data/ext/couchbase/test/test_integration_crud.cxx +8 -1
  40. data/ext/couchbase/test/test_integration_examples.cxx +41 -0
  41. data/ext/couchbase/test/test_integration_management.cxx +15 -3
  42. data/ext/couchbase/test/test_integration_search.cxx +601 -0
  43. data/ext/couchbase/test/test_transaction_transaction_simple.cxx +73 -0
  44. data/ext/couchbase/test/test_unit_config_profiles.cxx +12 -12
  45. data/ext/couchbase/test/test_unit_connection_string.cxx +35 -0
  46. data/ext/couchbase/third_party/snappy/CMakeLists.txt +150 -27
  47. data/ext/couchbase/third_party/snappy/cmake/config.h.in +28 -24
  48. data/ext/couchbase/third_party/snappy/snappy-internal.h +189 -25
  49. data/ext/couchbase/third_party/snappy/snappy-sinksource.cc +26 -9
  50. data/ext/couchbase/third_party/snappy/snappy-sinksource.h +11 -11
  51. data/ext/couchbase/third_party/snappy/snappy-stubs-internal.cc +1 -1
  52. data/ext/couchbase/third_party/snappy/snappy-stubs-internal.h +227 -308
  53. data/ext/couchbase/third_party/snappy/snappy-stubs-public.h.in +0 -11
  54. data/ext/couchbase/third_party/snappy/snappy.cc +1176 -410
  55. data/ext/couchbase/third_party/snappy/snappy.h +19 -4
  56. data/ext/couchbase.cxx +27 -6
  57. data/ext/revisions.rb +3 -3
  58. data/lib/couchbase/cluster.rb +13 -9
  59. data/lib/couchbase/cluster_registry.rb +7 -2
  60. data/lib/couchbase/configuration.rb +3 -4
  61. data/lib/couchbase/options.rb +85 -2
  62. data/lib/couchbase/search_options.rb +158 -240
  63. data/lib/couchbase/version.rb +1 -1
  64. metadata +17 -6
  65. data/ext/couchbase/core/CMakeLists.txt +0 -0
@@ -17,7 +17,7 @@
17
17
 
18
18
  #include "core/operations/management/analytics.hxx"
19
19
  #include "core/operations/management/collection_create.hxx"
20
- #include "core/operations/management/scope_create.hxx"
20
+ #include "core/operations/management/collections.hxx"
21
21
  #include "test_helper_integration.hxx"
22
22
 
23
23
  TEST_CASE("integration: analytics query")
@@ -78,7 +78,7 @@ TEST_CASE("integration: analytics query")
78
78
  REQUIRE(test::utils::wait_until([&]() {
79
79
  couchbase::core::operations::analytics_request req{};
80
80
  req.statement = fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = ?)", dataset_name);
81
- req.positional_parameters.emplace_back(couchbase::core::json_string(couchbase::core::utils::json::generate(test_value)));
81
+ req.positional_parameters.emplace_back(couchbase::core::utils::json::generate(test_value));
82
82
  resp = test::utils::execute(integration.cluster, req);
83
83
  return resp.rows.size() == 1;
84
84
  }));
@@ -130,10 +130,30 @@ TEST_CASE("integration: analytics query")
130
130
 
131
131
  SECTION("consistency")
132
132
  {
133
- couchbase::core::operations::analytics_request req{};
134
- req.statement = fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = "{}")", dataset_name, test_value);
135
- req.scan_consistency = couchbase::core::analytics_scan_consistency::request_plus;
136
- auto resp = test::utils::execute(integration.cluster, req);
133
+ couchbase::core::operations::analytics_response resp{};
134
+ CHECK(test::utils::wait_until([&]() {
135
+ /*
136
+ * In consistency test, always do fresh mutation
137
+ */
138
+ test_value = test::utils::uniq_id("value");
139
+ value = couchbase::core::utils::json::generate({ { "testkey", test_value } });
140
+ {
141
+ auto id = couchbase::core::document_id(integration.ctx.bucket, "_default", "_default", key);
142
+ couchbase::core::operations::upsert_request req{ id, couchbase::core::utils::to_binary(value) };
143
+ REQUIRE_SUCCESS(test::utils::execute(integration.cluster, req).ctx.ec());
144
+ }
145
+
146
+ couchbase::core::operations::analytics_request req{};
147
+ req.statement = fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = "{}")", dataset_name, test_value);
148
+ req.scan_consistency = couchbase::core::analytics_scan_consistency::request_plus;
149
+ resp = test::utils::execute(integration.cluster, req);
150
+ /* Analytics might give us code 23027, ignore it here
151
+ *
152
+ * "errors": [{"code": 23027, "msg": "Bucket default on link Default.Local is not connected"} ],
153
+ */
154
+ return resp.ctx.first_error_code != 23027;
155
+ }));
156
+
137
157
  REQUIRE_SUCCESS(resp.ctx.ec);
138
158
  REQUIRE(resp.rows.size() == 1);
139
159
  REQUIRE(resp.rows[0] == value);
@@ -185,13 +205,15 @@ TEST_CASE("integration: analytics scope query")
185
205
  REQUIRE(created);
186
206
  }
187
207
 
188
- {
189
- couchbase::core::operations::analytics_request req{};
190
- req.statement =
191
- fmt::format("ALTER COLLECTION `{}`.`{}`.`{}` ENABLE ANALYTICS", integration.ctx.bucket, scope_name, collection_name);
192
- auto resp = test::utils::execute(integration.cluster, req);
193
- REQUIRE_SUCCESS(resp.ctx.ec);
194
- }
208
+ CHECK(test::utils::wait_until(
209
+ [&]() {
210
+ couchbase::core::operations::analytics_request req{};
211
+ req.statement =
212
+ fmt::format("ALTER COLLECTION `{}`.`{}`.`{}` ENABLE ANALYTICS", integration.ctx.bucket, scope_name, collection_name);
213
+ auto resp = test::utils::execute(integration.cluster, req);
214
+ return !resp.ctx.ec;
215
+ },
216
+ std::chrono::minutes{ 5 }));
195
217
 
196
218
  auto key = test::utils::uniq_id("key");
197
219
  auto test_value = test::utils::uniq_id("value");
@@ -259,3 +281,257 @@ TEST_CASE("unit: analytics query")
259
281
  REQUIRE(http_req.headers.find("analytics-priority") == http_req.headers.end());
260
282
  }
261
283
  }
284
+
285
+ TEST_CASE("integration: public API analytics query")
286
+ {
287
+ test::utils::integration_test_guard integration;
288
+
289
+ if (!integration.cluster_version().supports_analytics()) {
290
+ return;
291
+ }
292
+
293
+ auto cluster = couchbase::cluster(integration.cluster);
294
+ auto bucket = cluster.bucket(integration.ctx.bucket);
295
+ auto collection = bucket.default_collection();
296
+
297
+ auto dataset_name = test::utils::uniq_id("dataset");
298
+
299
+ {
300
+ couchbase::core::operations::management::analytics_dataset_create_request req{};
301
+ req.dataset_name = dataset_name;
302
+ req.bucket_name = integration.ctx.bucket;
303
+ auto resp = test::utils::execute(integration.cluster, req);
304
+ REQUIRE_SUCCESS(resp.ctx.ec);
305
+ }
306
+
307
+ {
308
+ couchbase::core::operations::management::analytics_link_connect_request req{};
309
+ req.force = true;
310
+ auto resp = test::utils::execute(integration.cluster, req);
311
+ REQUIRE_SUCCESS(resp.ctx.ec);
312
+ }
313
+
314
+ auto key = test::utils::uniq_id("key");
315
+ auto test_value = test::utils::uniq_id("value");
316
+ tao::json::value document = {
317
+ { "testkey", test_value },
318
+ };
319
+ {
320
+ auto [ctx, resp] = collection.upsert(key, document).get();
321
+ REQUIRE_SUCCESS(ctx.ec());
322
+ }
323
+
324
+ SECTION("simple query")
325
+ {
326
+ couchbase::analytics_result resp{};
327
+ couchbase::analytics_error_context ctx{};
328
+ CHECK(test::utils::wait_until([&]() {
329
+ std::tie(ctx, resp) =
330
+ cluster.analytics_query(fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = "{}")", dataset_name, test_value))
331
+ .get();
332
+ return !ctx.ec() && resp.meta_data().metrics().result_count() == 1;
333
+ }));
334
+ REQUIRE_SUCCESS(ctx.ec());
335
+ REQUIRE_FALSE(resp.meta_data().request_id().empty());
336
+ REQUIRE_FALSE(resp.meta_data().client_context_id().empty());
337
+ REQUIRE(resp.meta_data().status() == couchbase::analytics_status::success);
338
+ auto rows = resp.rows_as_json();
339
+ REQUIRE(rows.size() == 1);
340
+ REQUIRE(rows[0] == document);
341
+ }
342
+
343
+ SECTION("positional params")
344
+ {
345
+ couchbase::analytics_result resp{};
346
+ couchbase::analytics_error_context ctx{};
347
+ CHECK(test::utils::wait_until([&]() {
348
+ std::tie(ctx, resp) = cluster
349
+ .analytics_query(fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = ?)", dataset_name),
350
+ couchbase::analytics_options{}.positional_parameters(test_value))
351
+ .get();
352
+ return !ctx.ec() && resp.meta_data().metrics().result_count() == 1;
353
+ }));
354
+ REQUIRE_SUCCESS(ctx.ec());
355
+ auto rows = resp.rows_as_json();
356
+ REQUIRE(rows.size() == 1);
357
+ REQUIRE(rows[0] == document);
358
+ }
359
+
360
+ SECTION("named params")
361
+ {
362
+ couchbase::analytics_result resp{};
363
+ couchbase::analytics_error_context ctx{};
364
+ CHECK(test::utils::wait_until([&]() {
365
+ std::tie(ctx, resp) =
366
+ cluster
367
+ .analytics_query(fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = $testkey)", dataset_name),
368
+ couchbase::analytics_options{}.named_parameters(std::pair{ "testkey", test_value }))
369
+ .get();
370
+ return !ctx.ec() && resp.meta_data().metrics().result_count() == 1;
371
+ }));
372
+ REQUIRE_SUCCESS(ctx.ec());
373
+ auto rows = resp.rows_as_json();
374
+ REQUIRE(rows.size() == 1);
375
+ REQUIRE(rows[0] == document);
376
+ }
377
+
378
+ SECTION("named params preformatted")
379
+ {
380
+ couchbase::analytics_result resp{};
381
+ couchbase::analytics_error_context ctx{};
382
+ CHECK(test::utils::wait_until([&]() {
383
+ std::tie(ctx, resp) =
384
+ cluster
385
+ .analytics_query(fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = $testkey)", dataset_name),
386
+ couchbase::analytics_options{}.encoded_named_parameters(
387
+ { { "testkey", couchbase::core::utils::json::generate_binary(test_value) } }))
388
+ .get();
389
+ return !ctx.ec() && resp.meta_data().metrics().result_count() == 1;
390
+ }));
391
+ REQUIRE_SUCCESS(ctx.ec());
392
+ auto rows = resp.rows_as_json();
393
+ REQUIRE(rows.size() == 1);
394
+ REQUIRE(rows[0] == document);
395
+ }
396
+
397
+ SECTION("raw")
398
+ {
399
+ couchbase::analytics_result resp{};
400
+ couchbase::analytics_error_context ctx{};
401
+ CHECK(test::utils::wait_until([&]() {
402
+ std::tie(ctx, resp) =
403
+ cluster
404
+ .analytics_query(fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = $testkey)", dataset_name),
405
+ couchbase::analytics_options{}.raw("$testkey", test_value))
406
+ .get();
407
+ return !ctx.ec() && resp.meta_data().metrics().result_count() == 1;
408
+ }));
409
+ REQUIRE_SUCCESS(ctx.ec());
410
+ auto rows = resp.rows_as_json();
411
+ REQUIRE(rows.size() == 1);
412
+ REQUIRE(rows[0] == document);
413
+ }
414
+
415
+ SECTION("consistency")
416
+ {
417
+ couchbase::analytics_result resp{};
418
+ couchbase::analytics_error_context ctx{};
419
+ CHECK(test::utils::wait_until([&]() {
420
+ /*
421
+ * In consistency test, always do fresh mutation
422
+ */
423
+ test_value = test::utils::uniq_id("value");
424
+ document = {
425
+ { "testkey", test_value },
426
+ };
427
+ {
428
+ auto [ctx2, _] = collection.upsert(key, document).get();
429
+ REQUIRE_SUCCESS(ctx2.ec());
430
+ }
431
+
432
+ std::tie(ctx, resp) =
433
+ cluster
434
+ .analytics_query(fmt::format(R"(SELECT testkey FROM `Default`.`{}` WHERE testkey = "{}")", dataset_name, test_value),
435
+ couchbase::analytics_options{}.scan_consistency(couchbase::analytics_scan_consistency::request_plus))
436
+ .get();
437
+ /* Analytics might give us code 23027, ignore it here
438
+ *
439
+ * "errors": [{"code": 23027, "msg": "Bucket default on link Default.Local is not connected"} ],
440
+ */
441
+ return ctx.first_error_code() != 23027;
442
+ }));
443
+
444
+ REQUIRE_SUCCESS(ctx.ec());
445
+ auto rows = resp.rows_as_json();
446
+ REQUIRE(rows.size() == 1);
447
+ REQUIRE(rows[0] == document);
448
+ }
449
+
450
+ SECTION("readonly")
451
+ {
452
+ auto [ctx, resp] =
453
+ cluster.analytics_query(fmt::format("DROP DATASET Default.`{}`", dataset_name), couchbase::analytics_options{}.readonly(true))
454
+ .get();
455
+
456
+ REQUIRE(ctx.ec() == couchbase::errc::common::internal_server_failure);
457
+ REQUIRE(resp.meta_data().status() == couchbase::analytics_status::fatal);
458
+ }
459
+
460
+ {
461
+ couchbase::core::operations::management::analytics_dataset_drop_request req{};
462
+ req.dataset_name = dataset_name;
463
+ test::utils::execute(integration.cluster, req);
464
+ }
465
+ }
466
+
467
+ TEST_CASE("integration: public API analytics scope query")
468
+ {
469
+ test::utils::integration_test_guard integration;
470
+
471
+ if (!integration.cluster_version().supports_analytics() || !integration.cluster_version().supports_collections()) {
472
+ return;
473
+ }
474
+
475
+ auto cluster = couchbase::cluster(integration.cluster);
476
+ auto bucket = cluster.bucket(integration.ctx.bucket);
477
+
478
+ auto scope_name = test::utils::uniq_id("scope");
479
+ auto collection_name = test::utils::uniq_id("collection");
480
+
481
+ {
482
+ const couchbase::core::operations::management::scope_create_request req{ integration.ctx.bucket, scope_name };
483
+ auto resp = test::utils::execute(integration.cluster, req);
484
+ REQUIRE_SUCCESS(resp.ctx.ec);
485
+ auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, integration.ctx.bucket, resp.uid);
486
+ REQUIRE(created);
487
+ }
488
+
489
+ {
490
+ const couchbase::core::operations::management::collection_create_request req{ integration.ctx.bucket, scope_name, collection_name };
491
+ auto resp = test::utils::execute(integration.cluster, req);
492
+ REQUIRE_SUCCESS(resp.ctx.ec);
493
+ auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, integration.ctx.bucket, resp.uid);
494
+ REQUIRE(created);
495
+ }
496
+
497
+ CHECK(test::utils::wait_until(
498
+ [&]() {
499
+ auto [ctx, resp] = cluster
500
+ .analytics_query(fmt::format(
501
+ "ALTER COLLECTION `{}`.`{}`.`{}` ENABLE ANALYTICS", integration.ctx.bucket, scope_name, collection_name))
502
+ .get();
503
+ return !ctx.ec();
504
+ },
505
+ std::chrono::minutes{ 5 }));
506
+
507
+ auto scope = bucket.scope(scope_name);
508
+ auto collection = scope.collection(collection_name);
509
+
510
+ auto key = test::utils::uniq_id("key");
511
+ auto test_value = test::utils::uniq_id("value");
512
+ const tao::json::value document = {
513
+ { "testkey", test_value },
514
+ };
515
+ {
516
+ auto [ctx, resp] = collection.upsert(key, document).get();
517
+ REQUIRE_SUCCESS(ctx.ec());
518
+ }
519
+
520
+ couchbase::analytics_result resp{};
521
+ couchbase::analytics_error_context ctx{};
522
+ CHECK(test::utils::wait_until([&]() {
523
+ std::tie(ctx, resp) =
524
+ scope.analytics_query(fmt::format(R"(SELECT testkey FROM `{}` WHERE testkey = "{}")", collection_name, test_value)).get();
525
+ return !ctx.ec() && resp.meta_data().metrics().result_count() == 1;
526
+ }));
527
+ REQUIRE_SUCCESS(ctx.ec());
528
+ REQUIRE(resp.rows_as_json()[0] == document);
529
+ REQUIRE_FALSE(resp.meta_data().request_id().empty());
530
+ REQUIRE_FALSE(resp.meta_data().client_context_id().empty());
531
+ REQUIRE(resp.meta_data().status() == couchbase::analytics_status::success);
532
+
533
+ {
534
+ const couchbase::core::operations::management::scope_drop_request req{ integration.ctx.bucket, scope_name };
535
+ test::utils::execute(integration.cluster, req);
536
+ }
537
+ }
@@ -228,6 +228,9 @@ TEST_CASE("integration: pessimistic locking", "[integration]")
228
228
  {
229
229
  couchbase::core::operations::get_and_lock_request req{ id };
230
230
  req.lock_time = lock_time;
231
+ if (integration.ctx.deployment == test::utils::deployment_type::capella) {
232
+ req.timeout = std::chrono::seconds{ 2 };
233
+ }
231
234
  auto resp = test::utils::execute(integration.cluster, req);
232
235
  REQUIRE(resp.ctx.ec() == couchbase::errc::common::ambiguous_timeout);
233
236
  REQUIRE(resp.ctx.retried_because_of(couchbase::retry_reason::key_value_locked));
@@ -810,7 +813,11 @@ TEST_CASE("integration: pessimistic locking with public API", "[integration]")
810
813
 
811
814
  // it is not allowed to lock the same key twice
812
815
  {
813
- auto [ctx, resp] = collection.get_and_lock(id, lock_time, {}).get();
816
+ couchbase::get_and_lock_options options{};
817
+ if (integration.ctx.deployment == test::utils::deployment_type::capella) {
818
+ options.timeout(std::chrono::seconds{ 2 });
819
+ }
820
+ auto [ctx, resp] = collection.get_and_lock(id, lock_time, options).get();
814
821
  REQUIRE(ctx.ec() == couchbase::errc::common::ambiguous_timeout);
815
822
  REQUIRE(ctx.retried_because_of(couchbase::retry_reason::key_value_locked));
816
823
  }
@@ -17,6 +17,10 @@
17
17
 
18
18
  #include "test_helper_integration.hxx"
19
19
 
20
+ #include "core/operations/management/query_index_build.hxx"
21
+ #include "core/operations/management/query_index_create.hxx"
22
+ #include "core/operations/management/query_index_get_all.hxx"
23
+
20
24
  #include <couchbase/cluster.hxx>
21
25
  #include <couchbase/fmt/cas.hxx>
22
26
  #include <couchbase/fmt/mutation_token.hxx>
@@ -129,6 +133,43 @@ TEST_CASE("example: start using", "[integration]")
129
133
  return;
130
134
  }
131
135
 
136
+ {
137
+ couchbase::core::operations::management::query_index_create_request req{};
138
+ req.index_name = "def_inventory_airline_primary";
139
+ req.bucket_name = "travel-sample";
140
+ req.scope_name = "inventory";
141
+ req.collection_name = "airline";
142
+ req.is_primary = true;
143
+ req.ignore_if_exists = true;
144
+ auto resp = test::utils::execute(integration.cluster, req);
145
+ REQUIRE_FALSE(resp.ctx.ec);
146
+ }
147
+
148
+ {
149
+ couchbase::core::operations::management::query_index_build_request req{};
150
+ req.index_names = { "def_inventory_airline_primary" };
151
+ req.bucket_name = "travel-sample";
152
+ req.scope_name = "inventory";
153
+ req.collection_name = "airline";
154
+ auto resp = test::utils::execute(integration.cluster, req);
155
+ REQUIRE_FALSE(resp.ctx.ec);
156
+ }
157
+
158
+ CHECK(test::utils::wait_until(
159
+ [&integration]() {
160
+ couchbase::core::operations::management::query_index_get_all_request req{};
161
+ req.bucket_name = "travel-sample";
162
+ req.scope_name = "inventory";
163
+ auto resp = test::utils::execute(integration.cluster, req);
164
+ if (resp.ctx.ec) {
165
+ return false;
166
+ }
167
+ return std::any_of(resp.indexes.begin(), resp.indexes.end(), [](const auto& index) {
168
+ return index.collection_name == "airline" && index.is_primary && index.state == "online";
169
+ });
170
+ },
171
+ std::chrono::minutes{ 5 }));
172
+
132
173
  const auto env = test::utils::test_context::load_from_environment();
133
174
  const char* argv[] = {
134
175
  "start_using", // name of the "executable"
@@ -2851,11 +2851,23 @@ wait_for_search_pindexes_ready(test::utils::integration_test_guard& integration,
2851
2851
  return false;
2852
2852
  }
2853
2853
  auto stats = couchbase::core::utils::json::parse(resp.stats);
2854
- const auto* num_pindexes_actual = stats.find(fmt::format("{}:{}:num_pindexes_actual", integration.ctx.bucket, index_name));
2854
+
2855
+ const auto num_pindexes_actual_key = fmt::format("{}:{}:num_pindexes_actual", integration.ctx.bucket, index_name);
2856
+ const auto num_pindexes_target_key = fmt::format("{}:{}:num_pindexes_target", integration.ctx.bucket, index_name);
2857
+ const auto* num_pindexes_actual = stats.find(num_pindexes_actual_key);
2858
+ const auto* num_pindexes_target = stats.find(num_pindexes_target_key);
2859
+ CB_LOG_DEBUG(
2860
+ "wait_for_search_pindexes_ready: {}={}, {}={}",
2861
+ num_pindexes_actual_key,
2862
+ (num_pindexes_actual == nullptr || !num_pindexes_actual->is_number()) ? "missing"
2863
+ : std::to_string(num_pindexes_actual->get_unsigned()),
2864
+ num_pindexes_target_key,
2865
+ (num_pindexes_target == nullptr || !num_pindexes_target->is_number()) ? "missing"
2866
+ : std::to_string(num_pindexes_target->get_unsigned()));
2867
+
2855
2868
  if (num_pindexes_actual == nullptr || !num_pindexes_actual->is_number()) {
2856
2869
  return false;
2857
2870
  }
2858
- const auto* num_pindexes_target = stats.find(fmt::format("{}:{}:num_pindexes_target", integration.ctx.bucket, index_name));
2859
2871
  if (num_pindexes_target == nullptr || !num_pindexes_target->is_number()) {
2860
2872
  return false;
2861
2873
  }
@@ -2889,7 +2901,7 @@ TEST_CASE("integration: search index management analyze document", "[integration
2889
2901
  REQUIRE_SUCCESS(resp.ctx.ec);
2890
2902
  }
2891
2903
 
2892
- REQUIRE(wait_for_search_pindexes_ready(integration, index_name));
2904
+ REQUIRE(test::utils::wait_for_search_pindexes_ready(integration.cluster, integration.ctx.bucket, index_name));
2893
2905
 
2894
2906
  couchbase::core::operations::management::search_index_analyze_document_response resp;
2895
2907
  bool operation_completed = test::utils::wait_until([&integration, &index_name, &resp]() {