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.
Files changed (157) hide show
  1. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/PKG-INFO +1 -1
  2. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster.rs +15 -4
  3. {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
  4. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_async/mod.rs +242 -123
  5. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_slotmap.rs +379 -32
  6. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_topology.rs +520 -34
  7. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/_glide_ffi.py +2 -4
  8. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/glide_json.py +1 -0
  9. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/pyproject.toml +1 -1
  10. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/PKG-INFO +1 -1
  11. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/MANIFEST.in +0 -0
  12. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/README.md +0 -0
  13. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/.cargo/config.toml +0 -0
  14. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/.gitignore +0 -0
  15. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/.ort.yml +0 -0
  16. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/Cargo.toml +0 -0
  17. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/DEVELOPER.md +0 -0
  18. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/README.md +0 -0
  19. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/cbindgen.toml +0 -0
  20. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/Cargo.toml +0 -0
  21. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/README.md +0 -0
  22. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/Cargo.toml +0 -0
  23. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/client.rs +0 -0
  24. {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
  25. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/command_request.rs +0 -0
  26. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/connection_request.rs +0 -0
  27. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/errors.rs +0 -0
  28. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/lib.rs +0 -0
  29. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/request_type.rs +0 -0
  30. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-glide-core/src/scripts_container.rs +0 -0
  31. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-logger-core/Cargo.toml +0 -0
  32. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-logger-core/src/lib.rs +0 -0
  33. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-redis/Cargo.toml +0 -0
  34. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-redis/src/cluster_routing.rs +0 -0
  35. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-redis/src/cluster_topology.rs +0 -0
  36. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-redis/src/lib.rs +0 -0
  37. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-telemetry/Cargo.toml +0 -0
  38. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-telemetry/src/lib.rs +0 -0
  39. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/Cargo.toml +0 -0
  40. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/src/lib.rs +0 -0
  41. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/src/runtime.rs +0 -0
  42. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/src/sync.rs +0 -0
  43. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/mock-tokio/src/task.rs +0 -0
  44. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/miri-tests/rust-toolchain.toml +0 -0
  45. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/ffi/src/lib.rs +0 -0
  46. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/.cargo/config.toml +0 -0
  47. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/.ort.yml +0 -0
  48. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/Cargo.toml +0 -0
  49. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/README.md +0 -0
  50. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/THIRD_PARTY_LICENSES_RUST +0 -0
  51. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/build.rs +0 -0
  52. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/Cargo.toml +0 -0
  53. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/LICENSE +0 -0
  54. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/README.md +0 -0
  55. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/appveyor.yml +0 -0
  56. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/get_command_info.py +0 -0
  57. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/CHANGELOG.md +0 -0
  58. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/Cargo.toml +0 -0
  59. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/LICENSE +0 -0
  60. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/async-await.rs +0 -0
  61. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/async-multiplexed.rs +0 -0
  62. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/async-pub-sub.rs +0 -0
  63. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/async-scan.rs +0 -0
  64. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/examples/basic.rs +0 -0
  65. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/release.toml +0 -0
  66. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/connection.rs +0 -0
  67. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/connection_manager.rs +0 -0
  68. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/mod.rs +0 -0
  69. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/multiplexed_connection.rs +0 -0
  70. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/runtime.rs +0 -0
  71. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/aio/tokio.rs +0 -0
  72. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/client.rs +0 -0
  73. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_async/LICENSE +0 -0
  74. {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
  75. {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
  76. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_client.rs +0 -0
  77. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_pipeline.rs +0 -0
  78. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cluster_routing.rs +0 -0
  79. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/cmd.rs +0 -0
  80. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/commands/cluster_scan.rs +0 -0
  81. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/commands/macros.rs +0 -0
  82. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/commands/mod.rs +0 -0
  83. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/connection.rs +0 -0
  84. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/lib.rs +0 -0
  85. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/macros.rs +0 -0
  86. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/parser.rs +0 -0
  87. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/pipeline.rs +0 -0
  88. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/push_manager.rs +0 -0
  89. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/retry_strategies.rs +0 -0
  90. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/sentinel.rs +0 -0
  91. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/tls.rs +0 -0
  92. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/redis/src/types.rs +0 -0
  93. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/rustfmt.toml +0 -0
  94. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/redis-rs/src/main.rs +0 -0
  95. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/mod.rs +0 -0
  96. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/reconnecting_connection.rs +0 -0
  97. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/standalone_client.rs +0 -0
  98. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/types.rs +0 -0
  99. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/client/value_conversion.rs +0 -0
  100. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/cluster_scan_container.rs +0 -0
  101. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/errors.rs +0 -0
  102. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/iam/mod.rs +0 -0
  103. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/lib.rs +0 -0
  104. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/protobuf/command_request.proto +0 -0
  105. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/protobuf/connection_request.proto +0 -0
  106. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/protobuf/response.proto +0 -0
  107. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/request_type.rs +0 -0
  108. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/rotating_buffer.rs +0 -0
  109. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/scripts_container.rs +0 -0
  110. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/src/socket_listener.rs +0 -0
  111. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/Cargo.toml +0 -0
  112. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/src/lib.rs +0 -0
  113. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/src/metrics_exporter_file.rs +0 -0
  114. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/src/open_telemetry.rs +0 -0
  115. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide-core/telemetry/src/span_exporter_file.rs +0 -0
  116. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/__init__.py +0 -0
  117. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/__init__.py +0 -0
  118. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/batch.py +0 -0
  119. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/batch_options.py +0 -0
  120. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/bitmap.py +0 -0
  121. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/command_args.py +0 -0
  122. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/core_options.py +0 -0
  123. {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
  124. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/ft_options/ft_constants.py +0 -0
  125. {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
  126. {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
  127. {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
  128. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/json_batch.py +0 -0
  129. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/server_modules/json_options.py +0 -0
  130. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/sorted_set.py +0 -0
  131. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/commands/stream.py +0 -0
  132. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/config.py +0 -0
  133. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/constants.py +0 -0
  134. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/exceptions.py +0 -0
  135. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/protobuf_codec.py +0 -0
  136. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/py.typed +0 -0
  137. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_shared/routes.py +0 -0
  138. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/__init__.py +0 -0
  139. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/config.py +0 -0
  140. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/glide_client.py +0 -0
  141. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/logger.py +0 -0
  142. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/py.typed +0 -0
  143. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/__init__.py +0 -0
  144. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/cluster_commands.py +0 -0
  145. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/cluster_scan_cursor.py +0 -0
  146. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/core.py +0 -0
  147. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/ft.py +0 -0
  148. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/script.py +0 -0
  149. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/glide_sync/sync_commands/standalone_commands.py +0 -0
  150. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/logger_core/Cargo.toml +0 -0
  151. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/logger_core/src/lib.rs +0 -0
  152. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/setup.cfg +0 -0
  153. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/setup.py +0 -0
  154. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/SOURCES.txt +0 -0
  155. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/dependency_links.txt +0 -0
  156. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/requires.txt +0 -0
  157. {valkey_glide_sync-2.2.5 → valkey_glide_sync-2.2.6}/valkey_glide_sync.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: valkey-glide-sync
3
- Version: 2.2.5
3
+ Version: 2.2.6
4
4
  Summary: Valkey GLIDE Sync client. Supports Valkey and Redis OSS.
5
5
  License: Apache-2.0
6
6
  Classifier: Topic :: Database
@@ -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(|slots_data| {
386
- SlotMap::new(slots_data.1, self.cluster_params.read_from_replicas.clone())
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 pairs of the node address and its associated shard addresses.
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.management_connection
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, DEFAULT_NUMBER_OF_REFRESH_SLOTS_RETRIES,
38
- DEFAULT_REFRESH_SLOTS_RETRY_BASE_DURATION_MILLIS, DEFAULT_REFRESH_SLOTS_RETRY_BASE_FACTOR,
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::{log_error, log_warn};
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(inner_clone, &policy_clone).await
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(inner.clone(), policy).await?;
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 (res, failed_connections) = calculate_topology_from_random_nodes(
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)) = res {
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 !failed_connections.is_empty() {
2209
- trace!("check_for_topology_diff: calling trigger_refresh_connection_tasks");
2210
- Self::trigger_refresh_connection_tasks(
2211
- inner,
2212
- failed_connections,
2213
- RefreshConnectionType::OnlyManagementConnection,
2214
- true,
2215
- )
2216
- .await;
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(inner: Arc<InnerCore<C>>, curr_retry: usize) -> RedisResult<()> {
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(inner: Arc<InnerCore<C>>, curr_retry: usize) -> RedisResult<()> {
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
- calculate_topology_from_random_nodes(&inner, num_of_nodes_to_query, curr_retry)
2238
- .await
2239
- .0?;
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
- .slot_map
2392
- .add_new_primary(slot, new_primary);
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
- // Retrieves random connections from initial seed nodes after resolving their addresses.
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<Vec<(String, Shared<Pin<Box<dyn Future<Output = C> + Send>>>)>>
3458
+ ) -> RedisResult<InitialNodeConnectionsResult<C>>
3404
3459
  where
3405
3460
  C: ConnectionLike + Connect + Clone + Send + Sync + 'static,
3406
3461
  {
3407
- // Resolve initial nodes to get their addresses.
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::AllConnectionsUnavailable,
3428
- "No valid addresses found",
3464
+ ErrorKind::InvalidClientConfig,
3465
+ "Cannot refresh topology from initial nodes: no initial nodes configured",
3429
3466
  )));
3430
3467
  }
3431
3468
 
3432
- let selected_addresses: Vec<String> = {
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
- valid_addresses
3435
- .clone()
3474
+ resolved
3436
3475
  .into_iter()
3437
3476
  .choose_multiple(&mut rng, num_of_nodes_to_query)
3438
3477
  };
3439
3478
 
3440
- // Run refresh_and_update_connections with a timeout of connection timeout
3441
- // If selected hosts do not have active connections, this will initiate connections to them and will add them to the connection map.
3442
- // If they already have active connections, it will return those connections.
3443
- let _ = tokio::time::timeout(
3444
- inner.get_cluster_param(|p| p.connection_timeout)?,
3445
- ClusterConnInner::refresh_and_update_connections(
3446
- inner.clone(),
3447
- selected_addresses.iter().cloned().collect(),
3448
- RefreshConnectionType::OnlyManagementConnection,
3449
- true,
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(connections)
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
- RedisResult<(
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 = match refresh_topology_from_initial_nodes {
3490
- true => match get_random_connections_from_initial_nodes(inner, num_of_nodes_to_query).await
3491
- {
3492
- Ok(connections_futures) => connections_futures,
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 (Err(err), std::collections::HashSet::new());
3495
- }
3496
- },
3497
- false => {
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
- let failed_addresses = topology_join_results
3527
- .iter()
3528
- .filter_map(|(address, res)| match res {
3529
- Err(err) if err.is_unrecoverable_error() => Some(address.clone()),
3530
- _ => None,
3531
- })
3532
- .collect::<std::collections::HashSet<String>>();
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>