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

Sign up to get free protection for your applications and to get access to all the features.
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