|
1 | 1 | use std::sync::Arc; |
2 | 2 |
|
3 | 3 | use assert_matches::assert_matches; |
| 4 | +use scylla::client::PoolSize; |
4 | 5 | use scylla::client::session::Session; |
5 | 6 | use scylla::client::session_builder::SessionBuilder; |
6 | | -use scylla::errors::{ExecutionError, RequestAttemptError, SchemaAgreementError}; |
| 7 | +use scylla::errors::{DbError, ExecutionError, RequestAttemptError, SchemaAgreementError}; |
7 | 8 | use scylla::policies::load_balancing::{NodeIdentifier, SingleTargetLoadBalancingPolicy}; |
8 | 9 | use scylla::response::query_result::QueryResult; |
9 | 10 | use scylla::statement::Statement; |
10 | 11 | use scylla_proxy::{ |
11 | 12 | Condition, ProxyError, Reaction, RequestOpcode, RequestReaction, RequestRule, RunningProxy, |
12 | 13 | ShardAwareness, WorkerError, |
13 | 14 | }; |
| 15 | +use tracing::info; |
14 | 16 |
|
15 | 17 | use crate::utils::{ |
16 | 18 | calculate_proxy_host_ids, setup_tracing, test_with_3_node_cluster, unique_keyspace_name, |
@@ -157,3 +159,82 @@ async fn test_schema_await_with_unreachable_node() { |
157 | 159 | Err(err) => panic!("{}", err), |
158 | 160 | } |
159 | 161 | } |
| 162 | + |
| 163 | +// Verifies that schema agreement process works correctly even if the first check fails. |
| 164 | +#[tokio::test] |
| 165 | +#[cfg_attr(scylla_cloud_tests, ignore)] |
| 166 | +async fn test_schema_await_with_transient_failure() { |
| 167 | + setup_tracing(); |
| 168 | + |
| 169 | + let res = test_with_3_node_cluster( |
| 170 | + ShardAwareness::QueryNode, |
| 171 | + |proxy_uris, translation_map, mut running_proxy| async move { |
| 172 | + // DB preparation phase |
| 173 | + let session: Session = SessionBuilder::new() |
| 174 | + .known_node(proxy_uris[0].as_str()) |
| 175 | + .address_translator(Arc::new(translation_map.clone())) |
| 176 | + // Important in order to have a predictable amount of connections after session creation. |
| 177 | + // Shard connections are created asynchronously, so it's hard to predict how many will be opened |
| 178 | + // already when we check schema agreement. |
| 179 | + .pool_size(PoolSize::PerHost(1.try_into().unwrap())) |
| 180 | + .build() |
| 181 | + .await |
| 182 | + .unwrap(); |
| 183 | + |
| 184 | + let node_rules = Some(vec![RequestRule( |
| 185 | + Condition::not(Condition::ConnectionRegisteredAnyEvent) |
| 186 | + .and(Condition::RequestOpcode(RequestOpcode::Query)) |
| 187 | + .and(Condition::BodyContainsCaseSensitive(Box::new( |
| 188 | + *b"system.local", |
| 189 | + ))) |
| 190 | + .and(Condition::TrueForLimitedTimes(1)), |
| 191 | + // Use error that would prevent DefaultRetryPolicy from retrying. |
| 192 | + // I don't think it is used for those queries, but it's additional future-proofing |
| 193 | + // for the test. |
| 194 | + RequestReaction::forge_with_error(DbError::SyntaxError), |
| 195 | + )]); |
| 196 | + |
| 197 | + // First, a sanity check for proxy rules. |
| 198 | + // If for each node first request fails (and subsequent requests succeed), |
| 199 | + // then first schema agreement check should return error, and second should succeed. |
| 200 | + // Note that this is only true because we configured the session with 1-connection-per-node. |
| 201 | + info!("Starting phase 1 - sanity check"); |
| 202 | + { |
| 203 | + running_proxy |
| 204 | + .running_nodes |
| 205 | + .iter_mut() |
| 206 | + .for_each(|node| node.change_request_rules(node_rules.clone())); |
| 207 | + |
| 208 | + // The important check: first call should error out, second one should succeed. |
| 209 | + session.check_schema_agreement().await.unwrap_err(); |
| 210 | + session.check_schema_agreement().await.unwrap(); |
| 211 | + |
| 212 | + running_proxy |
| 213 | + .running_nodes |
| 214 | + .iter_mut() |
| 215 | + .for_each(|node| node.change_request_rules(Some(vec![]))); |
| 216 | + } |
| 217 | + |
| 218 | + // Now let's check that awaiting schema agreement doesn't bail on error. |
| 219 | + // I'll use the same proxy rules as before, so first check will error out. |
| 220 | + info!("Starting phase 2 - main test"); |
| 221 | + { |
| 222 | + running_proxy |
| 223 | + .running_nodes |
| 224 | + .iter_mut() |
| 225 | + .for_each(|node| node.change_request_rules(node_rules.clone())); |
| 226 | + |
| 227 | + session.await_schema_agreement().await.unwrap(); |
| 228 | + } |
| 229 | + |
| 230 | + running_proxy |
| 231 | + }, |
| 232 | + ) |
| 233 | + .await; |
| 234 | + |
| 235 | + match res { |
| 236 | + Ok(()) => (), |
| 237 | + Err(ProxyError::Worker(WorkerError::DriverDisconnected(_))) => (), |
| 238 | + Err(err) => panic!("{}", err), |
| 239 | + } |
| 240 | +} |
0 commit comments