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.
- checksums.yaml +4 -4
- data/.github/workflows/tests-6.0.3.yml +49 -0
- data/.github/workflows/tests.yml +47 -0
- data/.gitmodules +3 -0
- data/.idea/dictionaries/gem_terms.xml +5 -0
- data/.idea/inspectionProfiles/Project_Default.xml +1 -0
- data/.idea/vcs.xml +1 -0
- data/Gemfile +1 -0
- data/README.md +55 -2
- data/Rakefile +18 -0
- data/bin/init-cluster +62 -0
- data/bin/setup +1 -0
- data/couchbase.gemspec +3 -2
- data/examples/crud.rb +1 -2
- data/examples/managing_buckets.rb +47 -0
- data/examples/managing_collections.rb +58 -0
- data/examples/managing_query_indexes.rb +63 -0
- data/examples/query.rb +3 -2
- data/examples/query_with_consistency.rb +76 -0
- data/examples/subdocument.rb +23 -1
- data/ext/.clang-format +1 -1
- data/ext/.idea/dictionaries/couchbase_terms.xml +2 -0
- data/ext/.idea/vcs.xml +1 -0
- data/ext/CMakeLists.txt +30 -12
- data/ext/build_version.hxx.in +26 -0
- data/ext/couchbase/bucket.hxx +69 -8
- data/ext/couchbase/cluster.hxx +70 -54
- data/ext/couchbase/collections_manifest.hxx +3 -3
- data/ext/couchbase/configuration.hxx +14 -0
- data/ext/couchbase/couchbase.cxx +2044 -383
- data/ext/couchbase/{operations/document_id.hxx → document_id.hxx} +5 -4
- data/ext/couchbase/io/http_message.hxx +5 -1
- data/ext/couchbase/io/http_parser.hxx +2 -1
- data/ext/couchbase/io/http_session.hxx +6 -3
- data/ext/couchbase/io/{binary_message.hxx → mcbp_message.hxx} +15 -12
- data/ext/couchbase/io/mcbp_parser.hxx +99 -0
- data/ext/couchbase/io/{key_value_session.hxx → mcbp_session.hxx} +200 -95
- data/ext/couchbase/io/session_manager.hxx +37 -22
- data/ext/couchbase/mutation_token.hxx +2 -1
- data/ext/couchbase/operations.hxx +38 -8
- data/ext/couchbase/operations/bucket_create.hxx +138 -0
- data/ext/couchbase/operations/bucket_drop.hxx +65 -0
- data/ext/couchbase/operations/bucket_flush.hxx +65 -0
- data/ext/couchbase/operations/bucket_get.hxx +69 -0
- data/ext/couchbase/operations/bucket_get_all.hxx +62 -0
- data/ext/couchbase/operations/bucket_settings.hxx +111 -0
- data/ext/couchbase/operations/bucket_update.hxx +115 -0
- data/ext/couchbase/operations/cluster_developer_preview_enable.hxx +60 -0
- data/ext/couchbase/operations/collection_create.hxx +86 -0
- data/ext/couchbase/operations/collection_drop.hxx +82 -0
- data/ext/couchbase/operations/command.hxx +10 -10
- data/ext/couchbase/operations/document_decrement.hxx +80 -0
- data/ext/couchbase/operations/document_exists.hxx +80 -0
- data/ext/couchbase/operations/{get.hxx → document_get.hxx} +4 -2
- data/ext/couchbase/operations/document_get_and_lock.hxx +64 -0
- data/ext/couchbase/operations/document_get_and_touch.hxx +64 -0
- data/ext/couchbase/operations/document_increment.hxx +80 -0
- data/ext/couchbase/operations/document_insert.hxx +74 -0
- data/ext/couchbase/operations/{lookup_in.hxx → document_lookup_in.hxx} +2 -2
- data/ext/couchbase/operations/{mutate_in.hxx → document_mutate_in.hxx} +11 -2
- data/ext/couchbase/operations/{query.hxx → document_query.hxx} +101 -6
- data/ext/couchbase/operations/document_remove.hxx +67 -0
- data/ext/couchbase/operations/document_replace.hxx +76 -0
- data/ext/couchbase/operations/{upsert.hxx → document_touch.hxx} +14 -14
- data/ext/couchbase/operations/{remove.hxx → document_unlock.hxx} +12 -10
- data/ext/couchbase/operations/document_upsert.hxx +74 -0
- data/ext/couchbase/operations/query_index_build_deferred.hxx +85 -0
- data/ext/couchbase/operations/query_index_create.hxx +134 -0
- data/ext/couchbase/operations/query_index_drop.hxx +108 -0
- data/ext/couchbase/operations/query_index_get_all.hxx +106 -0
- data/ext/couchbase/operations/scope_create.hxx +81 -0
- data/ext/couchbase/operations/scope_drop.hxx +79 -0
- data/ext/couchbase/operations/scope_get_all.hxx +72 -0
- data/ext/couchbase/protocol/client_opcode.hxx +35 -0
- data/ext/couchbase/protocol/client_request.hxx +56 -9
- data/ext/couchbase/protocol/client_response.hxx +52 -15
- data/ext/couchbase/protocol/cmd_cluster_map_change_notification.hxx +81 -0
- data/ext/couchbase/protocol/cmd_decrement.hxx +187 -0
- data/ext/couchbase/protocol/cmd_exists.hxx +171 -0
- data/ext/couchbase/protocol/cmd_get.hxx +31 -8
- data/ext/couchbase/protocol/cmd_get_and_lock.hxx +142 -0
- data/ext/couchbase/protocol/cmd_get_and_touch.hxx +142 -0
- data/ext/couchbase/protocol/cmd_get_cluster_config.hxx +16 -3
- data/ext/couchbase/protocol/cmd_get_collections_manifest.hxx +16 -3
- data/ext/couchbase/protocol/cmd_get_error_map.hxx +16 -3
- data/ext/couchbase/protocol/cmd_hello.hxx +24 -8
- data/ext/couchbase/protocol/cmd_increment.hxx +187 -0
- data/ext/couchbase/protocol/cmd_info.hxx +1 -0
- data/ext/couchbase/protocol/cmd_insert.hxx +172 -0
- data/ext/couchbase/protocol/cmd_lookup_in.hxx +28 -13
- data/ext/couchbase/protocol/cmd_mutate_in.hxx +65 -13
- data/ext/couchbase/protocol/cmd_remove.hxx +59 -4
- data/ext/couchbase/protocol/cmd_replace.hxx +172 -0
- data/ext/couchbase/protocol/cmd_sasl_auth.hxx +15 -3
- data/ext/couchbase/protocol/cmd_sasl_list_mechs.hxx +15 -3
- data/ext/couchbase/protocol/cmd_sasl_step.hxx +15 -3
- data/ext/couchbase/protocol/cmd_select_bucket.hxx +14 -2
- data/ext/couchbase/protocol/cmd_touch.hxx +102 -0
- data/ext/couchbase/protocol/cmd_unlock.hxx +95 -0
- data/ext/couchbase/protocol/cmd_upsert.hxx +50 -14
- data/ext/couchbase/protocol/durability_level.hxx +67 -0
- data/ext/couchbase/protocol/frame_info_id.hxx +187 -0
- data/ext/couchbase/protocol/hello_feature.hxx +137 -0
- data/ext/couchbase/protocol/server_opcode.hxx +57 -0
- data/ext/couchbase/protocol/server_request.hxx +122 -0
- data/ext/couchbase/protocol/unsigned_leb128.h +15 -15
- data/ext/couchbase/utils/byteswap.hxx +1 -2
- data/ext/couchbase/utils/url_codec.hxx +225 -0
- data/ext/couchbase/version.hxx +3 -1
- data/ext/extconf.rb +4 -1
- data/ext/test/main.cxx +37 -113
- data/ext/third_party/snappy/.appveyor.yml +36 -0
- data/ext/third_party/snappy/.gitignore +8 -0
- data/ext/third_party/snappy/.travis.yml +98 -0
- data/ext/third_party/snappy/AUTHORS +1 -0
- data/ext/third_party/snappy/CMakeLists.txt +345 -0
- data/ext/third_party/snappy/CONTRIBUTING.md +26 -0
- data/ext/third_party/snappy/COPYING +54 -0
- data/ext/third_party/snappy/NEWS +188 -0
- data/ext/third_party/snappy/README.md +148 -0
- data/ext/third_party/snappy/cmake/SnappyConfig.cmake.in +33 -0
- data/ext/third_party/snappy/cmake/config.h.in +59 -0
- data/ext/third_party/snappy/docs/README.md +72 -0
- data/ext/third_party/snappy/format_description.txt +110 -0
- data/ext/third_party/snappy/framing_format.txt +135 -0
- data/ext/third_party/snappy/snappy-c.cc +90 -0
- data/ext/third_party/snappy/snappy-c.h +138 -0
- data/ext/third_party/snappy/snappy-internal.h +315 -0
- data/ext/third_party/snappy/snappy-sinksource.cc +121 -0
- data/ext/third_party/snappy/snappy-sinksource.h +182 -0
- data/ext/third_party/snappy/snappy-stubs-internal.cc +42 -0
- data/ext/third_party/snappy/snappy-stubs-internal.h +493 -0
- data/ext/third_party/snappy/snappy-stubs-public.h.in +63 -0
- data/ext/third_party/snappy/snappy-test.cc +613 -0
- data/ext/third_party/snappy/snappy-test.h +526 -0
- data/ext/third_party/snappy/snappy.cc +1770 -0
- data/ext/third_party/snappy/snappy.h +209 -0
- data/ext/third_party/snappy/snappy_compress_fuzzer.cc +60 -0
- data/ext/third_party/snappy/snappy_uncompress_fuzzer.cc +58 -0
- data/ext/third_party/snappy/snappy_unittest.cc +1512 -0
- data/ext/third_party/snappy/testdata/alice29.txt +3609 -0
- data/ext/third_party/snappy/testdata/asyoulik.txt +4122 -0
- data/ext/third_party/snappy/testdata/baddata1.snappy +0 -0
- data/ext/third_party/snappy/testdata/baddata2.snappy +0 -0
- data/ext/third_party/snappy/testdata/baddata3.snappy +0 -0
- data/ext/third_party/snappy/testdata/fireworks.jpeg +0 -0
- data/ext/third_party/snappy/testdata/geo.protodata +0 -0
- data/ext/third_party/snappy/testdata/html +1 -0
- data/ext/third_party/snappy/testdata/html_x_4 +1 -0
- data/ext/third_party/snappy/testdata/kppkn.gtb +0 -0
- data/ext/third_party/snappy/testdata/lcet10.txt +7519 -0
- data/ext/third_party/snappy/testdata/paper-100k.pdf +600 -2
- data/ext/third_party/snappy/testdata/plrabn12.txt +10699 -0
- data/ext/third_party/snappy/testdata/urls.10K +10000 -0
- data/lib/couchbase/binary_collection.rb +33 -76
- data/lib/couchbase/binary_collection_options.rb +94 -0
- data/lib/couchbase/bucket.rb +9 -3
- data/lib/couchbase/cluster.rb +161 -23
- data/lib/couchbase/collection.rb +108 -191
- data/lib/couchbase/collection_options.rb +430 -0
- data/lib/couchbase/errors.rb +136 -134
- data/lib/couchbase/json_transcoder.rb +32 -0
- data/lib/couchbase/management/analytics_index_manager.rb +185 -9
- data/lib/couchbase/management/bucket_manager.rb +84 -33
- data/lib/couchbase/management/collection_manager.rb +166 -1
- data/lib/couchbase/management/query_index_manager.rb +261 -0
- data/lib/couchbase/management/search_index_manager.rb +291 -0
- data/lib/couchbase/management/user_manager.rb +12 -10
- data/lib/couchbase/management/view_index_manager.rb +151 -1
- data/lib/couchbase/mutation_state.rb +11 -1
- data/lib/couchbase/scope.rb +4 -4
- data/lib/couchbase/version.rb +1 -1
- metadata +113 -18
- data/.travis.yml +0 -7
- data/ext/couchbase/io/binary_parser.hxx +0 -64
- 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)
|
data/examples/query.rb
CHANGED
@@ -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[
|
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
|
data/examples/subdocument.rb
CHANGED
@@ -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
|
]
|
data/ext/.clang-format
CHANGED
@@ -8,7 +8,7 @@ UseTab: Never
|
|
8
8
|
IndentWidth: 4
|
9
9
|
ColumnLimit: 140
|
10
10
|
AllowShortFunctionsOnASingleLine: None
|
11
|
-
AllowShortBlocksOnASingleLine:
|
11
|
+
AllowShortBlocksOnASingleLine: Empty
|
12
12
|
AllowShortCaseLabelsOnASingleLine: false
|
13
13
|
BreakBeforeBraces: Linux
|
14
14
|
SortIncludes: false
|
data/ext/.idea/vcs.xml
CHANGED
@@ -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>
|
data/ext/CMakeLists.txt
CHANGED
@@ -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
|
-
|
135
|
-
|
136
|
-
|
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
|
+
|
data/ext/couchbase/bucket.hxx
CHANGED
@@ -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::
|
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
|
-
|
67
|
+
void remove_node(size_t index)
|
49
68
|
{
|
50
|
-
|
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
|
-
|
78
|
+
if (closed_) {
|
79
|
+
return;
|
80
|
+
}
|
57
81
|
size_t index = 0;
|
58
|
-
std::tie(
|
59
|
-
|
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
|
-
|
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
|
data/ext/couchbase/cluster.hxx
CHANGED
@@ -20,11 +20,11 @@
|
|
20
20
|
#include <utility>
|
21
21
|
#include <thread>
|
22
22
|
|
23
|
-
#include <io/
|
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/
|
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::
|
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,
|
76
|
-
|
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
|
-
|
84
|
+
template<typename Handler>
|
85
|
+
void close(Handler&& handler)
|
80
86
|
{
|
81
|
-
|
82
|
-
|
83
|
-
bucket
|
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::
|
96
|
-
new_session->bootstrap(
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
149
|
+
return bucket->second->execute(request, std::forward<Handler>(handler));
|
137
150
|
}
|
138
151
|
|
139
|
-
template<class Handler>
|
140
|
-
void
|
152
|
+
template<class Request, class Handler>
|
153
|
+
void execute_http(Request request, Handler&& handler)
|
141
154
|
{
|
142
|
-
auto session = session_manager_
|
143
|
-
|
144
|
-
|
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_
|
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::
|
155
|
-
io::session_manager session_manager_;
|
156
|
-
std::map<std::string, bucket
|
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
|