diff --git a/pymongo/read_preferences.py b/pymongo/read_preferences.py index a0a3e9bb2..efe272761 100644 --- a/pymongo/read_preferences.py +++ b/pymongo/read_preferences.py @@ -350,6 +350,11 @@ class MovingAverage(object): self.average = None def add_sample(self, sample): + if sample < 0: + # Likely system time change while waiting for ismaster response + # and not using time.monotonic. Ignore it, the next one will + # probably be valid. + return if self.average is None: self.average = sample else: diff --git a/test/server_selection/rtt/first_value.json b/test/server_selection/rtt/first_value.json new file mode 100644 index 000000000..2e9219560 --- /dev/null +++ b/test/server_selection/rtt/first_value.json @@ -0,0 +1,5 @@ +{ + "avg_rtt_ms": "NULL", + "new_avg_rtt": 10, + "new_rtt_ms": 10 +} diff --git a/test/server_selection/rtt/first_value_zero.json b/test/server_selection/rtt/first_value_zero.json new file mode 100644 index 000000000..1953a742a --- /dev/null +++ b/test/server_selection/rtt/first_value_zero.json @@ -0,0 +1,5 @@ +{ + "avg_rtt_ms": "NULL", + "new_avg_rtt": 0, + "new_rtt_ms": 0 +} diff --git a/test/server_selection/rtt/value_test_1.json b/test/server_selection/rtt/value_test_1.json new file mode 100644 index 000000000..bfa3eb32b --- /dev/null +++ b/test/server_selection/rtt/value_test_1.json @@ -0,0 +1,5 @@ +{ + "avg_rtt_ms": 0, + "new_avg_rtt": 1.0, + "new_rtt_ms": 5 +} diff --git a/test/server_selection/rtt/value_test_2.json b/test/server_selection/rtt/value_test_2.json new file mode 100644 index 000000000..0614cc3f0 --- /dev/null +++ b/test/server_selection/rtt/value_test_2.json @@ -0,0 +1,5 @@ +{ + "avg_rtt_ms": 3.1, + "new_avg_rtt": 9.68, + "new_rtt_ms": 36 +} diff --git a/test/server_selection/rtt/value_test_3.json b/test/server_selection/rtt/value_test_3.json new file mode 100644 index 000000000..c42edc108 --- /dev/null +++ b/test/server_selection/rtt/value_test_3.json @@ -0,0 +1,5 @@ +{ + "avg_rtt_ms": 9.12, + "new_avg_rtt": 9.12, + "new_rtt_ms": 9.12 +} diff --git a/test/server_selection/rtt/value_test_4.json b/test/server_selection/rtt/value_test_4.json new file mode 100644 index 000000000..f65b362ec --- /dev/null +++ b/test/server_selection/rtt/value_test_4.json @@ -0,0 +1,5 @@ +{ + "avg_rtt_ms": 1, + "new_avg_rtt": 200.8, + "new_rtt_ms": 1000 +} diff --git a/test/server_selection/rtt/value_test_5.json b/test/server_selection/rtt/value_test_5.json new file mode 100644 index 000000000..4c86e05a2 --- /dev/null +++ b/test/server_selection/rtt/value_test_5.json @@ -0,0 +1,5 @@ +{ + "avg_rtt_ms": 0, + "new_avg_rtt": 0.05, + "new_rtt_ms": 0.25 +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Nearest.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Nearest.json new file mode 100644 index 000000000..9677494ad --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Nearest.json @@ -0,0 +1,60 @@ +{ + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.json new file mode 100644 index 000000000..9aac3327d --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.json @@ -0,0 +1,34 @@ +{ + "in_latency_window": [], + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "sf" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Primary.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Primary.json new file mode 100644 index 000000000..a4102fe82 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Primary.json @@ -0,0 +1,32 @@ +{ + "in_latency_window": [], + "operation": "read", + "read_preference": { + "mode": "Primary", + "tag_sets": [ + {} + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.json new file mode 100644 index 000000000..53b66c0b8 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.json @@ -0,0 +1,58 @@ +{ + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + {} + ] + }, + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.json new file mode 100644 index 000000000..6a3702d00 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.json @@ -0,0 +1,34 @@ +{ + "in_latency_window": [], + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "sf" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Secondary.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Secondary.json new file mode 100644 index 000000000..325a7550b --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Secondary.json @@ -0,0 +1,60 @@ +{ + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.json new file mode 100644 index 000000000..9012b7611 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.json @@ -0,0 +1,60 @@ +{ + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.json new file mode 100644 index 000000000..bad36648b --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.json @@ -0,0 +1,34 @@ +{ + "in_latency_window": [], + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "sf" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.json new file mode 100644 index 000000000..4d8bd1891 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.json @@ -0,0 +1,34 @@ +{ + "in_latency_window": [], + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "sf" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.json new file mode 100644 index 000000000..db2fb398d --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.json @@ -0,0 +1,34 @@ +{ + "in_latency_window": [], + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "type": "ReplicaSetNoPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Nearest.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Nearest.json new file mode 100644 index 000000000..5975f770c --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Nearest.json @@ -0,0 +1,76 @@ +{ + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.json new file mode 100644 index 000000000..af890cd59 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.json @@ -0,0 +1,42 @@ +{ + "in_latency_window": [], + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "sf" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Primary.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Primary.json new file mode 100644 index 000000000..de001ca0b --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Primary.json @@ -0,0 +1,58 @@ +{ + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "operation": "read", + "read_preference": { + "mode": "Primary", + "tag_sets": [ + {} + ] + }, + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.json new file mode 100644 index 000000000..b413745dd --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.json @@ -0,0 +1,58 @@ +{ + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + {} + ] + }, + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.json new file mode 100644 index 000000000..12b20040a --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.json @@ -0,0 +1,60 @@ +{ + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "sf" + } + ] + }, + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Secondary.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Secondary.json new file mode 100644 index 000000000..faf3b70a6 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Secondary.json @@ -0,0 +1,68 @@ +{ + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.json new file mode 100644 index 000000000..fd549a232 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.json @@ -0,0 +1,68 @@ +{ + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.json new file mode 100644 index 000000000..948a72863 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.json @@ -0,0 +1,60 @@ +{ + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "sf" + } + ] + }, + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.json new file mode 100644 index 000000000..27213d359 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.json @@ -0,0 +1,42 @@ +{ + "in_latency_window": [], + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "sf" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.json new file mode 100644 index 000000000..a57ee31be --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.json @@ -0,0 +1,60 @@ +{ + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "topology_description": { + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "tags": { + "data_center": "nyc" + }, + "type": "RSSecondary" + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "tags": { + "data_center": "nyc" + }, + "type": "RSPrimary" + } + ], + "type": "ReplicaSetWithPrimary" + } +} diff --git a/test/server_selection/server_selection/Sharded/read/SecondaryPreferred.json b/test/server_selection/server_selection/Sharded/read/SecondaryPreferred.json new file mode 100644 index 000000000..e45556f4c --- /dev/null +++ b/test/server_selection/server_selection/Sharded/read/SecondaryPreferred.json @@ -0,0 +1,60 @@ +{ + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "Mongos" + } + ], + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 35, + "tags": { + "data_center": "dc" + }, + "type": "Mongos" + } + ], + "topology_description": { + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 35, + "tags": { + "data_center": "dc" + }, + "type": "Mongos" + } + ], + "type": "Sharded" + } +} diff --git a/test/server_selection/server_selection/Sharded/write/SecondaryPreferred.json b/test/server_selection/server_selection/Sharded/write/SecondaryPreferred.json new file mode 100644 index 000000000..4262ce2ef --- /dev/null +++ b/test/server_selection/server_selection/Sharded/write/SecondaryPreferred.json @@ -0,0 +1,60 @@ +{ + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "Mongos" + } + ], + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 35, + "tags": { + "data_center": "dc" + }, + "type": "Mongos" + } + ], + "topology_description": { + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "nyc" + }, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 35, + "tags": { + "data_center": "dc" + }, + "type": "Mongos" + } + ], + "type": "Sharded" + } +} diff --git a/test/server_selection/server_selection/Single/read/SecondaryPreferred.json b/test/server_selection/server_selection/Single/read/SecondaryPreferred.json new file mode 100644 index 000000000..86c704c14 --- /dev/null +++ b/test/server_selection/server_selection/Single/read/SecondaryPreferred.json @@ -0,0 +1,44 @@ +{ + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "dc" + }, + "type": "Standalone" + } + ], + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "dc" + }, + "type": "Standalone" + } + ], + "topology_description": { + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "dc" + }, + "type": "Standalone" + } + ], + "type": "Single" + } +} diff --git a/test/server_selection/server_selection/Single/write/SecondaryPreferred.json b/test/server_selection/server_selection/Single/write/SecondaryPreferred.json new file mode 100644 index 000000000..be8771cae --- /dev/null +++ b/test/server_selection/server_selection/Single/write/SecondaryPreferred.json @@ -0,0 +1,44 @@ +{ + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "dc" + }, + "type": "Standalone" + } + ], + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "dc" + }, + "type": "Standalone" + } + ], + "topology_description": { + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "tags": { + "data_center": "dc" + }, + "type": "Standalone" + } + ], + "type": "Single" + } +} diff --git a/test/server_selection/server_selection/Unknown/read/SecondaryPreferred.json b/test/server_selection/server_selection/Unknown/read/SecondaryPreferred.json new file mode 100644 index 000000000..ce0d8376b --- /dev/null +++ b/test/server_selection/server_selection/Unknown/read/SecondaryPreferred.json @@ -0,0 +1,17 @@ +{ + "in_latency_window": [], + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [], + "type": "Unknown" + } +} diff --git a/test/server_selection/server_selection/Unknown/write/SecondaryPreferred.json b/test/server_selection/server_selection/Unknown/write/SecondaryPreferred.json new file mode 100644 index 000000000..4d6f79b46 --- /dev/null +++ b/test/server_selection/server_selection/Unknown/write/SecondaryPreferred.json @@ -0,0 +1,17 @@ +{ + "in_latency_window": [], + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [], + "topology_description": { + "servers": [], + "type": "Unknown" + } +} diff --git a/test/test_server_selection.py b/test/test_server_selection.py new file mode 100644 index 000000000..133578c16 --- /dev/null +++ b/test/test_server_selection.py @@ -0,0 +1,237 @@ +# Copyright 2009-2014 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test the topology module.""" + +import json +import os +import sys + +sys.path[0:0] = [""] + +from pymongo import read_preferences +from pymongo.common import clean_node +from pymongo.errors import AutoReconnect +from pymongo.ismaster import IsMaster +from pymongo.server_description import ServerDescription +from pymongo.settings import TopologySettings +from pymongo.server_selectors import writable_server_selector +from pymongo.topology import Topology +from test import unittest + + +# Location of JSON test specifications. +_TEST_PATH = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + os.path.join('server_selection', 'server_selection')) + + +class MockSocketInfo(object): + def close(self): + pass + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + +class MockPool(object): + def __init__(self, *args, **kwargs): + pass + + def reset(self): + pass + + +class MockMonitor(object): + def __init__(self, server_description, topology, pool, topology_settings): + pass + + def open(self): + pass + + def request_check(self): + pass + + def close(self): + pass + + +def get_addresses(server_list): + seeds = [] + hosts = [] + for server in server_list: + seeds.append(clean_node(server['address'])) + hosts.append(server['address']) + return seeds, hosts + + +def make_server_description(server, hosts): + """Make ServerDescription from server info from JSON file.""" + ismaster_response = {} + ismaster_response['tags'] = server['tags'] + ismaster_response['ok'] = True + ismaster_response['hosts'] = hosts + + server_type = server['type'] + + if server_type != "Standalone" and server_type != "Mongos": + ismaster_response['setName'] = True + if server_type == "RSPrimary": + ismaster_response['ismaster'] = True + elif server_type == "RSSecondary": + ismaster_response['secondary'] = True + elif server_type == "Mongos": + ismaster_response['msg'] = 'isdbgrid' + + return ServerDescription(clean_node(server['address']), + IsMaster(ismaster_response), + round_trip_time=server['avg_rtt_ms']) + + +class TestAllScenarios(unittest.TestCase): + pass + + +def create_test(scenario_def): + def run_scenario(self): + + # Initialize topologies. + seeds, hosts = get_addresses( + scenario_def['topology_description']['servers']) + + # "Eligible servers" is defined in the server selection spec as + # the set of servers matching both the ReadPreference's mode + # and tag sets. + top_latency = Topology( + TopologySettings(seeds=seeds, monitor_class=MockMonitor, + pool_class=MockPool)) + # "In latency window" is defined in the server selection + # spec as the subset of suitable_servers that falls within the + # allowable latency window. + top_suitable = Topology( + TopologySettings(seeds=seeds, local_threshold_ms=1000000, + monitor_class=MockMonitor, + pool_class=MockPool)) + + # Update topologies with server descriptions. + for server in scenario_def['topology_description']['servers']: + server_description = make_server_description(server, hosts) + top_suitable.on_change(server_description) + top_latency.on_change(server_description) + + # Create server selector. + if scenario_def["operation"] == "write": + instance = writable_server_selector + else: + # Make first letter lowercase to match read_pref's modes. + mode_string = scenario_def['read_preference']['mode'] + if mode_string: + mode_string = mode_string[:1].lower() + mode_string[1:] + + + mode = read_preferences.ReadPreference[ + read_preferences.read_pref_mode_from_name(mode_string)] + if scenario_def['read_preference']['tag_sets'][0]: + instance = mode.__class__( + tag_sets=scenario_def['read_preference']['tag_sets']) + else: + instance = mode + + # Select servers. + if not scenario_def['suitable_servers']: + self.assertRaises(AutoReconnect, top_suitable.select_server, + instance, + server_wait_time=0) + return + + if not scenario_def['in_latency_window']: + self.assertRaises(AutoReconnect, top_latency.select_server, + instance, + server_wait_time=0) + return + + actual_suitable_s = top_suitable.select_servers(instance, + server_wait_time=0) + actual_latency_s = top_latency.select_servers(instance, + server_wait_time=0) + + expected_suitable_servers = {} + for server in scenario_def['suitable_servers']: + server_description = make_server_description(server, hosts) + expected_suitable_servers[server['address']] = server_description + + actual_suitable_servers = {} + for s in actual_suitable_s: + actual_suitable_servers["%s:%d" % (s.description.address[0], + s.description.address[1])] = s.description + + self.assertEqual(len(actual_suitable_servers), + len(expected_suitable_servers)) + for k, actual in actual_suitable_servers.items(): + expected = expected_suitable_servers[k] + self.assertEqual(expected.address, actual.address) + self.assertEqual(expected.server_type, actual.server_type) + self.assertEqual(expected.round_trip_time, actual.round_trip_time) + self.assertEqual(expected.tags, actual.tags) + self.assertEqual(expected.all_hosts, actual.all_hosts) + + expected_latency_servers = {} + for server in scenario_def['in_latency_window']: + server_description = make_server_description(server, hosts) + expected_latency_servers[server['address']] = server_description + + actual_latency_servers = {} + for s in actual_latency_s: + actual_latency_servers["%s:%d" % + (s.description.address[0], + s.description.address[1])] = s.description + + self.assertEqual(len(actual_latency_servers), + len(expected_latency_servers)) + for k, actual in actual_latency_servers.items(): + expected = expected_latency_servers[k] + self.assertEqual(expected.address, actual.address) + self.assertEqual(expected.server_type, actual.server_type) + self.assertEqual(expected.round_trip_time, actual.round_trip_time) + self.assertEqual(expected.tags, actual.tags) + self.assertEqual(expected.all_hosts, actual.all_hosts) + + return run_scenario + + +def create_tests(): + for dirpath, _, filenames in os.walk(_TEST_PATH): + dirname = os.path.split(dirpath) + dirname = os.path.split(dirname[-2])[-1] + '_' + dirname[-1] + + for filename in filenames: + with open(os.path.join(dirpath, filename)) as scenario_stream: + scenario_def = json.load(scenario_stream) + + # Construct test from scenario. + new_test = create_test(scenario_def) + test_name = 'test_%s_%s' % ( + dirname, os.path.splitext(filename)[0]) + + new_test.__name__ = test_name + setattr(TestAllScenarios, new_test.__name__, new_test) + + +create_tests() + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_server_selection_rtt.py b/test/test_server_selection_rtt.py new file mode 100644 index 000000000..e5bd4c050 --- /dev/null +++ b/test/test_server_selection_rtt.py @@ -0,0 +1,73 @@ +# Copyright 2009-2014 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test the topology module.""" + +import json +import os +import sys + +sys.path[0:0] = [""] + +from test import unittest +from pymongo.read_preferences import MovingAverage + +# Location of JSON test specifications. +_TEST_PATH = os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'server_selection/rtt') + + +class TestAllScenarios(unittest.TestCase): + pass + + +def create_test(scenario_def): + def run_scenario(self): + moving_average = MovingAverage() + + moving_average.add_sample(scenario_def['avg_rtt_ms']) + if str(scenario_def['avg_rtt_ms']) == "NULL": + moving_average.reset() + + moving_average.add_sample(scenario_def['new_rtt_ms']) + if str(scenario_def['new_rtt_ms']) == "NULL": + moving_average.reset() + + self.assertAlmostEqual(moving_average.get(), + scenario_def['new_avg_rtt']) + + return run_scenario + + +def create_tests(): + for dirpath, _, filenames in os.walk(_TEST_PATH): + dirname = os.path.split(dirpath)[-1] + + for filename in filenames: + with open(os.path.join(dirpath, filename)) as scenario_stream: + scenario_def = json.load(scenario_stream) + + # Construct test from scenario. + new_test = create_test(scenario_def) + test_name = 'test_%s_%s' % ( + dirname, os.path.splitext(filename)[0]) + + new_test.__name__ = test_name + setattr(TestAllScenarios, new_test.__name__, new_test) + + +create_tests() + +if __name__ == "__main__": + unittest.main()