couchbase 3.0.0.alpha.1-universal-darwin-19 → 3.0.0.alpha.2-universal-darwin-19

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 (176) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests-6.0.3.yml +49 -0
  3. data/.github/workflows/tests.yml +47 -0
  4. data/.gitmodules +3 -0
  5. data/.idea/dictionaries/gem_terms.xml +5 -0
  6. data/.idea/inspectionProfiles/Project_Default.xml +1 -0
  7. data/.idea/vcs.xml +1 -0
  8. data/Gemfile +1 -0
  9. data/README.md +55 -2
  10. data/Rakefile +18 -0
  11. data/bin/init-cluster +62 -0
  12. data/bin/setup +1 -0
  13. data/couchbase.gemspec +3 -2
  14. data/examples/crud.rb +1 -2
  15. data/examples/managing_buckets.rb +47 -0
  16. data/examples/managing_collections.rb +58 -0
  17. data/examples/managing_query_indexes.rb +63 -0
  18. data/examples/query.rb +3 -2
  19. data/examples/query_with_consistency.rb +76 -0
  20. data/examples/subdocument.rb +23 -1
  21. data/ext/.clang-format +1 -1
  22. data/ext/.idea/dictionaries/couchbase_terms.xml +2 -0
  23. data/ext/.idea/vcs.xml +1 -0
  24. data/ext/CMakeLists.txt +30 -12
  25. data/ext/build_version.hxx.in +26 -0
  26. data/ext/couchbase/bucket.hxx +69 -8
  27. data/ext/couchbase/cluster.hxx +70 -54
  28. data/ext/couchbase/collections_manifest.hxx +3 -3
  29. data/ext/couchbase/configuration.hxx +14 -0
  30. data/ext/couchbase/couchbase.cxx +2044 -383
  31. data/ext/couchbase/{operations/document_id.hxx → document_id.hxx} +5 -4
  32. data/ext/couchbase/io/http_message.hxx +5 -1
  33. data/ext/couchbase/io/http_parser.hxx +2 -1
  34. data/ext/couchbase/io/http_session.hxx +6 -3
  35. data/ext/couchbase/io/{binary_message.hxx → mcbp_message.hxx} +15 -12
  36. data/ext/couchbase/io/mcbp_parser.hxx +99 -0
  37. data/ext/couchbase/io/{key_value_session.hxx → mcbp_session.hxx} +200 -95
  38. data/ext/couchbase/io/session_manager.hxx +37 -22
  39. data/ext/couchbase/mutation_token.hxx +2 -1
  40. data/ext/couchbase/operations.hxx +38 -8
  41. data/ext/couchbase/operations/bucket_create.hxx +138 -0
  42. data/ext/couchbase/operations/bucket_drop.hxx +65 -0
  43. data/ext/couchbase/operations/bucket_flush.hxx +65 -0
  44. data/ext/couchbase/operations/bucket_get.hxx +69 -0
  45. data/ext/couchbase/operations/bucket_get_all.hxx +62 -0
  46. data/ext/couchbase/operations/bucket_settings.hxx +111 -0
  47. data/ext/couchbase/operations/bucket_update.hxx +115 -0
  48. data/ext/couchbase/operations/cluster_developer_preview_enable.hxx +60 -0
  49. data/ext/couchbase/operations/collection_create.hxx +86 -0
  50. data/ext/couchbase/operations/collection_drop.hxx +82 -0
  51. data/ext/couchbase/operations/command.hxx +10 -10
  52. data/ext/couchbase/operations/document_decrement.hxx +80 -0
  53. data/ext/couchbase/operations/document_exists.hxx +80 -0
  54. data/ext/couchbase/operations/{get.hxx → document_get.hxx} +4 -2
  55. data/ext/couchbase/operations/document_get_and_lock.hxx +64 -0
  56. data/ext/couchbase/operations/document_get_and_touch.hxx +64 -0
  57. data/ext/couchbase/operations/document_increment.hxx +80 -0
  58. data/ext/couchbase/operations/document_insert.hxx +74 -0
  59. data/ext/couchbase/operations/{lookup_in.hxx → document_lookup_in.hxx} +2 -2
  60. data/ext/couchbase/operations/{mutate_in.hxx → document_mutate_in.hxx} +11 -2
  61. data/ext/couchbase/operations/{query.hxx → document_query.hxx} +101 -6
  62. data/ext/couchbase/operations/document_remove.hxx +67 -0
  63. data/ext/couchbase/operations/document_replace.hxx +76 -0
  64. data/ext/couchbase/operations/{upsert.hxx → document_touch.hxx} +14 -14
  65. data/ext/couchbase/operations/{remove.hxx → document_unlock.hxx} +12 -10
  66. data/ext/couchbase/operations/document_upsert.hxx +74 -0
  67. data/ext/couchbase/operations/query_index_build_deferred.hxx +85 -0
  68. data/ext/couchbase/operations/query_index_create.hxx +134 -0
  69. data/ext/couchbase/operations/query_index_drop.hxx +108 -0
  70. data/ext/couchbase/operations/query_index_get_all.hxx +106 -0
  71. data/ext/couchbase/operations/scope_create.hxx +81 -0
  72. data/ext/couchbase/operations/scope_drop.hxx +79 -0
  73. data/ext/couchbase/operations/scope_get_all.hxx +72 -0
  74. data/ext/couchbase/protocol/client_opcode.hxx +35 -0
  75. data/ext/couchbase/protocol/client_request.hxx +56 -9
  76. data/ext/couchbase/protocol/client_response.hxx +52 -15
  77. data/ext/couchbase/protocol/cmd_cluster_map_change_notification.hxx +81 -0
  78. data/ext/couchbase/protocol/cmd_decrement.hxx +187 -0
  79. data/ext/couchbase/protocol/cmd_exists.hxx +171 -0
  80. data/ext/couchbase/protocol/cmd_get.hxx +31 -8
  81. data/ext/couchbase/protocol/cmd_get_and_lock.hxx +142 -0
  82. data/ext/couchbase/protocol/cmd_get_and_touch.hxx +142 -0
  83. data/ext/couchbase/protocol/cmd_get_cluster_config.hxx +16 -3
  84. data/ext/couchbase/protocol/cmd_get_collections_manifest.hxx +16 -3
  85. data/ext/couchbase/protocol/cmd_get_error_map.hxx +16 -3
  86. data/ext/couchbase/protocol/cmd_hello.hxx +24 -8
  87. data/ext/couchbase/protocol/cmd_increment.hxx +187 -0
  88. data/ext/couchbase/protocol/cmd_info.hxx +1 -0
  89. data/ext/couchbase/protocol/cmd_insert.hxx +172 -0
  90. data/ext/couchbase/protocol/cmd_lookup_in.hxx +28 -13
  91. data/ext/couchbase/protocol/cmd_mutate_in.hxx +65 -13
  92. data/ext/couchbase/protocol/cmd_remove.hxx +59 -4
  93. data/ext/couchbase/protocol/cmd_replace.hxx +172 -0
  94. data/ext/couchbase/protocol/cmd_sasl_auth.hxx +15 -3
  95. data/ext/couchbase/protocol/cmd_sasl_list_mechs.hxx +15 -3
  96. data/ext/couchbase/protocol/cmd_sasl_step.hxx +15 -3
  97. data/ext/couchbase/protocol/cmd_select_bucket.hxx +14 -2
  98. data/ext/couchbase/protocol/cmd_touch.hxx +102 -0
  99. data/ext/couchbase/protocol/cmd_unlock.hxx +95 -0
  100. data/ext/couchbase/protocol/cmd_upsert.hxx +50 -14
  101. data/ext/couchbase/protocol/durability_level.hxx +67 -0
  102. data/ext/couchbase/protocol/frame_info_id.hxx +187 -0
  103. data/ext/couchbase/protocol/hello_feature.hxx +137 -0
  104. data/ext/couchbase/protocol/server_opcode.hxx +57 -0
  105. data/ext/couchbase/protocol/server_request.hxx +122 -0
  106. data/ext/couchbase/protocol/unsigned_leb128.h +15 -15
  107. data/ext/couchbase/utils/byteswap.hxx +1 -2
  108. data/ext/couchbase/utils/url_codec.hxx +225 -0
  109. data/ext/couchbase/version.hxx +3 -1
  110. data/ext/extconf.rb +4 -1
  111. data/ext/test/main.cxx +37 -113
  112. data/ext/third_party/snappy/.appveyor.yml +36 -0
  113. data/ext/third_party/snappy/.gitignore +8 -0
  114. data/ext/third_party/snappy/.travis.yml +98 -0
  115. data/ext/third_party/snappy/AUTHORS +1 -0
  116. data/ext/third_party/snappy/CMakeLists.txt +345 -0
  117. data/ext/third_party/snappy/CONTRIBUTING.md +26 -0
  118. data/ext/third_party/snappy/COPYING +54 -0
  119. data/ext/third_party/snappy/NEWS +188 -0
  120. data/ext/third_party/snappy/README.md +148 -0
  121. data/ext/third_party/snappy/cmake/SnappyConfig.cmake.in +33 -0
  122. data/ext/third_party/snappy/cmake/config.h.in +59 -0
  123. data/ext/third_party/snappy/docs/README.md +72 -0
  124. data/ext/third_party/snappy/format_description.txt +110 -0
  125. data/ext/third_party/snappy/framing_format.txt +135 -0
  126. data/ext/third_party/snappy/snappy-c.cc +90 -0
  127. data/ext/third_party/snappy/snappy-c.h +138 -0
  128. data/ext/third_party/snappy/snappy-internal.h +315 -0
  129. data/ext/third_party/snappy/snappy-sinksource.cc +121 -0
  130. data/ext/third_party/snappy/snappy-sinksource.h +182 -0
  131. data/ext/third_party/snappy/snappy-stubs-internal.cc +42 -0
  132. data/ext/third_party/snappy/snappy-stubs-internal.h +493 -0
  133. data/ext/third_party/snappy/snappy-stubs-public.h.in +63 -0
  134. data/ext/third_party/snappy/snappy-test.cc +613 -0
  135. data/ext/third_party/snappy/snappy-test.h +526 -0
  136. data/ext/third_party/snappy/snappy.cc +1770 -0
  137. data/ext/third_party/snappy/snappy.h +209 -0
  138. data/ext/third_party/snappy/snappy_compress_fuzzer.cc +60 -0
  139. data/ext/third_party/snappy/snappy_uncompress_fuzzer.cc +58 -0
  140. data/ext/third_party/snappy/snappy_unittest.cc +1512 -0
  141. data/ext/third_party/snappy/testdata/alice29.txt +3609 -0
  142. data/ext/third_party/snappy/testdata/asyoulik.txt +4122 -0
  143. data/ext/third_party/snappy/testdata/baddata1.snappy +0 -0
  144. data/ext/third_party/snappy/testdata/baddata2.snappy +0 -0
  145. data/ext/third_party/snappy/testdata/baddata3.snappy +0 -0
  146. data/ext/third_party/snappy/testdata/fireworks.jpeg +0 -0
  147. data/ext/third_party/snappy/testdata/geo.protodata +0 -0
  148. data/ext/third_party/snappy/testdata/html +1 -0
  149. data/ext/third_party/snappy/testdata/html_x_4 +1 -0
  150. data/ext/third_party/snappy/testdata/kppkn.gtb +0 -0
  151. data/ext/third_party/snappy/testdata/lcet10.txt +7519 -0
  152. data/ext/third_party/snappy/testdata/paper-100k.pdf +600 -2
  153. data/ext/third_party/snappy/testdata/plrabn12.txt +10699 -0
  154. data/ext/third_party/snappy/testdata/urls.10K +10000 -0
  155. data/lib/couchbase/binary_collection.rb +33 -76
  156. data/lib/couchbase/binary_collection_options.rb +94 -0
  157. data/lib/couchbase/bucket.rb +9 -3
  158. data/lib/couchbase/cluster.rb +161 -23
  159. data/lib/couchbase/collection.rb +108 -191
  160. data/lib/couchbase/collection_options.rb +430 -0
  161. data/lib/couchbase/errors.rb +136 -134
  162. data/lib/couchbase/json_transcoder.rb +32 -0
  163. data/lib/couchbase/management/analytics_index_manager.rb +185 -9
  164. data/lib/couchbase/management/bucket_manager.rb +84 -33
  165. data/lib/couchbase/management/collection_manager.rb +166 -1
  166. data/lib/couchbase/management/query_index_manager.rb +261 -0
  167. data/lib/couchbase/management/search_index_manager.rb +291 -0
  168. data/lib/couchbase/management/user_manager.rb +12 -10
  169. data/lib/couchbase/management/view_index_manager.rb +151 -1
  170. data/lib/couchbase/mutation_state.rb +11 -1
  171. data/lib/couchbase/scope.rb +4 -4
  172. data/lib/couchbase/version.rb +1 -1
  173. metadata +113 -18
  174. data/.travis.yml +0 -7
  175. data/ext/couchbase/io/binary_parser.hxx +0 -64
  176. data/lib/couchbase/results.rb +0 -307
@@ -0,0 +1,63 @@
1
+ require 'couchbase'
2
+ include Couchbase
3
+
4
+ def measure(msg)
5
+ start = Time.now
6
+ yield
7
+ printf "%s in %.2f seconds\n", msg, Time.now - start
8
+ end
9
+
10
+ def display_indexes(indexes, bucket_name)
11
+ puts "There are #{indexes.size} query indexes in the bucket \"#{bucket_name}\":"
12
+ indexes.each do |index|
13
+ print " * [#{index.state}] #{index.name}"
14
+ if index.primary?
15
+ print " (primary)"
16
+ end
17
+ unless index.index_key.empty?
18
+ print " on [#{index.index_key.join(", ")}]"
19
+ end
20
+ if index.condition
21
+ print " where #{index.condition}"
22
+ end
23
+ puts
24
+ end
25
+ end
26
+
27
+ bucket_name = "beer-sample"
28
+
29
+ options = Cluster::ClusterOptions.new
30
+ options.authenticate("Administrator", "password")
31
+ cluster = Cluster.connect("couchbase://192.168.42.101", options)
32
+
33
+ display_indexes(cluster.query_indexes.get_all_indexes(bucket_name), bucket_name)
34
+
35
+ index_name = "demo_index"
36
+ options = Management::QueryIndexManager::DropIndexOptions.new
37
+ options.ignore_if_does_not_exist = true
38
+ measure("Index \"#{index_name}\" has been dropped") { cluster.query_indexes.drop_index(bucket_name, index_name, options) }
39
+
40
+ options = Management::QueryIndexManager::CreateIndexOptions.new
41
+ options.ignore_if_exists = true
42
+ options.condition = "abv > 2"
43
+ measure("Index \"#{index_name}\" has been created") { cluster.query_indexes.create_index(bucket_name, index_name, %w[`type` `name`], options) }
44
+
45
+ options = Management::QueryIndexManager::DropPrimaryIndexOptions.new
46
+ options.ignore_if_does_not_exist = true
47
+ measure("Primary index \"#{bucket_name}\" has been dropped") { cluster.query_indexes.drop_primary_index(bucket_name, options) }
48
+
49
+ options = Management::QueryIndexManager::CreatePrimaryIndexOptions.new
50
+ options.deferred = true
51
+ measure("Primary index \"#{bucket_name}\" has been created") { cluster.query_indexes.create_primary_index(bucket_name, options) }
52
+
53
+ display_indexes(cluster.query_indexes.get_all_indexes(bucket_name), bucket_name)
54
+
55
+ measure("Build of the indexes for \"#{bucket_name}\" has been triggered") { cluster.query_indexes.build_deferred_indexes(bucket_name) }
56
+
57
+ display_indexes(cluster.query_indexes.get_all_indexes(bucket_name), bucket_name)
58
+
59
+ options = Management::QueryIndexManager::WatchIndexesOptions.new
60
+ options.watch_primary = true
61
+ measure("Watching for primary index build completion for \"#{bucket_name}\" has been finished") { cluster.query_indexes.watch_indexes(bucket_name, [], 10_000_000, options) }
62
+
63
+ display_indexes(cluster.query_indexes.get_all_indexes(bucket_name), bucket_name)
@@ -1,5 +1,4 @@
1
1
  require 'couchbase'
2
- require 'byebug'
3
2
 
4
3
  include Couchbase
5
4
 
@@ -7,11 +6,13 @@ options = Cluster::ClusterOptions.new
7
6
  options.authenticate("Administrator", "password")
8
7
  cluster = Cluster.connect("couchbase://localhost", options)
9
8
 
9
+ cluster.bucket("travel-sample") # this is necessary for 6.0.x
10
+
10
11
  options = Cluster::QueryOptions.new
11
12
  options.named_parameters({type: "hotel"})
12
13
  options.metrics = true
13
14
  res = cluster.query("SELECT * FROM `travel-sample` WHERE type = $type LIMIT 10", options)
14
15
  res.rows.each do |row|
15
- puts "#{row['country']}. #{row['name']}"
16
+ puts "#{row["travel-sample"]["country"]}. #{row["travel-sample"]["name"]}"
16
17
  end
17
18
  puts "Status: #{res.meta_data.status}. Execution time: #{res.meta_data.metrics.execution_time}"
@@ -0,0 +1,76 @@
1
+ require 'couchbase'
2
+
3
+ include Couchbase
4
+
5
+ options = Cluster::ClusterOptions.new
6
+ options.authenticate("Administrator", "password")
7
+ cluster = Cluster.connect("couchbase://localhost", options)
8
+
9
+ options = Management::QueryIndexManager::CreatePrimaryIndexOptions.new
10
+ options.ignore_if_exists = true
11
+ cluster.query_indexes.create_primary_index("default", options)
12
+
13
+ collection = cluster.bucket("default").default_collection
14
+
15
+ # METHOD 1
16
+ #
17
+ # The easiest way to enforce consistency is just set scan_consistency option to :request_plus
18
+ # Note: it waits until all documents queued to be indexed when the request has been receive,
19
+ # will be proceed and stored in the index. Often more granular approach is possible (skip to the next section)
20
+ puts "METHOD 1: using :request_plus"
21
+
22
+ random_number = rand(0..1_000_000_000)
23
+ collection.upsert("user:#{random_number}", {
24
+ "name" => ["Brass", "Doorknob"],
25
+ "email" => "brass.doorknob@example.com",
26
+ "data" => random_number,
27
+ })
28
+
29
+ options = Cluster::QueryOptions.new
30
+ options.timeout = 10_000
31
+ options.positional_parameters(["Brass"])
32
+ options.scan_consistency = :request_plus
33
+ res = cluster.query("SELECT name, email, data, META(`default`).id FROM `default` WHERE $1 IN name", options)
34
+ res.rows.each do |row|
35
+ puts "Name: #{row["name"].join(" ")}, e-mail: #{row["email"]}, data: #{row["data"]}"
36
+ if row["data"] == random_number
37
+ puts "--- Found our newly created document!"
38
+ end
39
+ if ENV['REMOVE_DOOR_KNOBS']
40
+ puts "Removing #{row["id"]} (requested via env)"
41
+ collection.remove(row["id"])
42
+ end
43
+ end
44
+
45
+ # METHOD 2
46
+ #
47
+ # More granular and light-weight approach is to use AT_PLUS consistency.
48
+ # The application need to to know exactly which mutation affect the result,
49
+ # and supply mutation tokens from those operations.
50
+ puts "METHOD 2: using MutationState"
51
+
52
+ random_number = rand(0..1_000_000_000)
53
+ res = collection.upsert("user:#{random_number}", {
54
+ "name" => ["Brass", "Doorknob"],
55
+ "email" => "brass.doorknob@example.com",
56
+ "data" => random_number,
57
+ })
58
+
59
+ state = MutationState.new(res.mutation_token)
60
+ # state.add(*tokens) could be used to add more tokens
61
+
62
+ options = Cluster::QueryOptions.new
63
+ options.timeout = 10_000
64
+ options.positional_parameters(["Brass"])
65
+ options.consistent_with(state)
66
+ res = cluster.query("SELECT name, email, data, META(`default`).id FROM `default` WHERE $1 IN name", options)
67
+ res.rows.each do |row|
68
+ puts "Name: #{row["name"].join(" ")}, e-mail: #{row["email"]}, data: #{row["data"]}"
69
+ if row["data"] == random_number
70
+ puts "--- Found our newly created document!"
71
+ end
72
+ if ENV['REMOVE_DOOR_KNOBS']
73
+ puts "Removing #{row["id"]} (requested via env)"
74
+ collection.remove(row["id"])
75
+ end
76
+ end
@@ -7,6 +7,28 @@ cluster = Cluster.connect("couchbase://localhost", options)
7
7
  bucket = cluster.bucket("default")
8
8
  collection = bucket.default_collection
9
9
 
10
+ document = {
11
+ name: "Douglas Reynholm",
12
+ email: "douglas@reynholmindustries.com",
13
+ addresses: {
14
+ billing: {
15
+ line1: "123 Any Street",
16
+ line2: "Anytown",
17
+ country: "United Kingdom"
18
+ },
19
+ delivery: {
20
+ line1: "123 Any Street",
21
+ line2: "Anytown",
22
+ country: "United Kingdom"
23
+ }
24
+ },
25
+ purchases: {
26
+ complete: [339, 976, 442, 666],
27
+ abandoned: [157, 42, 999]
28
+ }
29
+ }
30
+ collection.upsert("customer123", document)
31
+
10
32
  res = collection.mutate_in("customer123", [
11
33
  MutateInSpec.dict_upsert("fax", "311-555-0151")
12
34
  ])
@@ -19,7 +41,7 @@ res = collection.mutate_in("customer123", [
19
41
  ])
20
42
  puts "The document has been modified successfully: cas=#{res.cas}"
21
43
 
22
- res = collection.lookup_in"customer123", [
44
+ res = collection.lookup_in "customer123", [
23
45
  LookupInSpec.get("addresses.delivery.country"),
24
46
  LookupInSpec.exists("purchases.pending[-1]"),
25
47
  ]
@@ -8,7 +8,7 @@ UseTab: Never
8
8
  IndentWidth: 4
9
9
  ColumnLimit: 140
10
10
  AllowShortFunctionsOnASingleLine: None
11
- AllowShortBlocksOnASingleLine: false
11
+ AllowShortBlocksOnASingleLine: Empty
12
12
  AllowShortCaseLabelsOnASingleLine: false
13
13
  BreakBeforeBraces: Linux
14
14
  SortIncludes: false
@@ -2,7 +2,9 @@
2
2
  <dictionary name="couchbase_terms">
3
3
  <words>
4
4
  <w>dataverse</w>
5
+ <w>keyspace</w>
5
6
  <w>opcode</w>
7
+ <w>sasl</w>
6
8
  <w>subdoc</w>
7
9
  <w>upsert</w>
8
10
  <w>vbucket</w>
@@ -6,6 +6,7 @@
6
6
  <mapping directory="$PROJECT_DIR$/third_party/gsl" vcs="Git" />
7
7
  <mapping directory="$PROJECT_DIR$/third_party/http_parser" vcs="Git" />
8
8
  <mapping directory="$PROJECT_DIR$/third_party/json" vcs="Git" />
9
+ <mapping directory="$PROJECT_DIR$/third_party/snappy" vcs="Git" />
9
10
  <mapping directory="$PROJECT_DIR$/third_party/spdlog" vcs="Git" />
10
11
  </component>
11
12
  </project>
@@ -11,6 +11,7 @@ endif()
11
11
  add_subdirectory(third_party/gsl)
12
12
  add_subdirectory(third_party/json)
13
13
  add_subdirectory(third_party/spdlog)
14
+ add_subdirectory(third_party/snappy)
14
15
 
15
16
  include_directories(BEFORE SYSTEM third_party/gsl/include)
16
17
  include_directories(BEFORE SYSTEM third_party/asio/asio/include)
@@ -44,6 +45,13 @@ else()
44
45
  target_link_libraries(project_options INTERFACE -fsanitize=address)
45
46
  endif()
46
47
 
48
+ option(ENABLE_TSAN "Enable thread sanitizer" FALSE)
49
+
50
+ if(ENABLE_TSAN)
51
+ target_compile_options(project_options INTERFACE -fsanitize=thread)
52
+ target_link_libraries(project_options INTERFACE -fsanitize=thread)
53
+ endif()
54
+
47
55
  target_compile_options(
48
56
  project_warnings
49
57
  INTERFACE -Wall
@@ -107,9 +115,22 @@ else()
107
115
  message(STATUS "RUBY_EXECUTABLE: ${RUBY_EXECUTABLE}")
108
116
  endif()
109
117
  message(STATUS "RUBY_INCLUDE_DIR: ${RUBY_INCLUDE_DIR}")
118
+ message(STATUS "RUBY_LIBRARY: ${RUBY_LIBRARY}")
110
119
  include_directories(BEFORE SYSTEM "${RUBY_INCLUDE_DIR}")
111
120
 
121
+ find_program(GIT git)
122
+ if(GIT)
123
+ execute_process(
124
+ COMMAND git rev-parse HEAD
125
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
126
+ OUTPUT_STRIP_TRAILING_WHITESPACE
127
+ OUTPUT_VARIABLE BACKEND_GIT_REVISION)
128
+ endif()
129
+
130
+ string(TIMESTAMP BACKEND_BUILD_TIMESTAMP "%Y-%m-%d %H:%M:%S" UTC)
131
+ configure_file(${PROJECT_SOURCE_DIR}/build_version.hxx.in ${PROJECT_BINARY_DIR}/generated/build_version.hxx @ONLY)
112
132
  add_library(couchbase SHARED couchbase/couchbase.cxx)
133
+ target_include_directories(couchbase PRIVATE ${PROJECT_BINARY_DIR}/generated)
113
134
  target_link_libraries(
114
135
  couchbase
115
136
  PRIVATE project_options
@@ -120,8 +141,9 @@ target_link_libraries(
120
141
  cbcrypto
121
142
  cbsasl
122
143
  http_parser
144
+ snappy
123
145
  spdlog::spdlog_header_only)
124
- set_target_properties(cbcrypto cbsasl platform PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
146
+ set_target_properties(cbcrypto cbsasl platform snappy PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
125
147
 
126
148
  if(APPLE)
127
149
  target_link_options(couchbase PRIVATE -Wl,-undefined,dynamic_lookup)
@@ -130,16 +152,12 @@ else()
130
152
  endif()
131
153
 
132
154
  if(BUILD_EXAMPLES)
155
+ file(
156
+ GENERATE
157
+ OUTPUT ${PROJECT_BINARY_DIR}/generated/generated_config.hxx
158
+ CONTENT "#pragma once\n#define LIBCOUCHBASE_EXT_PATH \"$<TARGET_FILE:couchbase>\"")
133
159
  add_executable(main test/main.cxx)
134
- target_link_libraries(
135
- main
136
- PRIVATE project_options
137
- project_warnings
138
- OpenSSL::SSL
139
- OpenSSL::Crypto
140
- platform
141
- cbcrypto
142
- cbsasl
143
- http_parser
144
- spdlog::spdlog_header_only)
160
+ target_include_directories(main PRIVATE ${PROJECT_BINARY_DIR}/generated)
161
+ target_link_libraries(main PRIVATE project_options project_warnings ${RUBY_LIBRARY} spdlog::spdlog_header_only)
162
+ add_dependencies(main couchbase)
145
163
  endif()
@@ -0,0 +1,26 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #define BACKEND_BUILD_TIMESTAMP "@BACKEND_BUILD_TIMESTAMP@"
21
+ #define BACKEND_CXX_COMPILER "@CMAKE_CXX_COMPILER_ID@ @CMAKE_CXX_COMPILER_VERSION@"
22
+ #define BACKEND_C_COMPILER "@CMAKE_C_COMPILER_ID@ @CMAKE_C_COMPILER_VERSION@"
23
+ #define BACKEND_SYSTEM "@CMAKE_SYSTEM@"
24
+ #define BACKEND_SYSTEM_PROCESSOR "@CMAKE_SYSTEM_PROCESSOR@"
25
+ #define BACKEND_GIT_REVISION "5b91db73c89347cfb4985490051aea4d82f56c0e"
26
+
@@ -35,41 +35,102 @@ class bucket
35
35
  {
36
36
  }
37
37
 
38
+ ~bucket()
39
+ {
40
+ close();
41
+ }
42
+
38
43
  [[nodiscard]] const std::string& name() const
39
44
  {
40
45
  return name_;
41
46
  }
42
47
 
43
- void set_node(size_t index, std::shared_ptr<io::key_value_session> session)
48
+ void set_node(size_t index, std::shared_ptr<io::mcbp_session> session)
44
49
  {
50
+ if (closed_) {
51
+ return;
52
+ }
53
+
54
+ auto session_manifest = session->manifest();
55
+ if (!manifest_cache_) {
56
+ if (session_manifest) {
57
+ manifest_cache_ = session_manifest.value();
58
+ }
59
+ } else {
60
+ if (session_manifest && session_manifest->uid > manifest_cache_->uid) {
61
+ manifest_cache_ = session->manifest().value();
62
+ }
63
+ }
45
64
  sessions_.emplace(index, std::move(session));
46
65
  }
47
66
 
48
- [[nodiscard]] std::shared_ptr<io::key_value_session> get_node(size_t index)
67
+ void remove_node(size_t index)
49
68
  {
50
- return sessions_.at(index);
69
+ if (closed_) {
70
+ return;
71
+ }
72
+ sessions_.erase(index);
51
73
  }
52
74
 
53
75
  template<typename Request, typename Handler>
54
76
  void execute(Request request, Handler&& handler)
55
77
  {
56
- auto cmd = std::make_shared<operations::command<Request>>(ctx_, std::move(request));
78
+ if (closed_) {
79
+ return;
80
+ }
57
81
  size_t index = 0;
58
- std::tie(cmd->request.partition, index) = config_.map_key(request.id.key);
59
- cmd->send_to(sessions_.at(index), std::forward<Handler>(handler));
82
+ std::tie(request.partition, index) = config_.map_key(request.id.key);
83
+ auto session = sessions_.at(index);
84
+ if (manifest_cache_) {
85
+ request.id.collection_uid = get_collection_uid(request.id.collection);
86
+ } else {
87
+ if (!request.id.collection.empty() && request.id.collection != "_default._default") {
88
+ handler(make_response(std::make_error_code(error::common_errc::unsupported_operation), request, {}));
89
+ return;
90
+ }
91
+ }
92
+ auto cmd = std::make_shared<operations::command<Request>>(ctx_, std::move(request));
93
+ cmd->send_to(session, std::forward<Handler>(handler));
60
94
  }
61
95
 
62
96
  void close()
63
97
  {
98
+ if (closed_) {
99
+ return;
100
+ }
101
+ closed_ = true;
64
102
  for (auto& session : sessions_) {
65
103
  session.second->stop();
66
104
  }
67
105
  }
68
106
 
69
107
  private:
108
+ [[nodiscard]] uint32_t get_collection_uid(const std::string& collection_path)
109
+ {
110
+ Expects(manifest_cache_.has_value());
111
+ Expects(!collection_path.empty());
112
+ auto dot = collection_path.find('.');
113
+ Expects(dot != std::string::npos);
114
+ std::string scope = collection_path.substr(0, dot);
115
+ std::string collection = collection_path.substr(dot + 1);
116
+ for (const auto& s : manifest_cache_->scopes) {
117
+ if (s.name == scope) {
118
+ for (const auto& c : s.collections) {
119
+ if (c.name == collection) {
120
+ return gsl::narrow_cast<std::uint32_t>(c.uid);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ Ensures(false); // FIXME: return collection not found
126
+ return 0;
127
+ }
128
+
70
129
  asio::io_context& ctx_;
71
130
  std::string name_;
72
131
  configuration config_;
73
- std::map<size_t, std::shared_ptr<io::key_value_session>> sessions_;
132
+ bool closed_{ false };
133
+ std::optional<collections_manifest> manifest_cache_{};
134
+ std::map<size_t, std::shared_ptr<io::mcbp_session>> sessions_{};
74
135
  };
75
- } // namespace couchbase
136
+ } // namespace couchbase
@@ -20,11 +20,11 @@
20
20
  #include <utility>
21
21
  #include <thread>
22
22
 
23
- #include <io/key_value_session.hxx>
23
+ #include <io/mcbp_session.hxx>
24
24
  #include <io/session_manager.hxx>
25
25
  #include <bucket.hxx>
26
26
  #include <operations.hxx>
27
- #include <operations/query.hxx>
27
+ #include <operations/document_query.hxx>
28
28
 
29
29
  namespace couchbase
30
30
  {
@@ -56,15 +56,14 @@ class cluster
56
56
  : id_(uuid::random())
57
57
  , ctx_(ctx)
58
58
  , work_(asio::make_work_guard(ctx_))
59
- , session_(std::make_shared<io::key_value_session>(id_, ctx_))
60
- , session_manager_(id_, ctx_)
59
+ , session_(std::make_shared<io::mcbp_session>(id_, ctx_))
60
+ , session_manager_(std::make_shared<io::session_manager>(id_, ctx_))
61
61
  {
62
62
  }
63
63
 
64
64
  ~cluster()
65
65
  {
66
66
  work_.reset();
67
- close();
68
67
  }
69
68
 
70
69
  template<typename Handler>
@@ -72,16 +71,26 @@ class cluster
72
71
  {
73
72
  auto address = origin.get_address();
74
73
  origin_ = origin;
75
- session_->bootstrap(address.first, address.second, origin.get_username(), origin.get_password(), std::forward<Handler>(handler));
76
- session_->subscribe_to_configuration_updates([this](const configuration& config) { session_manager_.set_configuration(config); });
74
+ session_->bootstrap(address.first,
75
+ address.second,
76
+ origin.get_username(),
77
+ origin.get_password(),
78
+ [this, handler = std::forward<Handler>(handler)](std::error_code ec, configuration config) mutable {
79
+ session_manager_->set_configuration(config);
80
+ handler(ec);
81
+ });
77
82
  }
78
83
 
79
- void close()
84
+ template<typename Handler>
85
+ void close(Handler&& handler)
80
86
  {
81
- session_->stop();
82
- for (auto& bucket : buckets_) {
83
- bucket.second.close();
84
- }
87
+ asio::post(asio::bind_executor(ctx_, [this, handler = std::forward<Handler>(handler)]() {
88
+ session_->stop();
89
+ for (auto& bucket : buckets_) {
90
+ bucket.second->close();
91
+ }
92
+ handler();
93
+ }));
85
94
  }
86
95
 
87
96
  template<typename Handler>
@@ -92,38 +101,42 @@ class cluster
92
101
  }
93
102
  configuration config = session_->config();
94
103
  auto& node = config.nodes.front();
95
- auto new_session = std::make_shared<io::key_value_session>(id_, ctx_, bucket_name);
96
- new_session->bootstrap(node.hostname,
97
- std::to_string(*node.services_plain.key_value),
98
- origin_.get_username(),
99
- origin_.get_password(),
100
- [this, name = bucket_name, new_session, h = std::forward<Handler>(handler)](std::error_code ec) mutable {
101
- if (!ec) {
102
- bucket b(ctx_, name, new_session->config());
103
- size_t this_index = new_session->index();
104
- if (new_session->config().nodes.size() > 1) {
105
- for (const auto& n : new_session->config().nodes) {
106
- if (n.index != this_index) {
107
- auto s = std::make_shared<io::key_value_session>(id_, ctx_, name);
108
- s->bootstrap(n.hostname,
109
- std::to_string(*n.services_plain.key_value),
110
- origin_.get_username(),
111
- origin_.get_password(),
112
- [s, i = n.index, &b](std::error_code err) {
113
- if (err) {
114
- spdlog::warn("unable to bootstrap node: {}", err.message());
115
- } else {
116
- b.set_node(i, s);
117
- }
118
- });
104
+ auto new_session = std::make_shared<io::mcbp_session>(id_, ctx_, bucket_name);
105
+ new_session->bootstrap(
106
+ node.hostname,
107
+ std::to_string(*node.services_plain.key_value),
108
+ origin_.get_username(),
109
+ origin_.get_password(),
110
+ [this, name = bucket_name, new_session, h = std::forward<Handler>(handler)](std::error_code ec, configuration cfg) mutable {
111
+ if (!ec) {
112
+ if (!session_->supports_gcccp()) {
113
+ session_manager_->set_configuration(cfg);
114
+ }
115
+ auto b = std::make_shared<bucket>(ctx_, name, cfg);
116
+ size_t this_index = new_session->index();
117
+ b->set_node(this_index, new_session);
118
+ if (cfg.nodes.size() > 1) {
119
+ for (const auto& n : cfg.nodes) {
120
+ if (n.index != this_index) {
121
+ auto s = std::make_shared<io::mcbp_session>(id_, ctx_, name);
122
+ b->set_node(n.index, s);
123
+ s->bootstrap(n.hostname,
124
+ std::to_string(*n.services_plain.key_value),
125
+ origin_.get_username(),
126
+ origin_.get_password(),
127
+ [s, i = n.index, b](std::error_code err, configuration /*config*/) {
128
+ if (err) {
129
+ spdlog::warn("unable to bootstrap node: {}", err.message());
130
+ b->remove_node(i);
119
131
  }
120
- }
121
- }
122
- b.set_node(this_index, std::move(new_session));
123
- buckets_.emplace(name, std::move(b));
124
- }
125
- h(ec);
126
- });
132
+ });
133
+ }
134
+ }
135
+ }
136
+ buckets_.emplace(name, std::move(b));
137
+ }
138
+ h(ec);
139
+ });
127
140
  }
128
141
 
129
142
  template<class Request, class Handler>
@@ -133,17 +146,20 @@ class cluster
133
146
  if (bucket == buckets_.end()) {
134
147
  return handler(operations::make_response(std::make_error_code(error::common_errc::bucket_not_found), request, {}));
135
148
  }
136
- return bucket->second.execute(request, std::forward<Handler>(handler));
149
+ return bucket->second->execute(request, std::forward<Handler>(handler));
137
150
  }
138
151
 
139
- template<class Handler>
140
- void execute(operations::query_request request, Handler&& handler)
152
+ template<class Request, class Handler>
153
+ void execute_http(Request request, Handler&& handler)
141
154
  {
142
- auto session = session_manager_.check_out(service_type::query, origin_.username, origin_.password);
143
- auto cmd = std::make_shared<operations::command<operations::query_request>>(ctx_, std::move(request));
144
- cmd->send_to(session, [this, session, handler = std::forward<Handler>(handler)](operations::query_response resp) mutable {
155
+ auto session = session_manager_->check_out(Request::type, origin_.username, origin_.password);
156
+ if (!session) {
157
+ return handler(operations::make_response(std::make_error_code(error::common_errc::service_not_available), request, {}));
158
+ }
159
+ auto cmd = std::make_shared<operations::command<Request>>(ctx_, std::move(request));
160
+ cmd->send_to(session, [this, session, handler = std::forward<Handler>(handler)](typename Request::response_type resp) mutable {
145
161
  handler(std::move(resp));
146
- session_manager_.check_in(service_type::query, session);
162
+ session_manager_->check_in(Request::type, session);
147
163
  });
148
164
  }
149
165
 
@@ -151,9 +167,9 @@ class cluster
151
167
  uuid::uuid_t id_;
152
168
  asio::io_context& ctx_;
153
169
  asio::executor_work_guard<asio::io_context::executor_type> work_;
154
- std::shared_ptr<io::key_value_session> session_;
155
- io::session_manager session_manager_;
156
- std::map<std::string, bucket> buckets_;
170
+ std::shared_ptr<io::mcbp_session> session_;
171
+ std::shared_ptr<io::session_manager> session_manager_;
172
+ std::map<std::string, std::shared_ptr<bucket>> buckets_;
157
173
  origin origin_;
158
174
  };
159
- } // namespace couchbase
175
+ } // namespace couchbase