From 90832a9610ec9e6ecb369013e6c75055c33dce55 Mon Sep 17 00:00:00 2001 From: Matej Kenda Date: Tue, 2 Dec 2025 13:20:59 +0100 Subject: [PATCH] enh(MongoDB): Add read-preference validation to replica set connection and pool. --- .../Poco/MongoDB/ReplicaSetConnection.h | 6 ++++ .../ReplicaSetPoolableConnectionFactory.h | 6 ++-- MongoDB/src/ReplicaSetConnection.cpp | 36 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/MongoDB/include/Poco/MongoDB/ReplicaSetConnection.h b/MongoDB/include/Poco/MongoDB/ReplicaSetConnection.h index 3017e0e46..c6d08ba48 100644 --- a/MongoDB/include/Poco/MongoDB/ReplicaSetConnection.h +++ b/MongoDB/include/Poco/MongoDB/ReplicaSetConnection.h @@ -100,6 +100,12 @@ public: [[nodiscard]] bool isConnected() const; /// Returns true if currently connected to a server. + [[nodiscard]] bool matchesReadPreference() const; + /// Returns true if the currently connected server still matches the read preference. + /// Returns false if not connected or if the server no longer satisfies the read preference. + /// This is useful for connection pool validation to detect when a server role has changed + /// (e.g., primary became secondary). + private: void ensureConnection(); /// Ensures we have an active connection, creating one if needed. diff --git a/MongoDB/include/Poco/MongoDB/ReplicaSetPoolableConnectionFactory.h b/MongoDB/include/Poco/MongoDB/ReplicaSetPoolableConnectionFactory.h index 8d736538b..47ab3d8e0 100644 --- a/MongoDB/include/Poco/MongoDB/ReplicaSetPoolableConnectionFactory.h +++ b/MongoDB/include/Poco/MongoDB/ReplicaSetPoolableConnectionFactory.h @@ -62,8 +62,10 @@ public: bool validateObject(MongoDB::ReplicaSetConnection::Ptr pObject) { - // Check if the connection is still valid - return pObject->isConnected(); + // Check if the connection is still valid and matches the read preference. + // This ensures that if a server changes role (e.g., primary becomes secondary), + // the cached connection is invalidated and a new one is created. + return pObject->isConnected() && pObject->matchesReadPreference(); } void activateObject(MongoDB::ReplicaSetConnection::Ptr pObject) diff --git a/MongoDB/src/ReplicaSetConnection.cpp b/MongoDB/src/ReplicaSetConnection.cpp index 046e5c781..8b4c85dc6 100644 --- a/MongoDB/src/ReplicaSetConnection.cpp +++ b/MongoDB/src/ReplicaSetConnection.cpp @@ -116,6 +116,42 @@ bool ReplicaSetConnection::isConnected() const } +bool ReplicaSetConnection::matchesReadPreference() const +{ + if (!isConnected()) + { + return false; + } + + // Get the current topology + TopologyDescription topology = _replicaSet.topology(); + + // Get the server description for the currently connected server + ServerDescription server = topology.getServer(_connection->address()); + + // Check if the server is Unknown or has an error + if (server.type() == ServerDescription::Unknown || server.hasError()) + { + return false; + } + + // Use ReadPreference::selectServers to check if our current server + // would be selected with the current read preference + std::vector eligibleServers = _readPreference.selectServers(topology); + + // Check if our current server is in the list of eligible servers + for (const auto& eligible : eligibleServers) + { + if (eligible.address() == _connection->address()) + { + return true; + } + } + + return false; +} + + void ReplicaSetConnection::ensureConnection() { if (_connection.isNull())