Skip to content

Commit b0c3c1c

Browse files
PHP: Add refresh topology configuration (#70)
* PHP: Add refresh topology Signed-off-by: Prateek Kumar <[email protected]>
1 parent 13de2c8 commit b0c3c1c

10 files changed

+217
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#### Changes
44

5+
* PHP: Add refresh topology configuration
56
* PHP: Add Multi-Database Support for Cluster Mode Valkey 9.0 - Added `database_id` parameter to `ValkeyGlideCluster` constructor and support for SELECT, COPY, and MOVE commands in cluster mode. The COPY command can now specify a `database_id` parameter for cross-database operations. This feature requires Valkey 9.0+ with `cluster-databases > 1` configuration.
67

78
#### Documentation

common.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ typedef struct {
138138
valkey_glide_base_client_configuration_t base;
139139
valkey_glide_periodic_checks_status_t periodic_checks_status;
140140
valkey_glide_periodic_checks_manual_interval_t*
141-
periodic_checks_manual; /* NULL if using status */
141+
periodic_checks_manual; /* NULL if using status */
142+
bool refresh_topology_from_initial_nodes; /* false if not set */
142143
} valkey_glide_cluster_client_configuration_t;
143144

144145
/* Configuration parsing functions */

src/client_constructor_mock.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ PHP_METHOD(ClientConstructorMock, simulate_standalone_constructor) {
125125
/* Build the connection request. */
126126
size_t protobuf_message_len;
127127
uint8_t* request_bytes = create_connection_request(
128-
"localhost", 6379, &protobuf_message_len, &client_config, 0, false);
128+
"localhost", 6379, &protobuf_message_len, &client_config, 0, false, false);
129129

130130
zval* php_request =
131131
build_php_connection_request(request_bytes, protobuf_message_len, &client_config);
@@ -175,14 +175,28 @@ PHP_METHOD(ClientConstructorMock, simulate_cluster_constructor) {
175175
/* Populate configuration parameters shared between client and cluster connections. */
176176
valkey_glide_build_client_config_base(&common_params, &client_config.base, true);
177177

178+
/* Parse cluster-specific advanced config options */
179+
client_config.refresh_topology_from_initial_nodes = false; /* Default value */
180+
if (common_params.advanced_config && Z_TYPE_P(common_params.advanced_config) == IS_ARRAY) {
181+
HashTable* advanced_ht = Z_ARRVAL_P(common_params.advanced_config);
182+
const char key_name[] = "refresh_topology_from_initial_nodes";
183+
zval* refresh_topology_val =
184+
zend_hash_str_find(advanced_ht, key_name, sizeof(key_name) - 1);
185+
if (refresh_topology_val && Z_TYPE_P(refresh_topology_val) == IS_TRUE) {
186+
client_config.refresh_topology_from_initial_nodes = true;
187+
}
188+
}
189+
178190
/* Build the connection request. */
179191
size_t protobuf_message_len;
180-
uint8_t* request_bytes = create_connection_request("localhost",
181-
6379,
182-
&protobuf_message_len,
183-
&client_config.base,
184-
client_config.periodic_checks_status,
185-
true);
192+
uint8_t* request_bytes =
193+
create_connection_request("localhost",
194+
6379,
195+
&protobuf_message_len,
196+
&client_config.base,
197+
client_config.periodic_checks_status,
198+
true,
199+
client_config.refresh_topology_from_initial_nodes);
186200

187201
zval* php_request =
188202
build_php_connection_request(request_bytes, protobuf_message_len, &client_config.base);

tests/ConnectionRequestTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,4 +460,48 @@ public function testClusterPeriodicChecksDefault()
460460
$this->assertFalse($request->hasPeriodicChecksDisabled());
461461
$this->assertTrue($request->hasPeriodicChecksManualInterval());
462462
}
463+
464+
public function testClusterRefreshTopologyFromInitialNodesDefault()
465+
{
466+
// Test that refresh_topology_from_initial_nodes defaults to false when not specified
467+
$request = ClientConstructorMock::simulate_cluster_constructor(
468+
addresses: [['host' => 'localhost', 'port' => 8080]]
469+
);
470+
471+
$this->assertFalse($request->getRefreshTopologyFromInitialNodes());
472+
}
473+
474+
public function testClusterRefreshTopologyFromInitialNodesEnabled()
475+
{
476+
// Test that refresh_topology_from_initial_nodes can be set to true
477+
$request = ClientConstructorMock::simulate_cluster_constructor(
478+
addresses: [['host' => 'localhost', 'port' => 8080]],
479+
advanced_config: ['refresh_topology_from_initial_nodes' => true]
480+
);
481+
482+
$this->assertTrue($request->getRefreshTopologyFromInitialNodes());
483+
}
484+
485+
public function testClusterRefreshTopologyFromInitialNodesDisabled()
486+
{
487+
// Test that refresh_topology_from_initial_nodes can be explicitly set to false
488+
$request = ClientConstructorMock::simulate_cluster_constructor(
489+
addresses: [['host' => 'localhost', 'port' => 8080]],
490+
advanced_config: ['refresh_topology_from_initial_nodes' => false]
491+
);
492+
493+
$this->assertFalse($request->getRefreshTopologyFromInitialNodes());
494+
}
495+
496+
public function testStandaloneRefreshTopologyFromInitialNodesIgnored()
497+
{
498+
// Test that refresh_topology_from_initial_nodes is ignored for standalone clients
499+
$request = ClientConstructorMock::simulate_standalone_constructor(
500+
addresses: [['host' => 'localhost', 'port' => 8080]],
501+
advanced_config: ['refresh_topology_from_initial_nodes' => true]
502+
);
503+
504+
// Standalone clients should always have this as false (ignored)
505+
$this->assertFalse($request->getRefreshTopologyFromInitialNodes());
506+
}
463507
}

tests/ValkeyGlideClusterTest.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,4 +1062,79 @@ public function testCopyMultiDatabase()
10621062
$this->valkey_glide->select(0);
10631063
$this->valkey_glide->del($srcKey);
10641064
}
1065+
1066+
public function testRefreshTopologyFromInitialNodesDefault()
1067+
{
1068+
// Test that refresh_topology_from_initial_nodes defaults to false when not specified
1069+
$client = new ValkeyGlideCluster(
1070+
addresses: [['host' => 'localhost', 'port' => 7001]],
1071+
use_tls: false,
1072+
credentials: null,
1073+
read_from: ValkeyGlide::READ_FROM_PREFER_REPLICA,
1074+
request_timeout: null,
1075+
reconnect_strategy: null,
1076+
client_name: null,
1077+
periodic_checks: ValkeyGlideCluster::PERIODIC_CHECK_ENABLED_DEFAULT_CONFIGS,
1078+
client_az: null,
1079+
advanced_config: ['connection_timeout' => 5000]
1080+
);
1081+
1082+
// Perform basic operation to verify client works
1083+
$result = $client->ping(['type' => 'primarySlotKey', 'key' => 'test']);
1084+
$this->assertTrue($result);
1085+
1086+
$client->close();
1087+
}
1088+
1089+
public function testRefreshTopologyFromInitialNodesEnabled()
1090+
{
1091+
// Test that refresh_topology_from_initial_nodes can be set to true
1092+
$client = new ValkeyGlideCluster(
1093+
addresses: [['host' => 'localhost', 'port' => 7001]],
1094+
use_tls: false,
1095+
credentials: null,
1096+
read_from: ValkeyGlide::READ_FROM_PREFER_REPLICA,
1097+
request_timeout: null,
1098+
reconnect_strategy: null,
1099+
client_name: null,
1100+
periodic_checks: ValkeyGlideCluster::PERIODIC_CHECK_ENABLED_DEFAULT_CONFIGS,
1101+
client_az: null,
1102+
advanced_config: [
1103+
'connection_timeout' => 5000,
1104+
'refresh_topology_from_initial_nodes' => true
1105+
]
1106+
);
1107+
1108+
// Perform basic operation to verify client works
1109+
$result = $client->ping(['type' => 'primarySlotKey', 'key' => 'test']);
1110+
$this->assertTrue($result);
1111+
1112+
$client->close();
1113+
}
1114+
1115+
public function testRefreshTopologyFromInitialNodesDisabled()
1116+
{
1117+
// Test that refresh_topology_from_initial_nodes can be explicitly set to false
1118+
$client = new ValkeyGlideCluster(
1119+
addresses: [['host' => 'localhost', 'port' => 7001]],
1120+
use_tls: false,
1121+
credentials: null,
1122+
read_from: ValkeyGlide::READ_FROM_PREFER_REPLICA,
1123+
request_timeout: null,
1124+
reconnect_strategy: null,
1125+
client_name: null,
1126+
periodic_checks: ValkeyGlideCluster::PERIODIC_CHECK_ENABLED_DEFAULT_CONFIGS,
1127+
client_az: null,
1128+
advanced_config: [
1129+
'connection_timeout' => 5000,
1130+
'refresh_topology_from_initial_nodes' => false
1131+
]
1132+
);
1133+
1134+
// Perform basic operation to verify client works
1135+
$result = $client->ping(['type' => 'primarySlotKey', 'key' => 'test']);
1136+
$this->assertTrue($result);
1137+
1138+
$client->close();
1139+
}
10651140
}

tests/ValkeyGlideTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7829,4 +7829,30 @@ public function testGetCommandWithConsoleLogging()
78297829
echo "=======================================================\n";
78307830
}
78317831
}
7832+
7833+
public function testRefreshTopologyFromInitialNodesIgnoredInStandalone()
7834+
{
7835+
// Test that refresh_topology_from_initial_nodes is ignored for standalone clients
7836+
$client = new ValkeyGlide(
7837+
addresses: [['host' => 'localhost', 'port' => 6379]],
7838+
use_tls: false,
7839+
credentials: null,
7840+
read_from: ValkeyGlide::READ_FROM_PRIMARY,
7841+
request_timeout: null,
7842+
reconnect_strategy: null,
7843+
database_id: null,
7844+
client_name: null,
7845+
client_az: null,
7846+
advanced_config: [
7847+
'connection_timeout' => 5000,
7848+
'refresh_topology_from_initial_nodes' => true // Should be completely ignored
7849+
]
7850+
);
7851+
7852+
// Perform basic operation to verify client works
7853+
$result = $client->ping();
7854+
$this->assertTrue($result);
7855+
7856+
$client->close();
7857+
}
78327858
}

valkey_glide_cluster.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ PHP_METHOD(ValkeyGlideCluster, __construct) {
7878
/* Populate configuration parameters shared between client and cluster connections. */
7979
valkey_glide_build_client_config_base(&common_params, &client_config.base, true);
8080

81+
/* Parse cluster-specific advanced config options */
82+
client_config.refresh_topology_from_initial_nodes = false; /* Default value */
83+
if (common_params.advanced_config && Z_TYPE_P(common_params.advanced_config) == IS_ARRAY) {
84+
HashTable* advanced_ht = Z_ARRVAL_P(common_params.advanced_config);
85+
const char key_name[] = "refresh_topology_from_initial_nodes";
86+
zval* refresh_topology_val =
87+
zend_hash_str_find(advanced_ht, key_name, sizeof(key_name) - 1);
88+
if (refresh_topology_val && Z_TYPE_P(refresh_topology_val) == IS_TRUE) {
89+
client_config.refresh_topology_from_initial_nodes = true;
90+
}
91+
}
92+
8193
/* Issue the connection request. */
8294
const ConnectionResponse* conn_resp = create_glide_cluster_client(&client_config);
8395

valkey_glide_cluster.stub.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ class ValkeyGlideCluster
153153
*/
154154
public const IAM_CONFIG_REFRESH_INTERVAL = 'refreshIntervalSeconds';
155155

156+
/**
157+
* @var string
158+
* Advanced config key for refresh topology from initial nodes option
159+
*/
160+
public const ADVANCED_CONFIG_REFRESH_TOPOLOGY_FROM_INITIAL_NODES = 'refresh_topology_from_initial_nodes';
161+
156162
/**
157163
* @var int
158164
* Enables the periodic checks with the default configurations.
@@ -186,9 +192,11 @@ class ValkeyGlideCluster
186192
* @param string|null $client_name Client name identifier.
187193
* @param int|null $periodic_checks Periodic checks configuration.
188194
* @param string|null $client_az Client availability zone.
189-
* @param array|null $advanced_config Advanced configuration ['connection_timeout' => 5000,
190-
* 'tls_config' => ['use_insecure_tls' => false]].
191-
* connection_timeout is in milliseconds.
195+
* @param array|null $advanced_config Advanced configuration options:
196+
* - 'connection_timeout' => 5000 (milliseconds)
197+
* - 'tls_config' => ['use_insecure_tls' => false]
198+
* - 'refresh_topology_from_initial_nodes' => false (default: false)
199+
* When true, topology updates use only initial nodes instead of internal cluster view.
192200
* @param bool|null $lazy_connect Whether to use lazy connection.
193201
* @param int|null $database_id Index of the logical database to connect to. Must be non-negative
194202
* and within the range supported by the server configuration.

valkey_glide_commands_common.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ uint8_t* create_connection_request(const char* hos
7070
size_t* len,
7171
valkey_glide_base_client_configuration_t* config,
7272
valkey_glide_periodic_checks_status_t periodic_checks,
73-
bool is_cluster);
73+
bool is_cluster,
74+
bool refresh_topology_from_initial_nodes);
7475

7576
/* Bit operations - UNIFIED SIGNATURES */
7677
int execute_bitcount_command(zval* object, int argc, zval* return_value, zend_class_entry* ce);

valkey_glide_core_commands.c

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ uint8_t* create_connection_request(const char* hos
4646
size_t* len,
4747
valkey_glide_base_client_configuration_t* config,
4848
valkey_glide_periodic_checks_status_t periodic_checks,
49-
bool is_cluster) {
49+
bool is_cluster,
50+
bool refresh_topology_from_initial_nodes) {
5051
/* Create a connection request */
5152
ConnectionRequest__ConnectionRequest conn_req = CONNECTION_REQUEST__CONNECTION_REQUEST__INIT;
5253

@@ -133,6 +134,13 @@ uint8_t* create_connection_request(const char* hos
133134
conn_req.connection_timeout = config->advanced_config->connection_timeout;
134135
}
135136

137+
/* Set refresh topology from initial nodes for cluster mode */
138+
if (is_cluster) {
139+
conn_req.refresh_topology_from_initial_nodes = refresh_topology_from_initial_nodes;
140+
} else {
141+
conn_req.refresh_topology_from_initial_nodes = false;
142+
}
143+
136144
conn_req.lazy_connect = config->lazy_connect;
137145
/* Map read_from configuration */
138146
if (config->read_from == VALKEY_GLIDE_READ_FROM_PREFER_REPLICA) {
@@ -208,14 +216,20 @@ uint8_t* create_connection_request(const char* hos
208216
static const ConnectionResponse* create_base_glide_client(
209217
valkey_glide_base_client_configuration_t* config,
210218
valkey_glide_periodic_checks_status_t periodic_checks,
211-
bool is_cluster) {
219+
bool is_cluster,
220+
bool refresh_topology_from_initial_nodes) {
212221
/* Create a connection request using first address or default */
213222
size_t len;
214223
const char* default_host = "localhost";
215224
int default_port = 6379;
216225

217-
uint8_t* request_bytes = create_connection_request(
218-
default_host, default_port, &len, config, periodic_checks, is_cluster);
226+
uint8_t* request_bytes = create_connection_request(default_host,
227+
default_port,
228+
&len,
229+
config,
230+
periodic_checks,
231+
is_cluster,
232+
refresh_topology_from_initial_nodes);
219233

220234
if (!request_bytes) {
221235
return NULL;
@@ -243,12 +257,15 @@ static const ConnectionResponse* create_base_glide_client(
243257

244258
/* Create a Valkey Glide client */
245259
const ConnectionResponse* create_glide_client(valkey_glide_base_client_configuration_t* config) {
246-
return create_base_glide_client(config, VALKEY_GLIDE_PERIODIC_CHECKS_DISABLED, false);
260+
return create_base_glide_client(config, VALKEY_GLIDE_PERIODIC_CHECKS_DISABLED, false, false);
247261
}
248262

249263
const ConnectionResponse* create_glide_cluster_client(
250264
valkey_glide_cluster_client_configuration_t* config) {
251-
return create_base_glide_client(&config->base, config->periodic_checks_status, true);
265+
return create_base_glide_client(&config->base,
266+
config->periodic_checks_status,
267+
true,
268+
config->refresh_topology_from_initial_nodes);
252269
}
253270

254271
/* Custom result processor for SET commands with GET option support */

0 commit comments

Comments
 (0)