couchbase 4.2.5-dev.3 → 4.2.6-dev

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 (122) hide show
  1. package/README.md +81 -9
  2. package/deps/couchbase-cxx-client/CMakeLists.txt +9 -1
  3. package/deps/couchbase-cxx-client/bin/api.rb +234 -0
  4. package/deps/couchbase-cxx-client/bin/create-search-index +18 -135
  5. package/deps/couchbase-cxx-client/bin/init-cluster +17 -139
  6. package/deps/couchbase-cxx-client/bin/load-sample-buckets +54 -0
  7. package/deps/couchbase-cxx-client/core/cluster.hxx +33 -12
  8. package/deps/couchbase-cxx-client/core/cluster_options.hxx +3 -0
  9. package/deps/couchbase-cxx-client/core/crud_component.cxx +51 -22
  10. package/deps/couchbase-cxx-client/core/impl/build_deferred_query_indexes.cxx +115 -50
  11. package/deps/couchbase-cxx-client/core/impl/cluster.cxx +6 -0
  12. package/deps/couchbase-cxx-client/core/impl/create_bucket.cxx +155 -0
  13. package/deps/couchbase-cxx-client/core/impl/create_query_index.cxx +172 -59
  14. package/deps/couchbase-cxx-client/core/impl/dns_srv_tracker.cxx +2 -1
  15. package/deps/couchbase-cxx-client/core/impl/drop_bucket.cxx +66 -0
  16. package/deps/couchbase-cxx-client/core/impl/drop_query_index.cxx +138 -59
  17. package/deps/couchbase-cxx-client/core/impl/flush_bucket.cxx +66 -0
  18. package/deps/couchbase-cxx-client/core/impl/get_all_buckets.cxx +163 -0
  19. package/deps/couchbase-cxx-client/core/impl/get_all_query_indexes.cxx +67 -37
  20. package/deps/couchbase-cxx-client/core/impl/get_bucket.cxx +153 -0
  21. package/deps/couchbase-cxx-client/core/impl/internal_manager_error_context.cxx +113 -0
  22. package/deps/couchbase-cxx-client/core/impl/internal_manager_error_context.hxx +60 -0
  23. package/deps/couchbase-cxx-client/core/impl/key_value_error_category.cxx +2 -4
  24. package/deps/couchbase-cxx-client/core/impl/manager_error_context.cxx +100 -0
  25. package/deps/couchbase-cxx-client/core/impl/query.cxx +1 -0
  26. package/deps/couchbase-cxx-client/core/impl/update_bucket.cxx +130 -0
  27. package/deps/couchbase-cxx-client/core/impl/watch_query_indexes.cxx +53 -29
  28. package/deps/couchbase-cxx-client/core/io/dns_client.cxx +71 -38
  29. package/deps/couchbase-cxx-client/core/io/dns_config.cxx +5 -4
  30. package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +5 -6
  31. package/deps/couchbase-cxx-client/core/meta/features.hxx +6 -0
  32. package/deps/couchbase-cxx-client/core/operations/document_query.cxx +11 -0
  33. package/deps/couchbase-cxx-client/core/operations/document_query.hxx +1 -0
  34. package/deps/couchbase-cxx-client/core/origin.cxx +270 -0
  35. package/deps/couchbase-cxx-client/core/origin.hxx +2 -0
  36. package/deps/couchbase-cxx-client/core/protocol/status.cxx +2 -2
  37. package/deps/couchbase-cxx-client/core/range_scan_options.cxx +3 -27
  38. package/deps/couchbase-cxx-client/core/range_scan_options.hxx +13 -17
  39. package/deps/couchbase-cxx-client/core/range_scan_orchestrator.cxx +367 -170
  40. package/deps/couchbase-cxx-client/core/range_scan_orchestrator.hxx +13 -2
  41. package/deps/couchbase-cxx-client/core/range_scan_orchestrator_options.hxx +5 -3
  42. package/deps/couchbase-cxx-client/core/scan_options.hxx +0 -19
  43. package/deps/couchbase-cxx-client/core/scan_result.cxx +19 -5
  44. package/deps/couchbase-cxx-client/core/scan_result.hxx +5 -2
  45. package/deps/couchbase-cxx-client/core/timeout_defaults.hxx +2 -3
  46. package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +1 -0
  47. package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +2 -0
  48. package/deps/couchbase-cxx-client/core/topology/collections_manifest_fmt.hxx +1 -1
  49. package/deps/couchbase-cxx-client/core/topology/configuration.hxx +5 -0
  50. package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +2 -0
  51. package/deps/couchbase-cxx-client/core/utils/connection_string.cxx +4 -0
  52. package/deps/couchbase-cxx-client/couchbase/behavior_options.hxx +19 -2
  53. package/deps/couchbase-cxx-client/couchbase/bucket_manager.hxx +135 -0
  54. package/deps/couchbase-cxx-client/couchbase/build_query_index_options.hxx +0 -30
  55. package/deps/couchbase-cxx-client/couchbase/cluster.hxx +14 -0
  56. package/deps/couchbase-cxx-client/couchbase/collection_query_index_manager.hxx +7 -48
  57. package/deps/couchbase-cxx-client/couchbase/create_bucket_options.hxx +41 -0
  58. package/deps/couchbase-cxx-client/couchbase/create_primary_query_index_options.hxx +0 -29
  59. package/deps/couchbase-cxx-client/couchbase/create_query_index_options.hxx +0 -33
  60. package/deps/couchbase-cxx-client/couchbase/drop_bucket_options.hxx +41 -0
  61. package/deps/couchbase-cxx-client/couchbase/drop_primary_query_index_options.hxx +0 -30
  62. package/deps/couchbase-cxx-client/couchbase/drop_query_index_options.hxx +0 -31
  63. package/deps/couchbase-cxx-client/couchbase/error_codes.hxx +1 -2
  64. package/deps/couchbase-cxx-client/couchbase/flush_bucket_options.hxx +41 -0
  65. package/deps/couchbase-cxx-client/couchbase/get_all_buckets_options.hxx +44 -0
  66. package/deps/couchbase-cxx-client/couchbase/get_all_query_indexes_options.hxx +0 -30
  67. package/deps/couchbase-cxx-client/couchbase/get_bucket_options.hxx +43 -0
  68. package/deps/couchbase-cxx-client/couchbase/management/bucket_settings.hxx +116 -0
  69. package/deps/couchbase-cxx-client/couchbase/manager_error_context.hxx +29 -53
  70. package/deps/couchbase-cxx-client/couchbase/query_index_manager.hxx +16 -83
  71. package/deps/couchbase-cxx-client/couchbase/query_options.hxx +18 -0
  72. package/deps/couchbase-cxx-client/couchbase/security_options.hxx +15 -0
  73. package/deps/couchbase-cxx-client/couchbase/update_bucket_options.hxx +41 -0
  74. package/deps/couchbase-cxx-client/couchbase/watch_query_indexes_options.hxx +0 -31
  75. package/deps/couchbase-cxx-client/docs/cbc-analytics.md +1 -0
  76. package/deps/couchbase-cxx-client/docs/cbc-get.md +1 -0
  77. package/deps/couchbase-cxx-client/docs/cbc-pillowfight.md +1 -0
  78. package/deps/couchbase-cxx-client/docs/cbc-query.md +1 -0
  79. package/deps/couchbase-cxx-client/docs/cbc.md +10 -0
  80. package/deps/couchbase-cxx-client/test/CMakeLists.txt +1 -0
  81. package/deps/couchbase-cxx-client/test/test_integration_collections.cxx +6 -0
  82. package/deps/couchbase-cxx-client/test/test_integration_crud.cxx +5 -0
  83. package/deps/couchbase-cxx-client/test/test_integration_examples.cxx +137 -1
  84. package/deps/couchbase-cxx-client/test/test_integration_management.cxx +709 -266
  85. package/deps/couchbase-cxx-client/test/test_integration_query.cxx +19 -7
  86. package/deps/couchbase-cxx-client/test/test_integration_range_scan.cxx +351 -112
  87. package/deps/couchbase-cxx-client/test/test_integration_search.cxx +10 -1
  88. package/deps/couchbase-cxx-client/test/test_transaction_public_async_api.cxx +13 -12
  89. package/deps/couchbase-cxx-client/test/test_transaction_public_blocking_api.cxx +27 -21
  90. package/deps/couchbase-cxx-client/test/test_unit_query.cxx +75 -0
  91. package/deps/couchbase-cxx-client/test/utils/server_version.hxx +5 -0
  92. package/deps/couchbase-cxx-client/test/utils/wait_until.cxx +29 -10
  93. package/deps/couchbase-cxx-client/test/utils/wait_until.hxx +3 -1
  94. package/deps/couchbase-cxx-client/tools/utils.cxx +4 -1
  95. package/dist/binding.d.ts +21 -16
  96. package/dist/binding.js +1 -4
  97. package/dist/bindingutilities.d.ts +6 -1
  98. package/dist/bindingutilities.js +36 -1
  99. package/dist/collection.d.ts +65 -3
  100. package/dist/collection.js +107 -0
  101. package/dist/crudoptypes.d.ts +34 -0
  102. package/dist/crudoptypes.js +18 -1
  103. package/dist/queryexecutor.js +1 -0
  104. package/dist/querytypes.d.ts +7 -0
  105. package/dist/rangeScan.d.ts +107 -0
  106. package/dist/rangeScan.js +91 -0
  107. package/dist/streamablepromises.d.ts +6 -0
  108. package/dist/streamablepromises.js +25 -1
  109. package/package.json +13 -14
  110. package/scripts/createPlatformPackages.js +1 -4
  111. package/src/addondata.hpp +1 -0
  112. package/src/binding.cpp +5 -2
  113. package/src/connection.cpp +108 -2
  114. package/src/connection.hpp +1 -0
  115. package/src/constants.cpp +2 -12
  116. package/src/jstocbpp_autogen.hpp +49 -22
  117. package/src/jstocbpp_basic.hpp +2 -8
  118. package/src/mutationtoken.cpp +13 -0
  119. package/src/scan_iterator.cpp +90 -0
  120. package/src/scan_iterator.hpp +30 -0
  121. package/tools/gen-bindings-json.py +9 -8
  122. package/deps/couchbase-cxx-client/core/impl/collection_query_index_manager.cxx +0 -93
@@ -40,12 +40,12 @@ populate_documents_for_range_scan(const couchbase::collection& collection,
40
40
  options.expiry(expiry.value());
41
41
  }
42
42
 
43
- std::map<std::vector<std::byte>, couchbase::mutation_token> mutations;
43
+ std::map<std::string, couchbase::mutation_token> mutations;
44
44
  for (const auto& id : ids) {
45
45
  auto [ctx, resp] = collection.upsert<couchbase::codec::raw_binary_transcoder>(id, value, options).get();
46
46
  REQUIRE_SUCCESS(ctx.ec());
47
47
  REQUIRE(resp.mutation_token().has_value());
48
- mutations[couchbase::core::utils::to_binary(id)] = resp.mutation_token().value();
48
+ mutations[id] = resp.mutation_token().value();
49
49
  }
50
50
  return mutations;
51
51
  }
@@ -76,7 +76,6 @@ do_range_scan(couchbase::core::agent agent,
76
76
  std::vector<couchbase::core::range_scan_item> data;
77
77
 
78
78
  auto options = continue_options;
79
- options.ids_only = create_options.ids_only; // support servers before MB-54267. TODO: remove after server GA
80
79
 
81
80
  do {
82
81
  auto barrier = std::make_shared<std::promise<std::pair<couchbase::core::range_scan_continue_result, std::error_code>>>();
@@ -115,6 +114,26 @@ make_binary_value(std::size_t number_of_bytes)
115
114
  return value;
116
115
  }
117
116
 
117
+ static couchbase::core::topology::configuration::vbucket_map
118
+ get_vbucket_map(test::utils::integration_test_guard& integration)
119
+ {
120
+ auto barrier = std::make_shared<std::promise<tl::expected<couchbase::core::topology::configuration::vbucket_map, std::error_code>>>();
121
+ auto f = barrier->get_future();
122
+ integration.cluster->with_bucket_configuration(
123
+ integration.ctx.bucket, [barrier](std::error_code ec, const couchbase::core::topology::configuration& config) mutable {
124
+ if (ec) {
125
+ return barrier->set_value(tl::unexpected(ec));
126
+ }
127
+ if (!config.vbmap || config.vbmap->empty()) {
128
+ return barrier->set_value(tl::unexpected(couchbase::errc::common::feature_not_available));
129
+ }
130
+ barrier->set_value(config.vbmap.value());
131
+ });
132
+ auto vbucket_map = f.get();
133
+ EXPECT_SUCCESS(vbucket_map);
134
+ return vbucket_map.value();
135
+ }
136
+
118
137
  TEST_CASE("integration: range scan large values", "[integration]")
119
138
  {
120
139
  test::utils::integration_test_guard integration;
@@ -148,8 +167,8 @@ TEST_CASE("integration: range scan large values", "[integration]")
148
167
  couchbase::scope::default_name,
149
168
  couchbase::collection::default_name,
150
169
  couchbase::core::range_scan{
151
- { couchbase::core::utils::to_binary("largevalues") },
152
- { couchbase::core::utils::to_binary("largevalues\xff") },
170
+ couchbase::core::scan_term{ "largevalues" },
171
+ couchbase::core::scan_term{ "largevalues\xff" },
153
172
  },
154
173
  };
155
174
  create_options.snapshot_requirements = couchbase::core::range_snapshot_requirements{
@@ -208,8 +227,8 @@ TEST_CASE("integration: range scan small values", "[integration]")
208
227
  couchbase::scope::default_name,
209
228
  couchbase::collection::default_name,
210
229
  couchbase::core::range_scan{
211
- { couchbase::core::utils::to_binary("rangesmallvalues") },
212
- { couchbase::core::utils::to_binary("rangesmallvalues\xff") },
230
+ couchbase::core::scan_term{ "rangesmallvalues" },
231
+ couchbase::core::scan_term{ "rangesmallvalues\xff" },
213
232
  },
214
233
  };
215
234
  create_options.snapshot_requirements = couchbase::core::range_snapshot_requirements{
@@ -312,8 +331,8 @@ TEST_CASE("integration: range scan collection retry", "[integration]")
312
331
  couchbase::scope::default_name,
313
332
  new_collection.name(),
314
333
  couchbase::core::range_scan{
315
- { couchbase::core::utils::to_binary("rangecollectionretry") },
316
- { couchbase::core::utils::to_binary("rangecollectionretry\xff") },
334
+ couchbase::core::scan_term{ "rangecollectionretry" },
335
+ couchbase::core::scan_term{ "rangecollectionretry\xff" },
317
336
  },
318
337
  };
319
338
  create_options.snapshot_requirements = couchbase::core::range_snapshot_requirements{
@@ -372,8 +391,8 @@ TEST_CASE("integration: range scan only keys", "[integration]")
372
391
  couchbase::scope::default_name,
373
392
  couchbase::collection::default_name,
374
393
  couchbase::core::range_scan{
375
- { couchbase::core::utils::to_binary("rangekeysonly") },
376
- { couchbase::core::utils::to_binary("rangekeysonly\xff") },
394
+ couchbase::core::scan_term{ "rangekeysonly" },
395
+ couchbase::core::scan_term{ "rangekeysonly\xff" },
377
396
  },
378
397
  };
379
398
  create_options.ids_only = true;
@@ -435,8 +454,8 @@ TEST_CASE("integration: range scan cancellation before continue", "[integration]
435
454
  couchbase::scope::default_name,
436
455
  couchbase::collection::default_name,
437
456
  couchbase::core::range_scan{
438
- { couchbase::core::utils::to_binary("rangescancancel") },
439
- { couchbase::core::utils::to_binary("rangescancancel\xff") },
457
+ couchbase::core::scan_term{ "rangescancancel" },
458
+ couchbase::core::scan_term{ "rangescancancel\xff" },
440
459
  },
441
460
  };
442
461
  options.ids_only = true;
@@ -474,7 +493,6 @@ TEST_CASE("integration: range scan cancellation before continue", "[integration]
474
493
 
475
494
  couchbase::core::range_scan_continue_options options{};
476
495
  options.batch_time_limit = std::chrono::seconds{ 10 };
477
- options.ids_only = true; // support servers before MB-54267. TODO: remove after server GA
478
496
 
479
497
  bool items_callback_invoked{ false };
480
498
  {
@@ -539,8 +557,8 @@ TEST_CASE("integration: range scan cancel during streaming using protocol cancel
539
557
  couchbase::scope::default_name,
540
558
  couchbase::collection::default_name,
541
559
  couchbase::core::range_scan{
542
- { couchbase::core::utils::to_binary("rangescancancel") },
543
- { couchbase::core::utils::to_binary("rangescancancel\xff") },
560
+ couchbase::core::scan_term{ "rangescancancel" },
561
+ couchbase::core::scan_term{ "rangescancancel\xff" },
544
562
  },
545
563
  };
546
564
  options.ids_only = true;
@@ -577,7 +595,6 @@ TEST_CASE("integration: range scan cancel during streaming using protocol cancel
577
595
  couchbase::core::range_scan_continue_options options{};
578
596
  options.batch_time_limit = std::chrono::seconds{ 10 };
579
597
  options.batch_item_limit = 3; // limit batch to 3 items, while range expected to be larger
580
- options.ids_only = true; // support servers before MB-54267. TODO: remove after server GA
581
598
 
582
599
  auto barrier = std::make_shared<std::promise<std::pair<couchbase::core::range_scan_continue_result, std::error_code>>>();
583
600
  auto f = barrier->get_future();
@@ -651,8 +668,8 @@ TEST_CASE("integration: range scan cancel during streaming using pending_operati
651
668
  couchbase::scope::default_name,
652
669
  couchbase::collection::default_name,
653
670
  couchbase::core::range_scan{
654
- { couchbase::core::utils::to_binary("rangescancancel") },
655
- { couchbase::core::utils::to_binary("rangescancancel\xff") },
671
+ couchbase::core::scan_term{ "rangescancancel" },
672
+ couchbase::core::scan_term{ "rangescancancel\xff" },
656
673
  },
657
674
  };
658
675
  options.ids_only = true;
@@ -685,7 +702,6 @@ TEST_CASE("integration: range scan cancel during streaming using pending_operati
685
702
  couchbase::core::range_scan_continue_options options{};
686
703
  options.batch_time_limit = std::chrono::seconds{ 10 };
687
704
  options.batch_item_limit = 3; // limit batch to 3 items, while range expected to be larger
688
- options.ids_only = true; // support servers before MB-54267. TODO: remove after server GA
689
705
 
690
706
  auto barrier = std::make_shared<std::promise<std::pair<couchbase::core::range_scan_continue_result, std::error_code>>>();
691
707
  auto f = barrier->get_future();
@@ -774,7 +790,7 @@ make_doc_ids(std::size_t number_of_keys, const std::string& prefix)
774
790
  }
775
791
 
776
792
  static auto
777
- mutations_to_mutation_state(std::map<std::vector<std::byte>, couchbase::mutation_token> mutations)
793
+ mutations_to_mutation_state(std::map<std::string, couchbase::mutation_token> mutations)
778
794
  {
779
795
  couchbase::core::mutation_state state;
780
796
  for (const auto& [key, token] : mutations) {
@@ -800,42 +816,27 @@ TEST_CASE("integration: manager scan range without content", "[integration]")
800
816
  auto value = make_binary_value(1);
801
817
  auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 30 });
802
818
 
803
- auto barrier = std::make_shared<std::promise<tl::expected<std::size_t, std::error_code>>>();
804
- auto f = barrier->get_future();
805
- integration.cluster->with_bucket_configuration(
806
- integration.ctx.bucket, [barrier](std::error_code ec, const couchbase::core::topology::configuration& config) mutable {
807
- if (ec) {
808
- return barrier->set_value(tl::unexpected(ec));
809
- }
810
- if (!config.vbmap || config.vbmap->empty()) {
811
- return barrier->set_value(tl::unexpected(couchbase::errc::common::feature_not_available));
812
- }
813
- barrier->set_value(config.vbmap->size());
814
- });
815
- auto number_of_vbuckets = f.get();
816
- EXPECT_SUCCESS(number_of_vbuckets);
819
+ auto vbucket_map = get_vbucket_map(integration);
817
820
 
818
821
  auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
819
822
  ag.open_bucket(integration.ctx.bucket);
820
823
  auto agent = ag.get_agent(integration.ctx.bucket);
821
824
  REQUIRE(agent.has_value());
822
825
 
823
- couchbase::core::range_scan scan{ "rangescanwithoutcontent", "rangescanwithoutcontent\xff" };
826
+ couchbase::core::range_scan scan{
827
+ couchbase::core::scan_term{ "rangescanwithoutcontent" },
828
+ couchbase::core::scan_term{ "rangescanwithoutcontent\xff" },
829
+ };
824
830
  couchbase::core::range_scan_orchestrator_options options{};
825
831
  options.consistent_with = mutations_to_mutation_state(mutations);
826
832
  options.ids_only = true;
827
- couchbase::core::range_scan_orchestrator orchestrator(integration.io,
828
- agent.value(),
829
- number_of_vbuckets.value(),
830
- couchbase::scope::default_name,
831
- couchbase::collection::default_name,
832
- scan,
833
- options);
833
+ couchbase::core::range_scan_orchestrator orchestrator(
834
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, couchbase::collection::default_name, scan, options);
834
835
 
835
836
  auto result = orchestrator.scan();
836
837
  EXPECT_SUCCESS(result);
837
838
 
838
- std::set<std::vector<std::byte>> entry_ids{};
839
+ std::set<std::string> entry_ids{};
839
840
 
840
841
  do {
841
842
  auto entry = result->next();
@@ -848,10 +849,11 @@ TEST_CASE("integration: manager scan range without content", "[integration]")
848
849
  REQUIRE_FALSE(entry->body.has_value());
849
850
  } while (true);
850
851
 
852
+ REQUIRE(ids.size() == entry_ids.size());
853
+
851
854
  for (const auto& id : ids) {
852
- REQUIRE(entry_ids.count(couchbase::core::utils::to_binary(id)) == 1);
855
+ REQUIRE(entry_ids.count(id) == 1);
853
856
  }
854
- REQUIRE(ids.size() == entry_ids.size());
855
857
  }
856
858
 
857
859
  TEST_CASE("integration: manager scan range with content", "[integration]")
@@ -871,41 +873,26 @@ TEST_CASE("integration: manager scan range with content", "[integration]")
871
873
  auto value = make_binary_value(100);
872
874
  auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 30 });
873
875
 
874
- auto barrier = std::make_shared<std::promise<tl::expected<std::size_t, std::error_code>>>();
875
- auto f = barrier->get_future();
876
- integration.cluster->with_bucket_configuration(
877
- integration.ctx.bucket, [barrier](std::error_code ec, const couchbase::core::topology::configuration& config) mutable {
878
- if (ec) {
879
- return barrier->set_value(tl::unexpected(ec));
880
- }
881
- if (!config.vbmap || config.vbmap->empty()) {
882
- return barrier->set_value(tl::unexpected(couchbase::errc::common::feature_not_available));
883
- }
884
- barrier->set_value(config.vbmap->size());
885
- });
886
- auto number_of_vbuckets = f.get();
887
- EXPECT_SUCCESS(number_of_vbuckets);
876
+ auto vbucket_map = get_vbucket_map(integration);
888
877
 
889
878
  auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
890
879
  ag.open_bucket(integration.ctx.bucket);
891
880
  auto agent = ag.get_agent(integration.ctx.bucket);
892
881
  REQUIRE(agent.has_value());
893
882
 
894
- couchbase::core::range_scan scan{ "rangescanwithcontent", "rangescanwithcontent\xff" };
883
+ couchbase::core::range_scan scan{
884
+ couchbase::core::scan_term{ "rangescanwithcontent" },
885
+ couchbase::core::scan_term{ "rangescanwithcontent\xff" },
886
+ };
895
887
  couchbase::core::range_scan_orchestrator_options options{};
896
888
  options.consistent_with = mutations_to_mutation_state(mutations);
897
- couchbase::core::range_scan_orchestrator orchestrator(integration.io,
898
- agent.value(),
899
- number_of_vbuckets.value(),
900
- couchbase::scope::default_name,
901
- couchbase::collection::default_name,
902
- scan,
903
- options);
889
+ couchbase::core::range_scan_orchestrator orchestrator(
890
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, couchbase::collection::default_name, scan, options);
904
891
 
905
892
  auto result = orchestrator.scan();
906
893
  EXPECT_SUCCESS(result);
907
894
 
908
- std::set<std::vector<std::byte>> entry_ids{};
895
+ std::set<std::string> entry_ids{};
909
896
 
910
897
  do {
911
898
  auto entry = result->next();
@@ -918,11 +905,12 @@ TEST_CASE("integration: manager scan range with content", "[integration]")
918
905
  REQUIRE(entry->body.has_value());
919
906
  } while (true);
920
907
 
908
+ REQUIRE(ids.size() == entry_ids.size());
909
+
921
910
  for (const auto& id : ids) {
922
911
  INFO(id);
923
- REQUIRE(entry_ids.count(couchbase::core::utils::to_binary(id)) == 1);
912
+ REQUIRE(entry_ids.count(id) == 1);
924
913
  }
925
- REQUIRE(ids.size() == entry_ids.size());
926
914
  }
927
915
 
928
916
  TEST_CASE("integration: manager sampling scan with custom collection", "[integration]")
@@ -944,20 +932,7 @@ TEST_CASE("integration: manager sampling scan with custom collection", "[integra
944
932
  auto value = make_binary_value(100);
945
933
  auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 300 });
946
934
 
947
- auto barrier = std::make_shared<std::promise<tl::expected<std::size_t, std::error_code>>>();
948
- auto f = barrier->get_future();
949
- integration.cluster->with_bucket_configuration(
950
- integration.ctx.bucket, [barrier](std::error_code ec, const couchbase::core::topology::configuration& config) mutable {
951
- if (ec) {
952
- return barrier->set_value(tl::unexpected(ec));
953
- }
954
- if (!config.vbmap || config.vbmap->empty()) {
955
- return barrier->set_value(tl::unexpected(couchbase::errc::common::feature_not_available));
956
- }
957
- barrier->set_value(config.vbmap->size());
958
- });
959
- auto number_of_vbuckets = f.get();
960
- EXPECT_SUCCESS(number_of_vbuckets);
935
+ auto vbucket_map = get_vbucket_map(integration);
961
936
 
962
937
  auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
963
938
  ag.open_bucket(integration.ctx.bucket);
@@ -968,12 +943,12 @@ TEST_CASE("integration: manager sampling scan with custom collection", "[integra
968
943
  couchbase::core::range_scan_orchestrator_options options{};
969
944
  options.consistent_with = mutations_to_mutation_state(mutations);
970
945
  couchbase::core::range_scan_orchestrator orchestrator(
971
- integration.io, agent.value(), number_of_vbuckets.value(), couchbase::scope::default_name, new_collection.name(), scan, options);
946
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, new_collection.name(), scan, options);
972
947
 
973
948
  auto result = orchestrator.scan();
974
949
  EXPECT_SUCCESS(result);
975
950
 
976
- std::set<std::vector<std::byte>> entry_ids{};
951
+ std::set<std::string> entry_ids{};
977
952
 
978
953
  auto now = std::chrono::system_clock::now();
979
954
  do {
@@ -994,11 +969,65 @@ TEST_CASE("integration: manager sampling scan with custom collection", "[integra
994
969
  REQUIRE(ids.size() >= 10);
995
970
 
996
971
  for (const auto& id : entry_ids) {
997
- REQUIRE(std::find(ids.begin(), ids.end(), std::string(reinterpret_cast<const char*>(id.data()), id.size())) != ids.end());
972
+ REQUIRE(std::find(ids.begin(), ids.end(), id) != ids.end());
998
973
  }
999
974
  }
1000
975
 
1001
- TEST_CASE("integration: manager range scan with sort", "[integration]")
976
+ TEST_CASE("integration: manager prefix scan without content", "[integration]")
977
+ {
978
+ test::utils::integration_test_guard integration;
979
+
980
+ if (!integration.has_bucket_capability("range_scan")) {
981
+ SKIP("cluster does not support range_scan");
982
+ }
983
+
984
+ auto collection = couchbase::cluster(integration.cluster)
985
+ .bucket(integration.ctx.bucket)
986
+ .scope(couchbase::scope::default_name)
987
+ .collection(couchbase::collection::default_name);
988
+
989
+ auto ids = make_doc_ids(100, "prefixscanwithoutcontent-");
990
+ auto value = make_binary_value(1);
991
+ auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 30 });
992
+
993
+ auto vbucket_map = get_vbucket_map(integration);
994
+
995
+ auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
996
+ ag.open_bucket(integration.ctx.bucket);
997
+ auto agent = ag.get_agent(integration.ctx.bucket);
998
+ REQUIRE(agent.has_value());
999
+
1000
+ couchbase::core::prefix_scan scan{ "prefixscanwithoutcontent" };
1001
+ couchbase::core::range_scan_orchestrator_options options{};
1002
+ options.consistent_with = mutations_to_mutation_state(mutations);
1003
+ options.ids_only = true;
1004
+ couchbase::core::range_scan_orchestrator orchestrator(
1005
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, couchbase::collection::default_name, scan, options);
1006
+
1007
+ auto result = orchestrator.scan();
1008
+ EXPECT_SUCCESS(result);
1009
+
1010
+ std::set<std::string> entry_ids{};
1011
+
1012
+ do {
1013
+ auto entry = result->next();
1014
+ if (!entry) {
1015
+ break;
1016
+ }
1017
+
1018
+ auto [_, inserted] = entry_ids.insert(entry->key);
1019
+ REQUIRE(inserted);
1020
+ REQUIRE_FALSE(entry->body.has_value());
1021
+ } while (true);
1022
+
1023
+ REQUIRE(ids.size() == entry_ids.size());
1024
+
1025
+ for (const auto& id : ids) {
1026
+ REQUIRE(entry_ids.count(id) == 1);
1027
+ }
1028
+ }
1029
+
1030
+ TEST_CASE("integration: manager sampling scan with custom collection and up to 10 concurrent streams", "[integration]")
1002
1031
  {
1003
1032
  test::utils::integration_test_guard integration;
1004
1033
 
@@ -1013,43 +1042,151 @@ TEST_CASE("integration: manager range scan with sort", "[integration]")
1013
1042
  .scope(couchbase::scope::default_name)
1014
1043
  .collection(new_collection.name());
1015
1044
 
1016
- auto ids = make_doc_ids(100, "rangescansort-");
1045
+ auto ids = make_doc_ids(100, "samplingscan-");
1017
1046
  auto value = make_binary_value(100);
1018
1047
  auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 300 });
1019
1048
 
1020
- auto barrier = std::make_shared<std::promise<tl::expected<std::size_t, std::error_code>>>();
1021
- auto f = barrier->get_future();
1022
- integration.cluster->with_bucket_configuration(
1023
- integration.ctx.bucket, [barrier](std::error_code ec, const couchbase::core::topology::configuration& config) mutable {
1024
- if (ec) {
1025
- return barrier->set_value(tl::unexpected(ec));
1026
- }
1027
- if (!config.vbmap || config.vbmap->empty()) {
1028
- return barrier->set_value(tl::unexpected(couchbase::errc::common::feature_not_available));
1029
- }
1030
- barrier->set_value(config.vbmap->size());
1031
- });
1032
- auto number_of_vbuckets = f.get();
1033
- EXPECT_SUCCESS(number_of_vbuckets);
1049
+ auto vbucket_map = get_vbucket_map(integration);
1050
+
1051
+ auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
1052
+ ag.open_bucket(integration.ctx.bucket);
1053
+ auto agent = ag.get_agent(integration.ctx.bucket);
1054
+ REQUIRE(agent.has_value());
1055
+
1056
+ couchbase::core::sampling_scan scan{ 10, 50 };
1057
+ couchbase::core::range_scan_orchestrator_options options{};
1058
+ options.consistent_with = mutations_to_mutation_state(mutations);
1059
+ options.concurrency = 10;
1060
+ couchbase::core::range_scan_orchestrator orchestrator(
1061
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, new_collection.name(), scan, options);
1062
+
1063
+ auto result = orchestrator.scan();
1064
+ EXPECT_SUCCESS(result);
1065
+
1066
+ std::set<std::string> entry_ids{};
1067
+
1068
+ auto now = std::chrono::system_clock::now();
1069
+ do {
1070
+ auto entry = result->next();
1071
+ if (!entry) {
1072
+ break;
1073
+ }
1074
+
1075
+ REQUIRE(entry->body);
1076
+ REQUIRE_FALSE(entry->body->cas.empty());
1077
+ REQUIRE(entry->body->value == value);
1078
+ REQUIRE(entry->body->expiry_time() > now);
1079
+
1080
+ auto [_, inserted] = entry_ids.insert(entry->key);
1081
+ REQUIRE(inserted);
1082
+ } while (true);
1083
+
1084
+ REQUIRE(ids.size() >= 10);
1085
+
1086
+ for (const auto& id : entry_ids) {
1087
+ REQUIRE(std::find(ids.begin(), ids.end(), id) != ids.end());
1088
+ }
1089
+ }
1090
+
1091
+ TEST_CASE("integration: manager sampling scan with custom collection and up to 128 concurrent streams and batch item limit 0",
1092
+ "[integration]")
1093
+ {
1094
+ test::utils::integration_test_guard integration;
1095
+
1096
+ if (!integration.has_bucket_capability("range_scan")) {
1097
+ SKIP("cluster does not support range_scan");
1098
+ }
1099
+
1100
+ collection_guard new_collection(integration);
1101
+
1102
+ auto collection = couchbase::cluster(integration.cluster)
1103
+ .bucket(integration.ctx.bucket)
1104
+ .scope(couchbase::scope::default_name)
1105
+ .collection(new_collection.name());
1106
+
1107
+ auto ids = make_doc_ids(100, "samplingscan-");
1108
+ auto value = make_binary_value(100);
1109
+ auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 300 });
1110
+
1111
+ auto vbucket_map = get_vbucket_map(integration);
1034
1112
 
1035
1113
  auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
1036
1114
  ag.open_bucket(integration.ctx.bucket);
1037
1115
  auto agent = ag.get_agent(integration.ctx.bucket);
1038
1116
  REQUIRE(agent.has_value());
1039
1117
 
1040
- couchbase::core::range_scan scan{ "rangescansort", "rangescansort\xff" };
1118
+ couchbase::core::sampling_scan scan{ 10, 50 };
1119
+ couchbase::core::range_scan_orchestrator_options options{};
1120
+ options.consistent_with = mutations_to_mutation_state(mutations);
1121
+ options.concurrency = 128;
1122
+ options.batch_item_limit = 0;
1123
+ couchbase::core::range_scan_orchestrator orchestrator(
1124
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, new_collection.name(), scan, options);
1125
+
1126
+ auto result = orchestrator.scan();
1127
+ EXPECT_SUCCESS(result);
1128
+
1129
+ std::set<std::string> entry_ids{};
1130
+
1131
+ auto now = std::chrono::system_clock::now();
1132
+ do {
1133
+ auto entry = result->next();
1134
+ if (!entry) {
1135
+ break;
1136
+ }
1137
+
1138
+ REQUIRE(entry->body);
1139
+ REQUIRE_FALSE(entry->body->cas.empty());
1140
+ REQUIRE(entry->body->value == value);
1141
+ REQUIRE(entry->body->expiry_time() > now);
1142
+
1143
+ auto [_, inserted] = entry_ids.insert(entry->key);
1144
+ REQUIRE(inserted);
1145
+ } while (true);
1146
+
1147
+ REQUIRE(ids.size() >= 10);
1148
+
1149
+ for (const auto& id : entry_ids) {
1150
+ REQUIRE(std::find(ids.begin(), ids.end(), id) != ids.end());
1151
+ }
1152
+ }
1153
+
1154
+ TEST_CASE("integration: manager prefix scan without content and up to 5 concurrent streams", "[integration]")
1155
+ {
1156
+ test::utils::integration_test_guard integration;
1157
+
1158
+ if (!integration.has_bucket_capability("range_scan")) {
1159
+ SKIP("cluster does not support range_scan");
1160
+ }
1161
+
1162
+ auto collection = couchbase::cluster(integration.cluster)
1163
+ .bucket(integration.ctx.bucket)
1164
+ .scope(couchbase::scope::default_name)
1165
+ .collection(couchbase::collection::default_name);
1166
+
1167
+ auto ids = make_doc_ids(100, "prefixscanwithoutcontent-");
1168
+ auto value = make_binary_value(1);
1169
+ auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 30 });
1170
+
1171
+ auto vbucket_map = get_vbucket_map(integration);
1172
+
1173
+ auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
1174
+ ag.open_bucket(integration.ctx.bucket);
1175
+ auto agent = ag.get_agent(integration.ctx.bucket);
1176
+ REQUIRE(agent.has_value());
1177
+
1178
+ couchbase::core::prefix_scan scan{ "prefixscanwithoutcontent" };
1041
1179
  couchbase::core::range_scan_orchestrator_options options{};
1042
1180
  options.consistent_with = mutations_to_mutation_state(mutations);
1043
1181
  options.ids_only = true;
1044
- options.sort = couchbase::core::scan_sort::ascending;
1182
+ options.concurrency = 5;
1045
1183
  couchbase::core::range_scan_orchestrator orchestrator(
1046
- integration.io, agent.value(), number_of_vbuckets.value(), couchbase::scope::default_name, new_collection.name(), scan, options);
1184
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, couchbase::collection::default_name, scan, options);
1047
1185
 
1048
1186
  auto result = orchestrator.scan();
1049
1187
  EXPECT_SUCCESS(result);
1050
1188
 
1051
- std::vector<std::string> entry_ids{};
1052
- entry_ids.reserve(ids.size());
1189
+ std::set<std::string> entry_ids{};
1053
1190
 
1054
1191
  do {
1055
1192
  auto entry = result->next();
@@ -1057,11 +1194,113 @@ TEST_CASE("integration: manager range scan with sort", "[integration]")
1057
1194
  break;
1058
1195
  }
1059
1196
 
1060
- entry_ids.emplace_back(reinterpret_cast<const char*>(entry->key.data()), entry->key.size());
1197
+ auto [_, inserted] = entry_ids.insert(entry->key);
1198
+ REQUIRE(inserted);
1061
1199
  REQUIRE_FALSE(entry->body.has_value());
1062
1200
  } while (true);
1063
1201
 
1064
1202
  REQUIRE(ids.size() == entry_ids.size());
1065
- std::sort(ids.begin(), ids.end());
1066
- REQUIRE(ids == entry_ids);
1203
+
1204
+ for (const auto& id : ids) {
1205
+ REQUIRE(entry_ids.count(id) == 1);
1206
+ }
1207
+ }
1208
+
1209
+ TEST_CASE("integration: manager prefix scan, get 10 items and cancel", "[integration]")
1210
+ {
1211
+ test::utils::integration_test_guard integration;
1212
+
1213
+ if (!integration.has_bucket_capability("range_scan")) {
1214
+ SKIP("cluster does not support range_scan");
1215
+ }
1216
+
1217
+ auto collection = couchbase::cluster(integration.cluster)
1218
+ .bucket(integration.ctx.bucket)
1219
+ .scope(couchbase::scope::default_name)
1220
+ .collection(couchbase::collection::default_name);
1221
+
1222
+ auto ids = make_doc_ids(15, "rangescancancel-");
1223
+ auto value = make_binary_value(1);
1224
+ auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 30 });
1225
+
1226
+ auto vbucket_map = get_vbucket_map(integration);
1227
+
1228
+ auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
1229
+ ag.open_bucket(integration.ctx.bucket);
1230
+ auto agent = ag.get_agent(integration.ctx.bucket);
1231
+ REQUIRE(agent.has_value());
1232
+
1233
+ couchbase::core::prefix_scan scan{ "rangescancancel" };
1234
+ couchbase::core::range_scan_orchestrator_options options{};
1235
+ options.consistent_with = mutations_to_mutation_state(mutations);
1236
+ options.ids_only = true;
1237
+ couchbase::core::range_scan_orchestrator orchestrator(
1238
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, couchbase::collection::default_name, scan, options);
1239
+
1240
+ auto result = orchestrator.scan();
1241
+ EXPECT_SUCCESS(result);
1242
+
1243
+ std::set<std::string> entry_ids{};
1244
+ std::size_t const expected_id_count = 10;
1245
+
1246
+ for (std::size_t i = 0; i < expected_id_count; i++) {
1247
+ auto entry = result->next();
1248
+ if (!entry) {
1249
+ break;
1250
+ }
1251
+
1252
+ auto [_, inserted] = entry_ids.insert(entry->key);
1253
+ REQUIRE(inserted);
1254
+ REQUIRE_FALSE(entry->body.has_value());
1255
+ }
1256
+
1257
+ result->cancel();
1258
+
1259
+ REQUIRE(expected_id_count == entry_ids.size());
1260
+
1261
+ for (const auto& entry_id : entry_ids) {
1262
+ REQUIRE(std::count(ids.begin(), ids.end(), entry_id) == 0);
1263
+ }
1264
+
1265
+ auto next_item = result->next();
1266
+ REQUIRE(!next_item.has_value());
1267
+ REQUIRE(next_item.error() == couchbase::errc::key_value::range_scan_completed);
1268
+ REQUIRE(result->is_cancelled());
1269
+ }
1270
+
1271
+ TEST_CASE("integration: manager prefix scan with concurrency 0 (invalid argument)", "[integration]")
1272
+ {
1273
+ test::utils::integration_test_guard integration;
1274
+
1275
+ if (!integration.has_bucket_capability("range_scan")) {
1276
+ SKIP("cluster does not support range_scan");
1277
+ }
1278
+
1279
+ auto collection = couchbase::cluster(integration.cluster)
1280
+ .bucket(integration.ctx.bucket)
1281
+ .scope(couchbase::scope::default_name)
1282
+ .collection(couchbase::collection::default_name);
1283
+
1284
+ auto ids = make_doc_ids(100, "prefixscaninvalidconcurrency-");
1285
+ auto value = make_binary_value(1);
1286
+ auto mutations = populate_documents_for_range_scan(collection, ids, value, std::chrono::seconds{ 30 });
1287
+
1288
+ auto vbucket_map = get_vbucket_map(integration);
1289
+
1290
+ auto ag = couchbase::core::agent_group(integration.io, { { integration.cluster } });
1291
+ ag.open_bucket(integration.ctx.bucket);
1292
+ auto agent = ag.get_agent(integration.ctx.bucket);
1293
+ REQUIRE(agent.has_value());
1294
+
1295
+ couchbase::core::prefix_scan scan{ "prefixscaninvalidconcurrency" };
1296
+ couchbase::core::range_scan_orchestrator_options options{};
1297
+ options.consistent_with = mutations_to_mutation_state(mutations);
1298
+ options.ids_only = true;
1299
+ options.concurrency = 0;
1300
+ couchbase::core::range_scan_orchestrator orchestrator(
1301
+ integration.io, agent.value(), vbucket_map, couchbase::scope::default_name, couchbase::collection::default_name, scan, options);
1302
+
1303
+ auto result = orchestrator.scan();
1304
+ REQUIRE(!result.has_value());
1305
+ REQUIRE(result.error() == couchbase::errc::common::invalid_argument);
1067
1306
  }