Search code examples
jbosswildflymybatisapplication-server

How application server handle multiple requests to save data into table


I have created a web application in jsf and it has a button. If the button is clicked then it will go to the server side and execute the below function to save the data in a table and I am using mybatis for this.

public void save(A a)
{

SqlSession session = null; 

try{ 

session = SqlConnection.getInstance().openSession(); 

TestMapper testmap= session.getMapper(TestMapper.class); 

testmap.insert(a); 

session .commit(); 

}
catch(Exception e){
}
finally{
session.close();
}

}

Now i have deployed this application in an application server JBoss(wildfly).

As per my understanding, when multiple users try to access the application by hitting the URL, the application server creates thread for each of the user request. For example if 4 clients make request then 4 threads will be generated that is t1,t2,t3 and t4.

If all the 4 users hit the save button at the same time, how save method will be executed, like if t1 access the method and execute insert statement to insert data into table, then t2,t3 and t4 or simultaneously all the 4 threads will execute the insert method and insert data?


Solution

  • To bring some context I would describe first two possible approaches to handling requests. In this case HTTP but these approaches do not depend on the protocol used and the main important thing is that requests come from the network and for their execution some IO is needed (either access to filesystem or database or network calls to other systems). Note that the following description has some simplifications.

    These two approaches are:

    • synchronous
    • asynchronous

    In general to process the typical HTTP request that involves DB access at least four IO operations are needed:

    • request handler needs to read the request data from the client socket
    • request handler needs to write request to the socket connected to the DB
    • request handler needs to read response from the DB socket
    • request handler needs to write the response to the client socket

    Let's see how this is done for both cases.

    Synchronous

    In this approach the server has a pool (think a collection) of threads that are ready to serve a request.

    When the request comes in the server borrows a thread from the pool and executes a request handler in that thread.

    When the request handler needs to do the IO operation it initiates the IO operation and then waits for its completion. By wait I mean that thread execution is blocked until the IO operation completes and the data (for example response with the results of the SQL query) is available.

    In this case concurrency that is requests processing for multiple clients simultaneously is achieved by having some number of threads in the pool. IO operations are much slower if compared to CPU so most of the time the thread processing some request is blocked on IO operation and CPU cores can execute stages of the request processing for other clients.

    Note that because of the slowness of the IO operations thread pool used for handling HTTP requests is usually large enough. Documentation for sync requests processing subsystem used in wildfly says about 10 threads per CPU core as a reasonable value.

    Asynchronous

    In this case the IO is handled differently. There is a small number of threads handling IO. They all work the same way and I'll describe one of them.

    Such thread runs a loop which basically waits for events and every time an event happen it calls a handler for an event.

    The first such event is new request. When a request processing is started the request handler is invoked from the loop that is run by one of the IO threads. The first thing the request handler is doing it tries to read request from the client socket. So the handler initiates the IO operation on the client socket and returns control to the caller. That means that the thread is released and it can process another event.

    Another event happens when the IO operations that reads from client socket got some data available. In this case the loop invokes the handler at the point where the handler returned the control to the loop after the IO initiate namely it is resumed on the next step that processes the input data (like parses HTTP parameters) and initiates new IO operation (in this case request to the DB socket). And again the handler releases the thread so it can handler other events (like completion of IO operations that are part of other clients' requests processing).

    Given that IO operations are slow compared to the speed of CPU itself one thread handling IO can process a lot of requests concurrently.

    Note: that it is important that the requests handler code never uses any blocking operation (like blocking IO) because that would steal the IO thread and will not allow other requests to proceed.

    JSF and Mybatis

    In case of JSF and mybatis the synchronous approach is used. JSF uses a servlet to handle requests from the UI and servlets are handled by the synchronous processors in WildFly. JDBC which is used by mybatis to communicate to a DB is also using synchronous IO so threads are used to execute requests concurrently.

    Congestions

    All of the above is written with the assumption that there is no other sources of the congestion. By congestion here I mean a limitation on the ability of the certain component of the system to execute things in parallel.

    For example imagine a situation that a database is configured to only allow one client connection at a time (this is not a reasonable configuration and I'm using this only to demonstrate the idea). In this case even if multiple threads can execute the code of the save method in parallel all but one will be blocked at the moment when they try to open the connection to the database.

    Another similar example is if you are using sqlite database. It only allows one client to write to the DB at a time. So at the point when thread A tries to execute insert it will be blocked if the is another thread B that is already executing the insert. And only after the commit executed by the thread B the thread A would be able to proceed with the insert. The time A depends on the time it take for B to execute its request and the number of other threads waiting to do a write operation to the same DB.

    In practice if you are using a RDBMS that scales better (like postgresql, mysql or oracle) you will not hit this problem when using the small number of connection. But it may become a problem when there is a big number of concurrent requests and there is a limitation in the DB on the number of client connections or the connection pool is used to limit the number of connections on the application side. In this case if there are already many connections to the database the new clients will wait until existing requests are finished and connections are closed.