@@ -139,7 +139,9 @@ mg_memory::MgSessionPtr MakeRoutedBoltSession(const Config &config, std::chrono:
139139 return mg_memory::MakeCustomUnique<mg_session>(nullptr );
140140 }
141141 mg_map_insert (routing.get (), " address" , mg_value_make_string (coord_address.c_str ()));
142- mg_map_insert (extra.get (), " db" , mg_value_make_string (config.db .c_str ()));
142+ if (!config.db .empty ()) {
143+ mg_map_insert (extra.get (), " db" , mg_value_make_string (config.db .c_str ()));
144+ }
143145
144146 mg_map *rt_raw = nullptr ;
145147 const int status = mg_session_route (coord.get (), routing.get (), nullptr , extra.get (), &rt_raw);
@@ -152,7 +154,7 @@ mg_memory::MgSessionPtr MakeRoutedBoltSession(const Config &config, std::chrono:
152154
153155 // 3. Parse the routing table: TTL (seconds) and the WRITE (main) instance.
154156 const mg_value *ttl_val = mg_map_at (rt.get (), " ttl" );
155- if (ttl_val != nullptr && expiry_out != nullptr ) {
157+ if (ttl_val != nullptr && mg_value_get_type (ttl_val) == MG_VALUE_TYPE_INTEGER && expiry_out != nullptr ) {
156158 *expiry_out = std::chrono::steady_clock::now () + std::chrono::seconds (mg_value_integer (ttl_val));
157159 }
158160
@@ -224,10 +226,18 @@ void RoutedSession::Rebuild() {
224226}
225227
226228mg_session *RoutedSession::Get () {
227- if (routed_ && std::chrono::steady_clock::now () >= expiry_) {
228- // Proactive TTL re-route: the previous routing table has expired, fetch a
229- // fresh one (and possibly a new main) before handing back the session.
230- Rebuild ();
229+ // Proactive TTL re-route: once the previous routing table has expired, fetch a fresh one (and possibly a new
230+ // main) before handing back the session. Suppressed while an explicit transaction is open (re-routing would
231+ // silently drop it) and made non-destructive: a failed re-route keeps the existing working session rather than
232+ // discarding a healthy connection over a transient coordinator hiccup.
233+ if (routed_ && !in_transaction_ && std::chrono::steady_clock::now () >= expiry_) {
234+ auto refreshed = MakeRoutedBoltSession (config_, &expiry_);
235+ if (refreshed.get () != nullptr || session_.get () == nullptr ) {
236+ session_ = std::move (refreshed);
237+ } else {
238+ // Keep the still-usable session; back off so we retry periodically instead of on every call.
239+ expiry_ = std::chrono::steady_clock::now () + kRerouteRetryBackoffSec ;
240+ }
231241 }
232242 return session_.get ();
233243}
@@ -236,4 +246,15 @@ void RoutedSession::Reconnect() { Rebuild(); }
236246
237247bool RoutedSession::Connected () const { return session_.get () != nullptr ; }
238248
249+ void RoutedSession::ObserveQuery (const std::string &query) {
250+ // Track explicit-transaction state from the transaction-control keyword so proactive re-routing can avoid
251+ // tearing down an open transaction. Best-effort: matches the leading keyword of the trimmed query.
252+ const auto upper = utils::ToUpperCase (utils::Trim (query));
253+ if (upper.rfind (" BEGIN" , 0 ) == 0 ) {
254+ in_transaction_ = true ;
255+ } else if (upper.rfind (" COMMIT" , 0 ) == 0 || upper.rfind (" ROLLBACK" , 0 ) == 0 ) {
256+ in_transaction_ = false ;
257+ }
258+ }
259+
239260} // namespace utils::bolt
0 commit comments