Skip to content

Commit 7c4117b

Browse files
neilshwekyp
andauthored
RUBY-2909 Snapshot Query Examples for the Manual (#2419)
* RUBY-2909 Snapshot Query Examples for the Manual * RUBY-2909 add sharded, min mongodb version * RUBY-2909 add w=majority * RUBY-2909 don't leak clients * RUBY-2909 require stress * RUBY-2909 remove require stress * RUBY-2909 readd require stress * RUBY-2909 move file to stress * RUBY-2909 make test idiomatic to ruby * RUBY-2909 added logging (REMOVE ME) * RUBY-2909 add 5.0 as stress test version * Revert "RUBY-2909 add 5.0 as stress test version" This reverts commit 62cf07e. * RUBY-2909 move test file back and require no auth * RUBY-2909 require no tls * use existing test suite clients * RUBY-2909 run distinct on each mongos * RUBY-2909 use attr_accessor * RUBY-2909 add comma * RUBY-2909 do a distinct on replica sets * wait for snapshot reads to work Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent 2560451 commit 7c4117b

File tree

5 files changed

+171
-5
lines changed

5 files changed

+171
-5
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# frozen_string_literal: true
2+
# encoding: utf-8
3+
4+
require 'spec_helper'
5+
6+
describe 'Snapshot Query Examples' do
7+
require_topology :replica_set, :sharded
8+
require_no_auth
9+
require_no_tls
10+
11+
min_server_fcv '5.0'
12+
13+
let(:uri_string) do
14+
"mongodb://#{SpecConfig.instance.addresses.join(',')}/?w=majority"
15+
end
16+
17+
context "Snapshot Query Example 1" do
18+
before do
19+
client = authorized_client.use('pets')
20+
client['cats'].delete_many
21+
client['dogs'].delete_many
22+
23+
client['cats'].insert_one(
24+
name: "Whiskers",
25+
color: "white",
26+
age: 10,
27+
adoptable: true
28+
)
29+
30+
client['dogs'].insert_one(
31+
name: "Pebbles",
32+
color: "Brown",
33+
age: 10,
34+
adoptable: true
35+
)
36+
if ClusterConfig.instance.topology == :sharded
37+
run_mongos_distincts "pets", "cats"
38+
else
39+
wait_for_snapshot(db: 'pets', collection: 'cats')
40+
wait_for_snapshot(db: 'pets', collection: 'dogs')
41+
end
42+
end
43+
44+
it "returns a snapshot of the data" do
45+
46+
adoptable_pets_count = 0
47+
48+
# Start Snapshot Query Example 1
49+
50+
client = Mongo::Client.new(uri_string, database: "pets")
51+
52+
client.start_session(snapshot: true) do |session|
53+
adoptable_pets_count = client['cats'].aggregate([
54+
{ "$match": { "adoptable": true } },
55+
{ "$count": "adoptable_cats_count" }
56+
], session: session).first["adoptable_cats_count"]
57+
58+
adoptable_pets_count += client['dogs'].aggregate([
59+
{ "$match": { "adoptable": true } },
60+
{ "$count": "adoptable_dogs_count" }
61+
], session: session).first["adoptable_dogs_count"]
62+
63+
puts adoptable_pets_count
64+
end
65+
66+
# End Snapshot Query Example 1
67+
68+
expect(adoptable_pets_count).to eq 2
69+
client.close
70+
end
71+
end
72+
73+
context "Snapshot Query Example 2" do
74+
before do
75+
client = authorized_client.use('retail')
76+
client['sales'].delete_many
77+
78+
client['sales'].insert_one(
79+
shoeType: "boot",
80+
price: 30,
81+
saleDate: Time.now
82+
)
83+
84+
if ClusterConfig.instance.topology == :sharded
85+
run_mongos_distincts "retail", "sales"
86+
else
87+
wait_for_snapshot(db: 'retail', collection: 'sales')
88+
end
89+
end
90+
91+
it "returns a snapshot of the data" do
92+
93+
total = 0
94+
95+
# Start Snapshot Query Example 2
96+
97+
client = Mongo::Client.new(uri_string, database: "retail")
98+
99+
client.start_session(snapshot: true) do |session|
100+
total = client['sales'].aggregate([
101+
{
102+
"$match": {
103+
"$expr": {
104+
"$gt": [
105+
"$saleDate",
106+
{
107+
"$dateSubtract": {
108+
startDate: "$$NOW",
109+
unit: "day",
110+
amount: 1
111+
}
112+
}
113+
]
114+
}
115+
}
116+
},
117+
{ "$count": "total_daily_sales" }
118+
], session: session).first["total_daily_sales"]
119+
end
120+
121+
# End Snapshot Query Example 2
122+
123+
expect(total).to eq 1
124+
client.close
125+
end
126+
end
127+
end

spec/lite_spec_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ module Mrss
8181
require 'support/common_shortcuts'
8282
require 'support/client_registry'
8383
require 'support/client_registry_macros'
84+
require 'support/mongos_macros'
8485
require 'support/crypt'
8586
require 'support/json_ext_formatter'
8687
require 'support/sdam_formatter_integration'
@@ -102,6 +103,7 @@ class ExampleTimeout < StandardError; end
102103
config.include(CommonShortcuts::InstanceMethods)
103104
config.extend(Mrss::LiteConstraints)
104105
config.include(ClientRegistryMacros)
106+
config.include(MongosMacros)
105107

106108
if SpecConfig.instance.ci?
107109
SdamFormatterIntegration.subscribe

spec/runners/transactions/test.rb

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module Transactions
2222
#
2323
# @since 2.6.0
2424
class TransactionsTest < CRUD::CRUDTestBase
25+
include MongosMacros
2526

2627
attr_reader :expected_results
2728
attr_reader :skip_reason
@@ -262,11 +263,8 @@ def setup_test
262263

263264
coll.insert_many(@data) unless @data.empty?
264265

265-
$distinct_ran ||= {}
266-
$distinct_ran[@spec.database_name] ||= if description =~ /distinct/ || @operations.any? { |op| op.name == 'distinct' }
267-
::Utils.mongos_each_direct_client do |direct_client|
268-
direct_client.use(@spec.database_name)['test'].distinct('foo').to_a
269-
end
266+
if description =~ /distinct/ || @operations.any? { |op| op.name == 'distinct' }
267+
run_mongos_distincts(@spec.database_name, 'test')
270268
end
271269

272270
admin_support_client.command(@fail_point_command) if @fail_point_command

spec/support/common_shortcuts.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,5 +370,27 @@ def mock_dns(config)
370370
thread.join
371371
end
372372
end
373+
374+
# Wait for snapshot reads to become available to prevent this error:
375+
# [246:SnapshotUnavailable]: Unable to read from a snapshot due to pending collection catalog changes; please retry the operation. Snapshot timestamp is Timestamp(1646666892, 4). Collection minimum is Timestamp(1646666892, 5) (on localhost:27017, modern retry, attempt 1)
376+
def wait_for_snapshot(db: nil, collection: nil, client: nil)
377+
client ||= authorized_client
378+
client = client.use(db) if db
379+
collection ||= 'any'
380+
start_time = Mongo::Utils.monotonic_time
381+
begin
382+
client.start_session(snapshot: true) do |session|
383+
client[collection].aggregate([{'$match': {any: true}}], session: session).to_a
384+
end
385+
rescue Mongo::Error::OperationFailure => e
386+
# Retry them as the server demands...
387+
if e.code == 246 # SnapshotUnavailable
388+
if Mongo::Utils.monotonic_time < start_time + 10
389+
retry
390+
end
391+
end
392+
raise
393+
end
394+
end
373395
end
374396
end

spec/support/mongos_macros.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# frozen_string_literal: true
2+
3+
module MongosMacros
4+
5+
class << self
6+
attr_accessor :distinct_ran
7+
end
8+
self.distinct_ran = {}
9+
10+
# Work around for SERVER-39704 when seeing a Mongo::Error::OperationFailure
11+
# SnapshotUnavailable error -- run the distinct command on each mongos.
12+
def run_mongos_distincts(db_name, collection='test')
13+
MongosMacros.distinct_ran[db_name] ||= ::Utils.mongos_each_direct_client do |direct_client|
14+
direct_client.use(db_name)[collection].distinct('foo').to_a
15+
end
16+
end
17+
end

0 commit comments

Comments
 (0)