Skip to content

Commit b2fc5d9

Browse files
committed
PYCBC-1758: Propagate missing ClusterOptions to C++ core
Changes ------- * Propagate missing ClusterOptions to C++ core * Add test to prevent future missing options Change-Id: Ia3f66caf1c67d9d353381c5f7fde825cf043894b Reviewed-on: https://review.couchbase.org/c/couchbase-python-client/+/242776 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Dimitris Christodoulou <dimitris.christodoulou@couchbase.com>
1 parent 62adf0b commit b2fc5d9

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

couchbase/tests/connection_t.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class ConnectionTestSuite:
8787
'test_valid_connection_strings',
8888
'test_wan_config_profile',
8989
'test_wan_config_profile_with_auth',
90+
'test_cluster_options_core_round_trip',
9091
]
9192

9293
def test_cluster_auth_fail(self, couchbase_config):
@@ -381,6 +382,80 @@ def test_cluster_options(self, couchbase_config):
381382
assert cluster_opts == expected_opts
382383
assert user_agent == f'python/{platform.python_version()}'
383384

385+
# creating a new connection, allow retries
386+
@pytest.mark.flaky(reruns=5, reruns_delay=1)
387+
def test_cluster_options_core_round_trip(self, couchbase_config):
388+
# Validates that ClusterOptions are correctly propagated into the C++ core by
389+
# creating a real connection and checking get_connection_info().
390+
#
391+
# Options intentionally excluded from this test (validated at Python layer only
392+
# in test_cluster_options via skip_connect):
393+
# - trust_store_path: requires a TLS-capable server and a valid cert file on disk
394+
# - network ('external'): may route to unreachable endpoints in test environments
395+
# - enable_tracing / enable_metrics / enable_orphan_reporting: tracing/metrics options
396+
# are validated in test_cluster_tracing_options* and test_cluster_metrics_options*
397+
# - app_telemetry_endpoint: omitted because enable_app_telemetry is disabled here
398+
# - num_io_threads: not a cluster_options field; passed as a connection-level parameter
399+
opts = {
400+
'enable_tls': False,
401+
'tls_verify': TLSVerifyMode.NO_VERIFY,
402+
'ip_protocol': IpProtocol.Any,
403+
'enable_dns_srv': False,
404+
'enable_mutation_tokens': True,
405+
'enable_tcp_keep_alive': True,
406+
'enable_compression': True,
407+
'show_queries': True,
408+
'enable_unordered_execution': False,
409+
'enable_clustermap_notification': False,
410+
'disable_mozilla_ca_certificates': False,
411+
'tcp_keep_alive_interval': timedelta(seconds=30),
412+
'config_poll_interval': timedelta(seconds=30),
413+
'config_poll_floor': timedelta(seconds=5),
414+
'max_http_connections': 5,
415+
'dump_configuration': True,
416+
'preferred_server_group': 'test_group',
417+
'enable_app_telemetry': False,
418+
'allow_enterprise_analytics': True,
419+
'app_telemetry_backoff': timedelta(seconds=10),
420+
'app_telemetry_ping_interval': timedelta(seconds=60),
421+
'app_telemetry_ping_timeout': timedelta(seconds=5),
422+
}
423+
424+
expected = {
425+
'enable_tls': False,
426+
'tls_verify': 'none',
427+
'use_ip_protocol': 'any',
428+
'enable_dns_srv': False,
429+
'enable_mutation_tokens': True,
430+
'enable_tcp_keep_alive': True,
431+
'enable_compression': True,
432+
'show_queries': True,
433+
'enable_unordered_execution': False,
434+
'enable_clustermap_notification': False,
435+
'disable_mozilla_ca_certificates': False,
436+
'tcp_keep_alive_interval': 30000,
437+
'config_poll_interval': 30000,
438+
'config_poll_floor': 5000,
439+
'max_http_connections': 5,
440+
'dump_configuration': True,
441+
'preferred_server_group': 'test_group',
442+
'enable_app_telemetry': False,
443+
'allow_enterprise_analytics': True,
444+
'app_telemetry_backoff': 10000,
445+
'app_telemetry_ping_interval': 60000,
446+
'app_telemetry_ping_timeout': 5000,
447+
}
448+
449+
conn_string = couchbase_config.get_connection_string()
450+
username, pw = couchbase_config.get_username_and_pw()
451+
auth = PasswordAuthenticator(username, pw)
452+
cluster = Cluster.connect(conn_string, ClusterOptions(auth, **opts))
453+
client_opts = cluster._impl.get_connection_info()
454+
for key, expected_value in expected.items():
455+
assert client_opts.get(key) == expected_value, (
456+
f'Expected {key}={expected_value!r}, got {client_opts.get(key)!r}'
457+
)
458+
384459
def test_cluster_pw_auth(self, couchbase_config):
385460
conn_string = couchbase_config.get_connection_string()
386461
username, pw = couchbase_config.get_username_and_pw()

src/utils.hxx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,13 @@ update_cluster_options_from_py(couchbase::core::cluster_options& options,
237237
extract_bool_field(
238238
pyObj_options, "disable_mozilla_ca_certificates", options.disable_mozilla_ca_certificates);
239239
extract_bool_field(pyObj_options, "enable_lazy_connections", options.enable_lazy_connections);
240+
extract_bool_field(pyObj_options, "show_queries", options.show_queries);
241+
extract_bool_field(
242+
pyObj_options, "enable_unordered_execution", options.enable_unordered_execution);
243+
extract_bool_field(
244+
pyObj_options, "enable_clustermap_notification", options.enable_clustermap_notification);
245+
extract_bool_field(
246+
pyObj_options, "allow_enterprise_analytics", options.allow_enterprise_analytics);
240247

241248
// Always disable metrics for now
242249
options.enable_metrics = false;
@@ -255,6 +262,32 @@ update_cluster_options_from_py(couchbase::core::cluster_options& options,
255262
// Other
256263
extract_field<std::chrono::milliseconds>(
257264
pyObj_options, "config_poll_interval", options.config_poll_interval);
265+
extract_field<std::chrono::milliseconds>(
266+
pyObj_options, "config_poll_floor", options.config_poll_floor);
267+
extract_field_if_not_empty<std::string>(
268+
pyObj_options, "user_agent_extra", options.user_agent_extra);
269+
extract_field<std::size_t>(pyObj_options, "max_http_connections", options.max_http_connections);
270+
extract_bool_field(pyObj_options, "dump_configuration", options.dump_configuration);
271+
272+
// Preferred server group (Python key "preferred_server_group" -> C++ field "server_group")
273+
{
274+
PyObject* pyObj_server_group = PyDict_GetItemString(pyObj_options, "preferred_server_group");
275+
if (pyObj_server_group && PyUnicode_Check(pyObj_server_group)) {
276+
options.server_group = std::string(PyUnicode_AsUTF8(pyObj_server_group));
277+
}
278+
}
279+
280+
// App telemetry options (Python key "app_telemetry_backoff" -> C++ field
281+
// "app_telemetry_backoff_interval")
282+
extract_bool_field(pyObj_options, "enable_app_telemetry", options.enable_app_telemetry);
283+
extract_field_if_not_empty<std::string>(
284+
pyObj_options, "app_telemetry_endpoint", options.app_telemetry_endpoint);
285+
extract_field<std::chrono::milliseconds>(
286+
pyObj_options, "app_telemetry_backoff", options.app_telemetry_backoff_interval);
287+
extract_field<std::chrono::milliseconds>(
288+
pyObj_options, "app_telemetry_ping_interval", options.app_telemetry_ping_interval);
289+
extract_field<std::chrono::milliseconds>(
290+
pyObj_options, "app_telemetry_ping_timeout", options.app_telemetry_ping_timeout);
258291

259292
// Timeout options (nested dict with durations in microseconds)
260293
PyObject* pyObj_timeout_opts = PyDict_GetItemString(pyObj_options, "timeout_options");
@@ -363,9 +396,26 @@ cluster_options_to_py(const couchbase::core::cluster_options& opts,
363396
add_bool_field(dict, "enable_metrics", opts.enable_metrics);
364397
add_bool_field(dict, "enable_tracing", opts.enable_tracing);
365398
add_bool_field(dict, "enable_lazy_connections", opts.enable_lazy_connections);
399+
add_bool_field(dict, "show_queries", opts.show_queries);
400+
add_bool_field(dict, "enable_unordered_execution", opts.enable_unordered_execution);
401+
add_bool_field(dict, "enable_clustermap_notification", opts.enable_clustermap_notification);
402+
add_bool_field(dict, "allow_enterprise_analytics", opts.allow_enterprise_analytics);
403+
add_bool_field(dict, "dump_configuration", opts.dump_configuration);
404+
add_bool_field(dict, "enable_app_telemetry", opts.enable_app_telemetry);
366405

367406
// Other
368407
add_field<std::chrono::milliseconds>(dict, "config_poll_interval", opts.config_poll_interval);
408+
add_field<std::chrono::milliseconds>(dict, "config_poll_floor", opts.config_poll_floor);
409+
add_string_field_if_not_empty(dict, "user_agent_extra", opts.user_agent_extra);
410+
add_field<std::size_t>(dict, "max_http_connections", opts.max_http_connections);
411+
add_string_field_if_not_empty(dict, "preferred_server_group", opts.server_group);
412+
add_string_field_if_not_empty(dict, "app_telemetry_endpoint", opts.app_telemetry_endpoint);
413+
add_field<std::chrono::milliseconds>(
414+
dict, "app_telemetry_backoff", opts.app_telemetry_backoff_interval);
415+
add_field<std::chrono::milliseconds>(
416+
dict, "app_telemetry_ping_interval", opts.app_telemetry_ping_interval);
417+
add_field<std::chrono::milliseconds>(
418+
dict, "app_telemetry_ping_timeout", opts.app_telemetry_ping_timeout);
369419

370420
// Credentials
371421
PyObject* creds_dict = cbpp_to_py(creds);

0 commit comments

Comments
 (0)