valkey-glide-sync 2.2.5__tar.gz → 2.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/PKG-INFO +1 -1
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster.rs +15 -4
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_async/connections_container.rs +6 -7
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_async/mod.rs +242 -123
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_slotmap.rs +379 -32
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_topology.rs +520 -34
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/_glide_ffi.py +2 -4
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/glide_json.py +1 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/pyproject.toml +1 -1
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/PKG-INFO +1 -1
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/MANIFEST.in +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/README.md +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/.cargo/config.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/.gitignore +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/.ort.yml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/DEVELOPER.md +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/README.md +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/cbindgen.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/README.md +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/client.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/cluster_scan_container.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/command_request.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/connection_request.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/errors.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/request_type.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/scripts_container.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-logger-core/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-logger-core/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-redis/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-redis/src/cluster_routing.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-redis/src/cluster_topology.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-redis/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-telemetry/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-telemetry/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/src/runtime.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/src/sync.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/src/task.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/rust-toolchain.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/.cargo/config.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/.ort.yml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/README.md +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/THIRD_PARTY_LICENSES_RUST +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/build.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/LICENSE +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/README.md +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/appveyor.yml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/get_command_info.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/CHANGELOG.md +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/LICENSE +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/async-await.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/async-multiplexed.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/async-pub-sub.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/async-scan.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/basic.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/release.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/connection.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/connection_manager.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/mod.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/multiplexed_connection.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/runtime.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/tokio.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/client.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_async/LICENSE +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_async/connections_logic.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_async/pipeline_routing.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_client.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_pipeline.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_routing.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cmd.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/commands/cluster_scan.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/commands/macros.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/commands/mod.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/connection.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/macros.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/parser.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/pipeline.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/push_manager.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/retry_strategies.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/sentinel.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/tls.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/types.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/rustfmt.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/src/main.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/mod.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/reconnecting_connection.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/standalone_client.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/types.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/value_conversion.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/cluster_scan_container.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/errors.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/iam/mod.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/protobuf/command_request.proto +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/protobuf/connection_request.proto +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/protobuf/response.proto +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/request_type.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/rotating_buffer.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/scripts_container.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/socket_listener.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/src/metrics_exporter_file.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/src/open_telemetry.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/src/span_exporter_file.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/__init__.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/__init__.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/batch.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/batch_options.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/bitmap.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/command_args.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/core_options.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/ft_options/ft_aggregate_options.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/ft_options/ft_constants.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/ft_options/ft_create_options.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/ft_options/ft_profile_options.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/ft_options/ft_search_options.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/json_batch.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/json_options.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/sorted_set.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/stream.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/config.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/constants.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/exceptions.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/protobuf_codec.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/py.typed +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/routes.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/__init__.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/config.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/glide_client.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/logger.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/py.typed +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/__init__.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/cluster_commands.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/cluster_scan_cursor.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/core.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/ft.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/script.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/standalone_commands.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/logger_core/Cargo.toml +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/logger_core/src/lib.rs +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/setup.cfg +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/setup.py +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/SOURCES.txt +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/dependency_links.txt +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/requires.txt +0 -0
- {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/top_level.txt +0 -0
{valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster.rs
RENAMED
|
@@ -42,7 +42,7 @@ use crate::cluster_routing::{
|
|
|
42
42
|
MultipleNodeRoutingInfo, ResponsePolicy, Routable, SingleNodeRoutingInfo,
|
|
43
43
|
};
|
|
44
44
|
use crate::cluster_slotmap::SlotMap;
|
|
45
|
-
use crate::cluster_topology::parse_and_count_slots;
|
|
45
|
+
use crate::cluster_topology::{parse_and_count_slots, ParsedSlotsResult};
|
|
46
46
|
use crate::cmd::{cmd, Cmd};
|
|
47
47
|
use crate::connection::{
|
|
48
48
|
connect, Connection, ConnectionAddr, ConnectionInfo, ConnectionLike, RedisConnectionInfo,
|
|
@@ -226,6 +226,7 @@ where
|
|
|
226
226
|
connections: RefCell::new(HashMap::new()),
|
|
227
227
|
slots: RefCell::new(SlotMap::new(
|
|
228
228
|
vec![],
|
|
229
|
+
HashMap::new(),
|
|
229
230
|
cluster_params.read_from_replicas.clone(),
|
|
230
231
|
)),
|
|
231
232
|
auto_reconnect: RefCell::new(true),
|
|
@@ -382,9 +383,19 @@ where
|
|
|
382
383
|
ErrorKind::ClientError,
|
|
383
384
|
"can't parse node address",
|
|
384
385
|
)))?;
|
|
385
|
-
match parse_and_count_slots(&value, self.cluster_params.tls, addr).map(
|
|
386
|
-
|
|
387
|
-
|
|
386
|
+
match parse_and_count_slots(&value, self.cluster_params.tls, addr).map(
|
|
387
|
+
|ParsedSlotsResult {
|
|
388
|
+
slots,
|
|
389
|
+
address_to_ip_map,
|
|
390
|
+
..
|
|
391
|
+
}| {
|
|
392
|
+
SlotMap::new(
|
|
393
|
+
slots,
|
|
394
|
+
address_to_ip_map,
|
|
395
|
+
self.cluster_params.read_from_replicas.clone(),
|
|
396
|
+
)
|
|
397
|
+
},
|
|
398
|
+
) {
|
|
388
399
|
Ok(new_slots) => {
|
|
389
400
|
result = Ok(new_slots);
|
|
390
401
|
break;
|
|
@@ -344,10 +344,11 @@ where
|
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
/// Returns an iterator over the nodes in the `slot_map`, yielding
|
|
347
|
+
/// Returns an iterator over the nodes in the `slot_map`, yielding tuples of
|
|
348
|
+
/// (node address, (optional IP address, shard addresses)).
|
|
348
349
|
pub(crate) fn slot_map_nodes(
|
|
349
350
|
&self,
|
|
350
|
-
) -> impl Iterator<Item = (Arc<String>, Arc<ShardAddrs>)> + '_ {
|
|
351
|
+
) -> impl Iterator<Item = (Arc<String>, (Option<IpAddr>, Arc<ShardAddrs>))> + '_ {
|
|
351
352
|
self.slot_map
|
|
352
353
|
.nodes_map()
|
|
353
354
|
.iter()
|
|
@@ -628,11 +629,7 @@ where
|
|
|
628
629
|
let (address, conn) = (item.key(), item.value());
|
|
629
630
|
(
|
|
630
631
|
address.clone(),
|
|
631
|
-
conn.
|
|
632
|
-
.as_ref()
|
|
633
|
-
.unwrap_or(&conn.user_connection)
|
|
634
|
-
.conn
|
|
635
|
-
.clone(),
|
|
632
|
+
conn.get_connection(&ConnectionType::PreferManagement),
|
|
636
633
|
)
|
|
637
634
|
})
|
|
638
635
|
}
|
|
@@ -798,6 +795,7 @@ mod tests {
|
|
|
798
795
|
],
|
|
799
796
|
),
|
|
800
797
|
],
|
|
798
|
+
HashMap::new(),
|
|
801
799
|
ReadFromReplicaStrategy::AlwaysFromPrimary, // this argument shouldn't matter, since we overload the RFR strategy.
|
|
802
800
|
);
|
|
803
801
|
let connection_map = DashMap::new();
|
|
@@ -860,6 +858,7 @@ mod tests {
|
|
|
860
858
|
vec!["replica3-1".to_owned(), "replica3-2".to_owned()],
|
|
861
859
|
),
|
|
862
860
|
],
|
|
861
|
+
HashMap::new(),
|
|
863
862
|
ReadFromReplicaStrategy::AlwaysFromPrimary, // this argument shouldn't matter, since we overload the RFR strategy.
|
|
864
863
|
);
|
|
865
864
|
let connection_map = DashMap::new();
|
|
@@ -33,9 +33,11 @@ pub mod testing {
|
|
|
33
33
|
use crate::{
|
|
34
34
|
client::GlideConnectionOptions,
|
|
35
35
|
cluster_routing::{Routable, RoutingInfo, ShardUpdateResult},
|
|
36
|
+
cluster_slotmap::SlotMap,
|
|
36
37
|
cluster_topology::{
|
|
37
|
-
calculate_topology, get_slot, SlotRefreshState,
|
|
38
|
-
|
|
38
|
+
calculate_topology, get_slot, SlotRefreshState, TopologyHash,
|
|
39
|
+
DEFAULT_NUMBER_OF_REFRESH_SLOTS_RETRIES, DEFAULT_REFRESH_SLOTS_RETRY_BASE_DURATION_MILLIS,
|
|
40
|
+
DEFAULT_REFRESH_SLOTS_RETRY_BASE_FACTOR,
|
|
39
41
|
},
|
|
40
42
|
cmd,
|
|
41
43
|
commands::cluster_scan::{cluster_scan, ClusterScanArgs, ScanStateRC},
|
|
@@ -49,7 +51,7 @@ use pipeline_routing::{
|
|
|
49
51
|
route_for_pipeline, PipelineResponses, ResponsePoliciesMap,
|
|
50
52
|
};
|
|
51
53
|
|
|
52
|
-
use logger_core::
|
|
54
|
+
use logger_core::log_error;
|
|
53
55
|
use rand::seq::IteratorRandom;
|
|
54
56
|
|
|
55
57
|
use std::{
|
|
@@ -579,6 +581,15 @@ pub(crate) enum RefreshPolicy {
|
|
|
579
581
|
NotThrottable,
|
|
580
582
|
}
|
|
581
583
|
|
|
584
|
+
/// Indicates what triggered a slot refresh operation.
|
|
585
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
586
|
+
pub(crate) enum SlotRefreshTrigger {
|
|
587
|
+
/// Initial cluster connection setup - reuse existing connections to avoid double DNS lookup
|
|
588
|
+
InitialConnection,
|
|
589
|
+
/// Runtime refresh - periodic checks, topology changes, error recovery
|
|
590
|
+
RuntimeRefresh,
|
|
591
|
+
}
|
|
592
|
+
|
|
582
593
|
impl<C> From<cluster_routing::RoutingInfo> for InternalRoutingInfo<C> {
|
|
583
594
|
fn from(value: cluster_routing::RoutingInfo) -> Self {
|
|
584
595
|
match value {
|
|
@@ -1173,9 +1184,11 @@ where
|
|
|
1173
1184
|
periodic_checks_handler: None,
|
|
1174
1185
|
connections_validation_handler: None,
|
|
1175
1186
|
};
|
|
1187
|
+
// Initial slots and subscriptions refresh
|
|
1176
1188
|
Self::refresh_slots_and_subscriptions_with_retries(
|
|
1177
1189
|
connection.inner.clone(),
|
|
1178
1190
|
&RefreshPolicy::NotThrottable,
|
|
1191
|
+
SlotRefreshTrigger::InitialConnection,
|
|
1179
1192
|
)
|
|
1180
1193
|
.await?;
|
|
1181
1194
|
|
|
@@ -1340,6 +1353,7 @@ where
|
|
|
1340
1353
|
if let Err(err) = Self::refresh_slots_and_subscriptions_with_retries(
|
|
1341
1354
|
inner.clone(),
|
|
1342
1355
|
&RefreshPolicy::NotThrottable,
|
|
1356
|
+
SlotRefreshTrigger::InitialConnection,
|
|
1343
1357
|
)
|
|
1344
1358
|
.await
|
|
1345
1359
|
{
|
|
@@ -1607,7 +1621,12 @@ where
|
|
|
1607
1621
|
|
|
1608
1622
|
// Spawn the background task and return its handle
|
|
1609
1623
|
tokio::spawn(async move {
|
|
1610
|
-
Self::refresh_slots_and_subscriptions_with_retries(
|
|
1624
|
+
Self::refresh_slots_and_subscriptions_with_retries(
|
|
1625
|
+
inner_clone,
|
|
1626
|
+
&policy_clone,
|
|
1627
|
+
SlotRefreshTrigger::RuntimeRefresh,
|
|
1628
|
+
)
|
|
1629
|
+
.await
|
|
1611
1630
|
})
|
|
1612
1631
|
}
|
|
1613
1632
|
|
|
@@ -1965,6 +1984,7 @@ where
|
|
|
1965
1984
|
async fn refresh_slots_and_subscriptions_with_retries(
|
|
1966
1985
|
inner: Arc<InnerCore<C>>,
|
|
1967
1986
|
policy: &RefreshPolicy,
|
|
1987
|
+
trigger: SlotRefreshTrigger,
|
|
1968
1988
|
) -> RedisResult<()> {
|
|
1969
1989
|
let SlotRefreshState {
|
|
1970
1990
|
in_progress,
|
|
@@ -2013,7 +2033,7 @@ where
|
|
|
2013
2033
|
let retries_counter = AtomicUsize::new(0);
|
|
2014
2034
|
res = Retry::spawn(retry_strategy, || async {
|
|
2015
2035
|
let curr_retry = retries_counter.fetch_add(1, atomic::Ordering::Relaxed);
|
|
2016
|
-
Self::refresh_slots(inner.clone(), curr_retry)
|
|
2036
|
+
Self::refresh_slots(inner.clone(), curr_retry, trigger)
|
|
2017
2037
|
.await
|
|
2018
2038
|
.map_err(|err| {
|
|
2019
2039
|
if err.kind() == ErrorKind::AllConnectionsUnavailable {
|
|
@@ -2041,7 +2061,12 @@ where
|
|
|
2041
2061
|
) -> RedisResult<bool> {
|
|
2042
2062
|
let topology_changed = Self::check_for_topology_diff(inner.clone()).await;
|
|
2043
2063
|
if topology_changed {
|
|
2044
|
-
Self::refresh_slots_and_subscriptions_with_retries(
|
|
2064
|
+
Self::refresh_slots_and_subscriptions_with_retries(
|
|
2065
|
+
inner.clone(),
|
|
2066
|
+
policy,
|
|
2067
|
+
SlotRefreshTrigger::RuntimeRefresh,
|
|
2068
|
+
)
|
|
2069
|
+
.await?;
|
|
2045
2070
|
}
|
|
2046
2071
|
Ok(topology_changed)
|
|
2047
2072
|
}
|
|
@@ -2186,14 +2211,18 @@ where
|
|
|
2186
2211
|
let num_of_nodes = inner.conn_lock.read().expect(MUTEX_READ_ERR).len();
|
|
2187
2212
|
let num_of_nodes_to_query =
|
|
2188
2213
|
std::cmp::max(num_of_nodes.checked_ilog2().unwrap_or(0) as usize, 1);
|
|
2189
|
-
let
|
|
2214
|
+
let TopologyQueryResult {
|
|
2215
|
+
topology_result,
|
|
2216
|
+
failed_connections,
|
|
2217
|
+
} = calculate_topology_from_random_nodes(
|
|
2190
2218
|
&inner,
|
|
2191
2219
|
num_of_nodes_to_query,
|
|
2192
2220
|
DEFAULT_NUMBER_OF_REFRESH_SLOTS_RETRIES,
|
|
2221
|
+
SlotRefreshTrigger::RuntimeRefresh,
|
|
2193
2222
|
)
|
|
2194
2223
|
.await;
|
|
2195
2224
|
|
|
2196
|
-
if let Ok((_, found_topology_hash)) =
|
|
2225
|
+
if let Ok((_, found_topology_hash)) = topology_result {
|
|
2197
2226
|
if inner
|
|
2198
2227
|
.conn_lock
|
|
2199
2228
|
.read()
|
|
@@ -2205,38 +2234,52 @@ where
|
|
|
2205
2234
|
}
|
|
2206
2235
|
}
|
|
2207
2236
|
|
|
2208
|
-
if
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2237
|
+
if let Some(failed) = failed_connections {
|
|
2238
|
+
if !failed.is_empty() {
|
|
2239
|
+
trace!("check_for_topology_diff: calling trigger_refresh_connection_tasks");
|
|
2240
|
+
Self::trigger_refresh_connection_tasks(
|
|
2241
|
+
inner,
|
|
2242
|
+
failed,
|
|
2243
|
+
RefreshConnectionType::OnlyManagementConnection,
|
|
2244
|
+
true,
|
|
2245
|
+
)
|
|
2246
|
+
.await;
|
|
2247
|
+
}
|
|
2217
2248
|
}
|
|
2218
2249
|
|
|
2219
2250
|
false
|
|
2220
2251
|
}
|
|
2221
2252
|
|
|
2222
|
-
async fn refresh_slots(
|
|
2253
|
+
async fn refresh_slots(
|
|
2254
|
+
inner: Arc<InnerCore<C>>,
|
|
2255
|
+
curr_retry: usize,
|
|
2256
|
+
trigger: SlotRefreshTrigger,
|
|
2257
|
+
) -> RedisResult<()> {
|
|
2223
2258
|
// Update the slot refresh last run timestamp
|
|
2224
2259
|
let now = SystemTime::now();
|
|
2225
2260
|
let mut last_run_wlock = inner.slot_refresh_state.last_run.write().await;
|
|
2226
2261
|
*last_run_wlock = Some(now);
|
|
2227
2262
|
drop(last_run_wlock);
|
|
2228
|
-
Self::refresh_slots_inner(inner, curr_retry).await
|
|
2263
|
+
Self::refresh_slots_inner(inner, curr_retry, trigger).await
|
|
2229
2264
|
}
|
|
2230
2265
|
|
|
2231
2266
|
// Query a node to discover slot-> master mappings
|
|
2232
|
-
async fn refresh_slots_inner(
|
|
2267
|
+
async fn refresh_slots_inner(
|
|
2268
|
+
inner: Arc<InnerCore<C>>,
|
|
2269
|
+
curr_retry: usize,
|
|
2270
|
+
trigger: SlotRefreshTrigger,
|
|
2271
|
+
) -> RedisResult<()> {
|
|
2233
2272
|
let num_of_nodes = inner.conn_lock.read().expect(MUTEX_READ_ERR).len();
|
|
2234
2273
|
const MAX_REQUESTED_NODES: usize = 10;
|
|
2235
2274
|
let num_of_nodes_to_query = std::cmp::min(num_of_nodes, MAX_REQUESTED_NODES);
|
|
2236
|
-
let (new_slots, topology_hash) =
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2275
|
+
let (new_slots, topology_hash) = calculate_topology_from_random_nodes(
|
|
2276
|
+
&inner,
|
|
2277
|
+
num_of_nodes_to_query,
|
|
2278
|
+
curr_retry,
|
|
2279
|
+
trigger,
|
|
2280
|
+
)
|
|
2281
|
+
.await
|
|
2282
|
+
.topology_result?;
|
|
2240
2283
|
// Create a new connection vector of the found nodes
|
|
2241
2284
|
let nodes = new_slots.all_node_addresses();
|
|
2242
2285
|
let nodes_len = nodes.len();
|
|
@@ -2372,7 +2415,7 @@ where
|
|
|
2372
2415
|
|
|
2373
2416
|
let mut wlock_conn_container = inner.conn_lock.write().expect(MUTEX_READ_ERR);
|
|
2374
2417
|
let mut nodes_iter = wlock_conn_container.slot_map_nodes();
|
|
2375
|
-
for (node_addr, shard_addrs_arc) in &mut nodes_iter {
|
|
2418
|
+
for (node_addr, (ip_addr, shard_addrs_arc)) in &mut nodes_iter {
|
|
2376
2419
|
if node_addr == new_primary {
|
|
2377
2420
|
let is_existing_primary = shard_addrs_arc.primary().eq(&new_primary);
|
|
2378
2421
|
if is_existing_primary {
|
|
@@ -2387,9 +2430,11 @@ where
|
|
|
2387
2430
|
// Remove the replica from its existing shard and treat it as a new node in a new shard.
|
|
2388
2431
|
shard_addrs_arc.remove_replica(new_primary.clone())?;
|
|
2389
2432
|
drop(nodes_iter);
|
|
2390
|
-
return wlock_conn_container
|
|
2391
|
-
|
|
2392
|
-
|
|
2433
|
+
return wlock_conn_container.slot_map.add_new_primary(
|
|
2434
|
+
slot,
|
|
2435
|
+
new_primary,
|
|
2436
|
+
ip_addr,
|
|
2437
|
+
);
|
|
2393
2438
|
}
|
|
2394
2439
|
}
|
|
2395
2440
|
}
|
|
@@ -2398,7 +2443,7 @@ where
|
|
|
2398
2443
|
// Scenario 5: New Node - The new primary is not present in the current slots map, add it as a primary of a new shard.
|
|
2399
2444
|
wlock_conn_container
|
|
2400
2445
|
.slot_map
|
|
2401
|
-
.add_new_primary(slot, new_primary)
|
|
2446
|
+
.add_new_primary(slot, new_primary, None)
|
|
2402
2447
|
}
|
|
2403
2448
|
|
|
2404
2449
|
async fn execute_on_multiple_nodes<'a>(
|
|
@@ -3396,88 +3441,155 @@ where
|
|
|
3396
3441
|
}
|
|
3397
3442
|
}
|
|
3398
3443
|
|
|
3399
|
-
|
|
3444
|
+
/// Result of attempting to get connections from initial seed nodes.
|
|
3445
|
+
struct InitialNodeConnectionsResult<C> {
|
|
3446
|
+
/// Successfully found connections with their addresses
|
|
3447
|
+
#[allow(clippy::type_complexity)]
|
|
3448
|
+
connections: Vec<(String, Shared<Pin<Box<dyn Future<Output = C> + Send>>>)>,
|
|
3449
|
+
/// Addresses that need connection refresh (exists in slot map but not in connection map)
|
|
3450
|
+
addresses_needing_refresh: HashSet<String>,
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
/// Returns connections found for randomly selected initial nodes, along with addresses
|
|
3454
|
+
/// that need refresh and a hint if topology change was detected.
|
|
3400
3455
|
async fn get_random_connections_from_initial_nodes<C>(
|
|
3401
3456
|
inner: &Core<C>,
|
|
3402
3457
|
num_of_nodes_to_query: usize,
|
|
3403
|
-
) -> RedisResult<
|
|
3458
|
+
) -> RedisResult<InitialNodeConnectionsResult<C>>
|
|
3404
3459
|
where
|
|
3405
3460
|
C: ConnectionLike + Connect + Clone + Send + Sync + 'static,
|
|
3406
3461
|
{
|
|
3407
|
-
|
|
3408
|
-
// The resolved addresses are tuples of (host, Option<IpAddr>).
|
|
3409
|
-
// Representing the host and its resolved IP address (if available).
|
|
3410
|
-
let resolved_addresses =
|
|
3411
|
-
ClusterConnInner::<C>::try_to_expand_initial_nodes(&inner.initial_nodes).await;
|
|
3412
|
-
|
|
3413
|
-
// Filter the resolved addresses to keep only those with valid IP addresses.
|
|
3414
|
-
let valid_addresses: Vec<String> = resolved_addresses
|
|
3415
|
-
.into_iter()
|
|
3416
|
-
.filter_map(|(host, socket_addr)| match socket_addr {
|
|
3417
|
-
Some(addr) => Some(addr.to_string()),
|
|
3418
|
-
None => {
|
|
3419
|
-
log_warn("No valid IP address found for host: {}", host);
|
|
3420
|
-
None
|
|
3421
|
-
}
|
|
3422
|
-
})
|
|
3423
|
-
.collect();
|
|
3424
|
-
|
|
3425
|
-
if valid_addresses.is_empty() {
|
|
3462
|
+
if inner.initial_nodes.is_empty() {
|
|
3426
3463
|
return Err(RedisError::from((
|
|
3427
|
-
ErrorKind::
|
|
3428
|
-
"
|
|
3464
|
+
ErrorKind::InvalidClientConfig,
|
|
3465
|
+
"Cannot refresh topology from initial nodes: no initial nodes configured",
|
|
3429
3466
|
)));
|
|
3430
3467
|
}
|
|
3431
3468
|
|
|
3432
|
-
|
|
3469
|
+
// Resolve initial nodes and select random addresses for topology query.
|
|
3470
|
+
let selected_pairs = {
|
|
3471
|
+
let resolved =
|
|
3472
|
+
ClusterConnInner::<C>::try_to_expand_initial_nodes(&inner.initial_nodes).await;
|
|
3433
3473
|
let mut rng = rand::rng();
|
|
3434
|
-
|
|
3435
|
-
.clone()
|
|
3474
|
+
resolved
|
|
3436
3475
|
.into_iter()
|
|
3437
3476
|
.choose_multiple(&mut rng, num_of_nodes_to_query)
|
|
3438
3477
|
};
|
|
3439
3478
|
|
|
3440
|
-
//
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
.await;
|
|
3453
|
-
|
|
3454
|
-
// Create a future for each selected address to establish a connection.
|
|
3455
|
-
let connections = selected_addresses
|
|
3456
|
-
.into_iter()
|
|
3457
|
-
.filter_map(|address| {
|
|
3458
|
-
let conn = inner
|
|
3459
|
-
.conn_lock
|
|
3460
|
-
.read()
|
|
3461
|
-
.expect(MUTEX_READ_ERR)
|
|
3462
|
-
.management_connection_for_address(&address);
|
|
3463
|
-
conn
|
|
3464
|
-
})
|
|
3465
|
-
.collect::<Vec<_>>();
|
|
3479
|
+
// Find existing connections for selected addresses
|
|
3480
|
+
let mut connections = Vec::with_capacity(selected_pairs.len());
|
|
3481
|
+
let mut addresses_needing_refresh = HashSet::new();
|
|
3482
|
+
|
|
3483
|
+
for (original_addr, socket_addr) in selected_pairs {
|
|
3484
|
+
match lookup_management_connection(inner, &original_addr, socket_addr.map(|s| s.ip())) {
|
|
3485
|
+
ConnectionLookupResult::Found(conn) => connections.push(conn),
|
|
3486
|
+
ConnectionLookupResult::NeedsConnectionRefresh(addr) => {
|
|
3487
|
+
addresses_needing_refresh.insert(addr);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3466
3491
|
|
|
3467
|
-
Ok(
|
|
3492
|
+
Ok(InitialNodeConnectionsResult {
|
|
3493
|
+
connections,
|
|
3494
|
+
addresses_needing_refresh,
|
|
3495
|
+
})
|
|
3468
3496
|
}
|
|
3469
3497
|
|
|
3498
|
+
/// Result of attempting to find a connection for a node
|
|
3499
|
+
#[allow(clippy::type_complexity)]
|
|
3500
|
+
enum ConnectionLookupResult<C> {
|
|
3501
|
+
/// Connection found - returns the node address as stored in the connection map and the connection future
|
|
3502
|
+
Found((String, Shared<Pin<Box<dyn Future<Output = C> + Send>>>)),
|
|
3503
|
+
/// Connection not found - needs refresh for the given address
|
|
3504
|
+
NeedsConnectionRefresh(String),
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
/// Finds a management connection for a node or indicates it needs refresh.
|
|
3508
|
+
/// Returns the management connection if available, otherwise falls back to user connection.
|
|
3509
|
+
///
|
|
3510
|
+
/// # Arguments
|
|
3511
|
+
/// The `original_addr` may be provided by the user either as a hostname (DNS) or as a direct IP address.
|
|
3512
|
+
/// When a hostname is provided, we attempt to resolve it to an IP;
|
|
3513
|
+
/// if resolution succeeds, the result is passed as `resolved_ip`.
|
|
3514
|
+
///
|
|
3515
|
+
/// # Lookup Logic
|
|
3516
|
+
/// Resolve the node's canonical name from the slot map. The canonical name is the node's
|
|
3517
|
+
/// address as stored in the slot map (e.g., `my-cluster-001-001.xyz:6379`). The address may
|
|
3518
|
+
/// be provided as an IP but stored as a DNS name.
|
|
3519
|
+
///
|
|
3520
|
+
/// 1. Check if `original_addr` exists in the slot map as the canonical address (O(1))
|
|
3521
|
+
/// 2. If not, search if `resolved_ip` maps to a canonical address in the slot map (O(n))
|
|
3522
|
+
/// 3. If still not found, fall back to using `original_addr` as the canonical address
|
|
3523
|
+
///
|
|
3524
|
+
/// Then attempt to retrieve the connection for the canonical address from the connection map, or
|
|
3525
|
+
/// indicate that this address needs to be refreshed.
|
|
3526
|
+
///
|
|
3527
|
+
/// # Returns
|
|
3528
|
+
/// * `Found` - Connection was found
|
|
3529
|
+
/// * `NeedsConnectionRefresh` - Connection is missing and needs to be refreshed
|
|
3530
|
+
fn lookup_management_connection<C>(
|
|
3531
|
+
inner: &Core<C>,
|
|
3532
|
+
original_addr: &str,
|
|
3533
|
+
resolved_ip: Option<IpAddr>,
|
|
3534
|
+
) -> ConnectionLookupResult<C>
|
|
3535
|
+
where
|
|
3536
|
+
C: ConnectionLike + Connect + Clone + Send + Sync + 'static,
|
|
3537
|
+
{
|
|
3538
|
+
let original_addr_key = Arc::new(original_addr.to_string());
|
|
3539
|
+
|
|
3540
|
+
let (canonical_addr, conn_opt) = {
|
|
3541
|
+
let conn_lock = inner.conn_lock.read().expect(MUTEX_READ_ERR);
|
|
3542
|
+
|
|
3543
|
+
// Resolve canonical address: slot map lookup, fallback to original_addr
|
|
3544
|
+
let canonical_addr = if conn_lock
|
|
3545
|
+
.slot_map
|
|
3546
|
+
.nodes_map()
|
|
3547
|
+
.contains_key(&original_addr_key)
|
|
3548
|
+
{
|
|
3549
|
+
original_addr.to_string()
|
|
3550
|
+
} else {
|
|
3551
|
+
resolved_ip
|
|
3552
|
+
.and_then(|ip| {
|
|
3553
|
+
conn_lock
|
|
3554
|
+
.slot_map
|
|
3555
|
+
.node_address_for_ip(ip)
|
|
3556
|
+
.map(|a| (*a).clone())
|
|
3557
|
+
})
|
|
3558
|
+
.unwrap_or_else(|| original_addr.to_string())
|
|
3559
|
+
};
|
|
3560
|
+
|
|
3561
|
+
let conn_opt = conn_lock.management_connection_for_address(&canonical_addr);
|
|
3562
|
+
(canonical_addr, conn_opt)
|
|
3563
|
+
}; // Lock released here
|
|
3564
|
+
|
|
3565
|
+
match conn_opt {
|
|
3566
|
+
Some(conn) => ConnectionLookupResult::Found(conn),
|
|
3567
|
+
None => ConnectionLookupResult::NeedsConnectionRefresh(canonical_addr),
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
/// Result of querying random cluster nodes for topology calculation.
|
|
3571
|
+
struct TopologyQueryResult {
|
|
3572
|
+
/// The calculated topology (slot map and hash), or an error if calculation failed
|
|
3573
|
+
topology_result: RedisResult<(SlotMap, TopologyHash)>,
|
|
3574
|
+
/// Optionally addresses of connections that failed during the query and need refresh
|
|
3575
|
+
failed_connections: Option<HashSet<String>>,
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
/// Queries random cluster nodes to calculate the current topology.
|
|
3579
|
+
///
|
|
3580
|
+
/// Selects up to `num_of_nodes_to_query` random nodes and queries them for their
|
|
3581
|
+
/// view of the cluster topology. The results are aggregated to determine the
|
|
3582
|
+
/// most common topology view.
|
|
3583
|
+
///
|
|
3584
|
+
/// When `trigger` is `SlotRefreshTrigger::RuntimeRefresh` and `refresh_topology_from_initial_nodes`
|
|
3585
|
+
/// is enabled, connections are obtained from initial seed nodes rather than
|
|
3586
|
+
/// existing cluster connections.
|
|
3470
3587
|
async fn calculate_topology_from_random_nodes<C>(
|
|
3471
3588
|
inner: &Core<C>,
|
|
3472
3589
|
num_of_nodes_to_query: usize,
|
|
3473
3590
|
curr_retry: usize,
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
crate::cluster_slotmap::SlotMap,
|
|
3477
|
-
crate::cluster_topology::TopologyHash,
|
|
3478
|
-
)>,
|
|
3479
|
-
std::collections::HashSet<String>,
|
|
3480
|
-
)
|
|
3591
|
+
trigger: SlotRefreshTrigger,
|
|
3592
|
+
) -> TopologyQueryResult
|
|
3481
3593
|
where
|
|
3482
3594
|
C: ConnectionLike + Connect + Clone + Send + Sync + 'static,
|
|
3483
3595
|
{
|
|
@@ -3485,33 +3597,39 @@ where
|
|
|
3485
3597
|
.get_cluster_param(|p| p.refresh_topology_from_initial_nodes)
|
|
3486
3598
|
.unwrap_or(false);
|
|
3487
3599
|
|
|
3600
|
+
// During initial connection, use existing connections to avoid double DNS lookup
|
|
3601
|
+
let use_initial_nodes_lookup = refresh_topology_from_initial_nodes
|
|
3602
|
+
&& !matches!(trigger, SlotRefreshTrigger::InitialConnection);
|
|
3603
|
+
|
|
3488
3604
|
// Get connections either from seed nodes or random existing connections.
|
|
3489
|
-
let requested_nodes =
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3605
|
+
let (requested_nodes, mut failed_addresses) = if use_initial_nodes_lookup {
|
|
3606
|
+
match get_random_connections_from_initial_nodes(inner, num_of_nodes_to_query).await {
|
|
3607
|
+
Ok(InitialNodeConnectionsResult {
|
|
3608
|
+
connections,
|
|
3609
|
+
addresses_needing_refresh,
|
|
3610
|
+
}) => (connections, addresses_needing_refresh),
|
|
3493
3611
|
Err(err) => {
|
|
3494
|
-
return
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
if let Some(random_conns) = inner
|
|
3499
|
-
.conn_lock
|
|
3500
|
-
.read()
|
|
3501
|
-
.expect(MUTEX_READ_ERR)
|
|
3502
|
-
.random_connections(num_of_nodes_to_query, ConnectionType::PreferManagement)
|
|
3503
|
-
{
|
|
3504
|
-
random_conns
|
|
3505
|
-
} else {
|
|
3506
|
-
return (
|
|
3507
|
-
Err(RedisError::from((
|
|
3508
|
-
ErrorKind::AllConnectionsUnavailable,
|
|
3509
|
-
"No available connections to refresh slots from",
|
|
3510
|
-
))),
|
|
3511
|
-
std::collections::HashSet::new(),
|
|
3512
|
-
);
|
|
3612
|
+
return TopologyQueryResult {
|
|
3613
|
+
topology_result: Err(err),
|
|
3614
|
+
failed_connections: None,
|
|
3615
|
+
};
|
|
3513
3616
|
}
|
|
3514
3617
|
}
|
|
3618
|
+
} else if let Some(random_conns) = inner
|
|
3619
|
+
.conn_lock
|
|
3620
|
+
.read()
|
|
3621
|
+
.expect(MUTEX_READ_ERR)
|
|
3622
|
+
.random_connections(num_of_nodes_to_query, ConnectionType::PreferManagement)
|
|
3623
|
+
{
|
|
3624
|
+
(random_conns, HashSet::new())
|
|
3625
|
+
} else {
|
|
3626
|
+
return TopologyQueryResult {
|
|
3627
|
+
topology_result: Err(RedisError::from((
|
|
3628
|
+
ErrorKind::AllConnectionsUnavailable,
|
|
3629
|
+
"No available connections to refresh slots from",
|
|
3630
|
+
))),
|
|
3631
|
+
failed_connections: None,
|
|
3632
|
+
};
|
|
3515
3633
|
};
|
|
3516
3634
|
|
|
3517
3635
|
let topology_join_results =
|
|
@@ -3523,13 +3641,14 @@ where
|
|
|
3523
3641
|
.await;
|
|
3524
3642
|
|
|
3525
3643
|
// Add topology command failures (with unrecoverable errors) to the existing connection failures set.
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3644
|
+
failed_addresses.extend(
|
|
3645
|
+
topology_join_results
|
|
3646
|
+
.iter()
|
|
3647
|
+
.filter_map(|(address, res)| match res {
|
|
3648
|
+
Err(err) if err.is_unrecoverable_error() => Some(address.clone()),
|
|
3649
|
+
_ => None,
|
|
3650
|
+
}),
|
|
3651
|
+
);
|
|
3533
3652
|
|
|
3534
3653
|
let topology_values = topology_join_results.iter().filter_map(|(addr, res)| {
|
|
3535
3654
|
res.as_ref()
|
|
@@ -3543,16 +3662,16 @@ where
|
|
|
3543
3662
|
let read_from_replicas = inner
|
|
3544
3663
|
.get_cluster_param(|params| params.read_from_replicas.clone())
|
|
3545
3664
|
.expect(MUTEX_READ_ERR);
|
|
3546
|
-
|
|
3547
|
-
calculate_topology(
|
|
3665
|
+
TopologyQueryResult {
|
|
3666
|
+
topology_result: calculate_topology(
|
|
3548
3667
|
topology_values,
|
|
3549
3668
|
curr_retry,
|
|
3550
3669
|
tls_mode,
|
|
3551
3670
|
num_of_nodes_to_query,
|
|
3552
3671
|
read_from_replicas,
|
|
3553
3672
|
),
|
|
3554
|
-
failed_addresses,
|
|
3555
|
-
|
|
3673
|
+
failed_connections: Some(failed_addresses),
|
|
3674
|
+
}
|
|
3556
3675
|
}
|
|
3557
3676
|
|
|
3558
3677
|
impl<C> ConnectionLike for ClusterConnection<C>
|