[CXX-2381] Improve generic server errors returned to the client Created: 05/Oct/21  Updated: 27/Mar/23  Resolved: 20/May/22

Status: Closed
Project: C++ Driver
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Unknown
Reporter: Alex Bevilacqua Assignee: Roberto Sanchez
Resolution: Won't Do Votes: 2
Labels: needs-first-responder
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Case:

 Description   

This request is specifically for server-side connection limits being reached and not communicated back to the client application, however other server-side failures could be better reported to clients.

For example, after adapting the connection pooling example as follows:

// file: test.cpp
// build: c++ --std=c++11 test.cpp -o test $(pkg-config --cflags --libs libmongocxx)  -lpthread
#include <iostream>
#include <ostream>
#include <sstream>
#include <thread>
#include <vector>
 
#include <bsoncxx/builder/basic/document.hpp>
#include <bsoncxx/builder/basic/kvp.hpp>
#include <bsoncxx/json.hpp>
#include <bsoncxx/types.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/pool.hpp>
#include <mongocxx/uri.hpp>
 
using bsoncxx::builder::basic::kvp;
 
int main() {
    // The mongocxx::instance constructor and destructor initialize and shut down the driver,
    // respectively. Therefore, a mongocxx::instance must be created before using the driver and
    // must remain alive for as long as the driver is in use.
    mongocxx::instance inst{};
    mongocxx::uri uri{"mongodb://localhost:27017/?minPoolSize=5&maxPoolSize=5"};
 
    mongocxx::pool pool{uri};
 
    std::vector<std::string> collection_names = {"foo", "bar", "baz"};
    std::vector<std::thread> threads{};
 
    for (auto i : {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) {
        auto run = [&](std::int64_t j) {
            // Each client and collection can only be used in a single thread.
            auto client = pool.acquire();
            auto coll = (*client)["test"][collection_names[static_cast<std::size_t>(j)]];
            coll.delete_many({});
 
            bsoncxx::types::b_int64 index = {j};
            coll.insert_one(bsoncxx::builder::basic::make_document(kvp("x", index)));
 
            if (auto doc =
                    (*client)["test"][collection_names[static_cast<std::size_t>(j)]].find_one({})) {
                // In order to ensure that the newline is printed immediately after the document,
                // they need to be streamed to std::cout as a single string.
                std::stringstream ss;
                ss << bsoncxx::to_json(*doc) << std::endl;
 
                std::cout << ss.str();
            }
 
            // The client goes out of scope at the end of the lambda and is returned to the pool.
        };
 
        std::thread runner{run, i};
 
        threads.push_back(std::move(runner));
    }
 
    for (auto&& runner : threads) {
        runner.join();
    }
}

And starting up a mongod with a hard limit of 5 simultaneous connections :

m 4.4.9-ent
rm -rf data && mkdir data 
mongod --dbpath data --maxConns 5

The application will fail with:

terminate called after throwing an instance of 'mongocxx::v_noabi::bulk_write_exception'
  what():  Failed to send "isMaster" command with database "admin": socket error or timeout: generic server error
Aborted (core dumped)

The error message is not obviously actionable, though the mongod log in this case contains details that identify the cause of the failure:

{"t":{"$date":"2021-10-05T10:59:55.937-04:00"},"s":"I","c":"NETWORK","id":51800,"ctx":"conn24","msg":"client metadata","attr":{"remote":"127.0.0.1:36876","client":"conn24","doc":{"driver":{"name":"mongoc / mongocxx","version":"1.17.4 / 3.6.2"},"os":{"type":"Linux","name":"Linux Mint","version":"20.2","architecture":"x86_64"},"platform":"(null)cfg=0x02a15ea0e9 posix=200809 stdc=201710 CC=GCC 9.3.0 CFLAGS=\"\" LDFLAGS=\"\""}}}

{"t":{"$date":"2021-10-05T10:59:55.937-04:00"},"s":"I","c":"NETWORK","id":22942,"ctx":"listener","msg":"Connection refused because there are too many open connections","attr":{"connectionCount":6}}



 Comments   
Comment by Alex Bevilacqua [ 01/Jun/22 ]

roberto.sanchez@mongodb.com I think your conclusion is acceptable. It makes sense that the server can log the reason for rejecting the connection attempt, and since the connection is never successfully opened that reason can't be communicated "over the wire".

Comment by Roberto Sanchez [ 20/May/22 ]

alex.bevilacqua@mongodb.com, it looks like it will not be possible for us to implement the requested improvement. I was able to reproduce the error per your report and with that I was able to determine that this error is being generated in a C driver function which is called by the C++ driver. At that point I was able to investigate what error information might be available at that point in the C driver's interaction with the server that could serve the purpose you requested. Because of the early stage at which this failure is occurring, there is very little in the way of error information available to the driver.

I discussed this with the C/C++ driver team members and after a bit of brainstorming we came up empty. Unfortunately, the driver does not have access to the error information from the server log. The connection is attempted and because it is rejected without a connection ever being established there is no way to communicate to that thread the reason for the connection error. To the driver thread it simply appears as though there is some unspecified error. Accepting the connection in order to communicate the server-side error message does not seem like a feasible solution. Additionally, it is not something that can be controlled from the driver side.

I am closing this ticket as this is not something which is possible for us to do. If you'd like to suggest alternatives that might be suitable from the driver side, I recommend that you speak with kevin.albertson@mongodb.com.

Comment by Alex Bevilacqua [ 05/Oct/21 ]

I'm guessing that the "informational" nature of the error in this case is preventing it from being passed back to the client application/driver.

Generated at Wed Feb 07 22:05:45 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.