vrogier / ocilib

OCILIB (C and C++ Drivers for Oracle) - Open source C and C++ library for accessing Oracle databases
http://www.ocilib.net
Apache License 2.0
322 stars 116 forks source link

Asynchronous execution #148

Closed ghost closed 6 years ago

ghost commented 6 years ago

Hello,

have you considered adding asynchronous feature to the library? Currently when I run an event loop, long-running calls to Oracle block it completely.

vrogier commented 6 years ago

Hi,

When I've started the development of OCILIB about 10 years ago as a high level API, I had to make a choice between using OCI synchronously or asynchronously (as both mode are supported, with some limitations to the asynchronous mode). I choose to not implement asynchronous mode for 2 reasons:

I do not have plans for supporting it in the future.

Best regards,

Vincent

bangusi commented 6 years ago

@brano543 Run ocilib calls inside a separate thread. That is what I do when using ocilib in a GUI app for example.

ghost commented 6 years ago

@bangusi Could you provide an example on how exactly you do it, so you continue your event loop and then send the response back? I am using nghttp2_asio example and basically my problem is that I don't know how to put it as background job.

This is basically what I am doing now. Unfortunately the library is too complex for me to understand what it is exactly being done under the hood, because every time I try to print some status at end of get_report function, it almost instantly exits the function, so I don't know what is blocking the socket. Maybe it waits for res.end() to be called to continue. Could you advise?

void get_report(const request &req, const response &res)
{
    //get the response body
    vector<uint8_t> *msgpack_v = new vector<uint8_t>();
        //this callback gets called every time a chunk of data is recieved
    req.on_data([&res, msgpack_v](const uint8_t *data, std::size_t len) {
        if (len > 0)
        {
            std::copy(data, data + len, std::back_inserter(*msgpack_v));
        }
        else if (len == 0)
        {
            //set the response headers correctly
            header_map headers;
            headers.emplace("content-type", header_value{"application/octet-stream", 0}); 
            headers.emplace("access-control-allow-origin", header_value{"*", 0});        
                //set temporary download dir
                boost::filesystem::path input_path(boost::filesystem::current_path());
                boost::filesystem::path folder("/download");
                boost::filesystem::path download_folder = input_path / folder;
                string destination_directory = download_folder.string();

                //first we need to get the report name
                json object = json::from_msgpack(*msgpack_v);
                string report_name = object["name"].get<string>();
                vector<json> data = object["data"];

                //cleanup memory
                delete msgpack_v;

                                Validator validator = Validator();
                               unordered_map<string, string> validation_schema;
                               validation_schema["Receipt Required Flag"] = "(Y|N)";
                                unordered_map<string, string> parameters = validator.validate_form(data, validation_schema);

                                //blocking call
                               vector<vector<string>> results =get_report(destination_directory, parameters["Receipt Required Flag"]);
}});
}
bangusi commented 6 years ago

@brano543 I have never used boost::asio apart from cursory glance but here is how I typically make sql calls when I want asynchronous behaviour

void get_report(...)
{
    // execute your sql (calls to ocilib )
    // catch any exeptions
    // post results to the event queue of your main thread
}

// start a thread, pass parameters: note that std::thread will make copies so is you want to get the result of your sql back make sure you passs in-parametere such as a std::shared_ptr or plain pointer

std::thread th(get_report, destination_directory, parameters["Receipt Required Flag"]);
th.detach();
ghost commented 6 years ago

@bangusi Thank you for your advice, this was the first thing I have tried and helped me. However, in order to make it work with the boost::asio::io_service, I had to pass it into the thread, so I ended up with doing it like i describe here https://github.com/nghttp2/nghttp2/issues/1116.