Java 2 Ada - Tag Java2018-11-17T14:02:00+00:00Stephane Carrezurn:md5:d12e23c53b2436d6becce3d51ddbdf38AWAAda, Java and Python database accessurn:md5:261a4c0a5451f48410ed3bd704b3c9c02018-11-17T14:02:00+00:002018-11-17T14:02:00+00:00Stephane CarrezAdaperformanceJavaPythonMysqlSQLitePostgreSQL
<div class="post-text"><p>The database also has a serious impact on such benchmark and I've measured the following three famous databases:</p><ul><li><a href="https://www.sqlite.org/">SQLite</a></li><li><a href="https://mariadb.org/">MariaDB</a></li><li><a href="https://www.postgresql.org/">PostgreSQL</a></li></ul><p>The purpose of the benchmark is to be able to have a simple comparison between these different databases and different programming languages. For this, a very simple database table is created with only two integer columns one of them being the primary key with auto increment. For example the <a href="https://www.sqlite.org/">SQLite</a> table is created with the following SQL:</p><pre><code>CREATE table test_simple (
id INTEGER PRIMARY KEY AUTOINCREMENT,
value INTEGER
)
</code></pre><p>The database table is filled with a simple <code>INSERT</code> statement which is also benchmarked. The goal is not to demonstrate and show the faster insert method, nor the faster query for a given database or language.</p><h3>Benchmark</h3><p>The SQL benchmarks are simple and they are implemented in the same way for each language so that we can get a rough comparison between languages for a given database. The <code>SELECT</code> query retrieves all the database table rows but it includes a <code>LIMIT</code> to restrict the number of rows returned. The query is executed with different values for the limit so that a simple graph can be drawn. For each database, the SQL query looks like:</p><pre><code>SELECT * FROM test_simple LIMIT 10
</code></pre><p>The SQL statements are executed 10000 times for <code>SELECT</code> queries, 1000 times for <code>INSERT</code> and 100 times for <code>DROP</code>/<code>CREATE</code> statements.</p><p>Each SQL benchmark program generates an XML file that contains the results as well as resource statistics taken from the <code>/proc/self/stat</code> file. An Ada tool is provided to gather the results, prepare the data for plotting and produce an Excel file with the results.</p><h5>Python code</h5><pre><code>
def execute(self):
self.sql = "SELECT * FROM test_simple LIMIT " + str(self.expect_count)
repeat = self.repeat()
db = self.connection()
stmt = db.cursor()
for i in range(0, repeat):
stmt.execute(self.sql)
row_count = 0
for row in stmt:
row_count = row_count + 1
if row_count != self.expect_count:
raise Exception('Invalid result count:' + str(row_count))
stmt.close()
</code></pre><h5>Java code</h5><pre><code>public void execute() throws SQLException {
PreparedStatement stmt
= mConnection.prepareStatement("SELECT * FROM test_simple LIMIT " + mExpectCount);
for (int i = 0; i < mRepeat; i++) {
if (stmt.execute()) {
ResultSet rs = stmt.getResultSet();
int count = 0;
while (rs.next()) {
count++;
}
rs.close();
if (count != mExpectCount) {
throw new SQLException("Invalid result count: " + count);
}
} else {
throw new SQLException("No result");
}
}
stmt.close();
}
</code></pre><h5>Ada code</h5><pre><code>procedure Select_Table_N (Context : in out Context_Type) is
DB : constant ADO.Sessions.Master_Session := Context.Get_Session;
Count : Natural;
Stmt : ADO.Statements.Query_Statement
:= DB.Create_Statement ("SELECT * FROM test_simple LIMIT " & Positive'Image (LIMIT));
begin
for I in 1 .. Context.Repeat loop
Stmt.Execute;
Count := 0;
while Stmt.Has_Elements loop
Count := Count + 1;
Stmt.Next;
end loop;
if Count /= LIMIT then
raise Benchmark_Error with "Invalid result count:" & Natural'Image (Count);
end if;
end loop;
end Select_Table_N;
</code></pre><p>The benchmark were executed on an Intel i7-3770S CPU @3.10Ghz with 8-cores running Ubuntu 16.04 64-bits. The following database versions are used:</p><ul><li>MariaDB 10.0.36</li><li>PostgreSQL 9.5.14</li></ul><h3>Resource usage comparison</h3><p>The first point to note is the fact that both Python and Ada require only one thread to run the SQL benchmark. On its side, the Java VM and database drivers need 20 threads to run.</p><p>The second point is not surprising: Java needs 1000% more memory than Ada and Python uses 59% more memory than Ada. What is measured is the the VM RSS size which means this is really the memory that is physically mapped at a given time.</p><p>The SQLite database requires less resource than others. The result below don't take into account the resource used by the MariaDB and PostgreSQL servers. At that time, the MariaDB server was using 125Mb and the PostgreSQL server was using 31Mb.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/sql-memory.png" longdesc="Memory usage" alt="sql-memory.png"></img></div></div></p><h3>Speed comparison</h3><p>Looking at the CPU time used to run the benchmark, Ada appears as a clear winner. The Java PostgreSQL driver appears to be very slow at connecting and disconnecting to the database, and this is the main reason why it is slower than others.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/sql-time.png" longdesc="CPU time" alt="sql-time.png"></img></div></div></p><p>It is interesting to note however that both Java and Python provide very good performance results with SQLite database when the number of rows returned by the query is less than 100. With more than 500 rows, Ada becomes faster than others.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/sql-sqlite.png" longdesc="SQLite benchmark" alt="sql-sqlite.png"></img></div></div></p><p>With a PostgreSQL database, Ada is always faster even with small result sets.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/sql-postgresql.png" longdesc="PostgreSQL benchmark" alt="sql-postgresql.png"></img></div></div></p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/sql-mysql.png" longdesc="MySQL benchmark" alt="sql-mysql.png"></img></div></div></p><h3>Conclusion and references</h3><p>SQLite as an embedded database is used on more than 1 billion of devices as it is included in all smartphones (Android, iOS). It provides very good performances for small databases.</p><p>With client-server model, MariaDB and PostgreSQL are suffering a little when compared to SQLite.</p><p>For bigger databases, Ada provides the best performance and furthermore it appears to be more predictable that other languages (ie, linear curves).</p><p>The Excel result file is available in: <a href="https://github.com/stcarrez/sql-benchmark/raw/master/sql-benchmark-results.xls">sql-benchmark-results.xls</a></p><p>Sources of the benchmarks are available in the following GitHub repository:</p><ul><li><a href="https://github.com/stcarrez/sql-benchmark">https://github.com/stcarrez/sql-benchmark</a></li></ul></div> Rest API Benchmark comparison between Ada and Javaurn:md5:84721d78f2ba1c62a10feb2c0e4294b32017-03-21T22:55:00+00:002017-03-21T22:55:00+00:00Stephane CarrezAdaperformanceJava
<div class="post-text"><p>The goal is to benchmark the following servers and have an idea of how they compare with each others:</p><ul><li><a href="https://github.com/AdaCore/aws">Ada Web Server</a></li><li><a href="https://github.com/stcarrez/ada-asf">Ada Server Faces</a></li><li><a href="http://embed-web-srvr.sourceforge.net/">Embedded Web Server</a></li><li><a href="https://grizzly.java.net/">Java Grizzly</a></li></ul><p>The first three are implemented in Ada and the last one in Java.</p><h3>REST Server Implementation</h3><p>The implementation is different for each server but they all implement the same REST GET operation accessible from the <code>/api</code> base URL. They return the same JSON content:</p><pre><code class="lang-json">{"greeting":"Hello World!"}
</code></pre><p>Below is an extract of the server implementation for each server.</p><h4>AWS Rest API Server</h4><pre><code class="lang-ada">function Get_Api (Request : in AWS.Status.Data) return AWS.Response.Data is
begin
return AWS.Response.Build ("application/json", "{""greeting"":""Hello World!""}");
end Get_Api;
</code></pre><h4>ASF Rest API Server</h4><pre><code class="lang-ada">procedure Get (Req : in out ASF.Rest.Request'Class;
Reply : in out ASF.Rest.Response'Class;
Stream : in out ASF.Rest.Output_Stream'Class) is
begin
Stream.Start_Document;
Stream.Write_Entity ("greeting", "Hello World!");
Stream.End_Document;
end Get;
</code></pre><h4>EWS Rest API Server</h4><pre><code class="lang-ada">function Get (Request : EWS.HTTP.Request_P) return EWS.Dynamic.Dynamic_Response'Class is
Result : EWS.Dynamic.Dynamic_Response (Request);
begin
EWS.Dynamic.Set_Content_Type (Result, To => EWS.Types.JSON);
EWS.Dynamic.Set_Content (Result, "{""greeting"":""Hello World!""}");
return Result;
end Get;
</code></pre><h4>Java Rest API Server</h4><pre><code class="lang-java">@Produces(APPLICATION_JSON_UTF8_VALUE)
@Path("/api")
@Component
public class ApiResource {
public static final String RESPONSE = "{\"greeting\":\"Hello World!\"}";
@GET
public Response test() {
return ok(RESPONSE).build();
}
}
</code></pre><h3>Benchmark Strategy and Results</h3><p>The Ada and Java servers are started on the same host (one at a time), a Linux Ubuntu 14.04 64-bit powered by an Intel i7-3770S CPU @3.10Ghz with 8-cores. The benchmark is made by using <a href="https://www.joedog.org/">Siege</a> executed on a second computer running Linux Ubuntu 15.04 64-bit powered by an Intel i7-4720HQ CPU @2.60Ghz with 8-cores. Client and server hosts are connected through a Gigabit Ethernet link.</p><p><a href="https://www.joedog.org/">Siege</a> makes an intensive use of network connections which results in exhaustion of TCP/IP port to connect to the server. This is due to the TCP TIME_WAIT that prevents the TCP/IP port from being re-used for future connections. To avoid such exhaustion, the network stack is tuned on both the server and the client hosts with the <code>sysctl</code> commands:</p><pre><code>sudo sysctl -w net.ipv4.tcp_tw_recycle=1
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
</code></pre><p>The benchmark tests are executed by running the <a href="https://github.com/stcarrez/ada-rest-api-server-benchmark/blob/master/run-load-test.sh">run-load-test.sh</a> script and then making GNUplot graphs using <a href="https://github.com/stcarrez/ada-rest-api-server-benchmark/blob/master/plot-perf.gpi">plot-perf.gpi</a> script. The benchmark gives the number of REST requests which are made per second for different level of concurrency.</p><ul><li>The <a href="http://embed-web-srvr.sourceforge.net/">Embedded Web Server</a> targets embedded platforms and it uses only one task to serve requests. Despite this simple configuration, it gets some honorable results as it reaches 8000 requests per second.</li><li>The <a href="https://github.com/stcarrez/ada-asf">Ada Server Faces</a> provides an Ada implementation of Java Server Faces. It uses the <a href="https://github.com/AdaCore/aws">Ada Web Server</a>. The benchmark shows a small overhead (arround 4%).</li><li>The <a href="https://github.com/AdaCore/aws">Ada Web Server</a> is the fastest server in this configuration. As for the <a href="https://github.com/stcarrez/ada-asf">Ada Server Faces</a> it is configured to only have 8 tasks that serve requests. Increasing the number of tasks does not bring better performance.</li><li>The <a href="https://grizzly.java.net/">Java Grizzly</a> server is the faster Java server reported by Arcadius's benchmark. It uses 62 threads. It appears to serve 7% less requests than the <a href="https://github.com/AdaCore/aws">Ada Web Server</a>.</li></ul><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/ada-rest-api-benchmark.png" longdesc="REST API Benchmark" alt="ada-rest-api-benchmark.png"></img></div></div></p><p>On the memory side, the process Resident Set Size (RSS) is measured once the benchmark test ends and graphed below. The <a href="https://grizzly.java.net/">Java Grizzly</a> server uses arround 580 Mb, followed by <a href="https://github.com/stcarrez/ada-asf">Ada Server Faces</a> that uses 5.6Mb, <a href="https://github.com/AdaCore/aws">Ada Web Server</a> 3.6Mb and the <a href="http://embed-web-srvr.sourceforge.net/">EWS</a> only 1 Mb.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/ada-rest-api-memory.png" longdesc="Server Memory Usage" alt="ada-rest-api-memory.png"></img></div></div></p><h3>Conclusion and References</h3><p>The <a href="https://github.com/AdaCore/aws">Ada Web Server</a> has comparable performance with the <a href="https://grizzly.java.net/">Java Grizzly</a> server (it is even a little bit faster). But as far a memory is concerned, Ada has a serious advantage since it cuts the memory size by a factor of 100. Ada has other <a href="http://www.adaic.org/advantages/features-benefits/">advantages</a> that make it an alternative choice for web development (safety, security, realtime capabilities, ...).</p><p>Sources of the benchmarks are available in the following two GitHub repositories:</p><ul><li><a href="https://github.com/stcarrez/ada-rest-api-server-benchmark">https://github.com/stcarrez/ada-rest-api-server-benchmark</a></li><li><a href="https://github.com/arcadius/java-rest-api-web-container-benchmark">https://github.com/arcadius/java-rest-api-web-container-benchmark</a></li></ul></div> Process creation in Java and Adaurn:md5:47e5bac2c4b025b8036317e316d097612012-03-16T22:18:07+00:002012-03-16T22:18:07+00:00Stephane CarrezAdaJavaTutorialpipeprocess
<div class="post-text"><p>When developping and integrating applications together it is often useful to launch an external program. By doing this, many integration and implementation details are simplified (this integration technic also avoids license issues for some open source software integration).</p><p>This article explains how to launch an external program in Java and Ada and be able to read the process output to get the result.</p><h2>Java Process Creation</h2><p>The process creation is managed by the <code>ProcessBuilder</code> Java class. An instance of this class holds the necessary information to create and launch a new process. This includes the command line and its arguments, the standard input and outputs, the environment variables and the working directory.</p><p>The process builder instance is created by specifying the command and its arguments. For this the constructor accepts a variable list of parameters of type <code>String</code>.</p><pre><code class="lang-java">import java.lang.ProcessBuilder;
...
final String cmd = "ls";
final String arg1 = "-l";
final ProcessBuilder pb = new ProcessBuilder(cmd, arg1);
</code></pre><p>When the process builder is initialized, we can invoke the <code>start</code> method to create a new process. Each process is then represented by an instance of the <code>Process</code> class. It is possible to invoke <code>start</code> serveral times and each call creates a new process. It is necessary to catch the <code>IOException</code> which can be raised if the process cannot be created.</p><pre><code class="lang-java">import java.lang.Process;
...
try {
final Process p = pb.start();
...
} catch (final IOException ex) {
System.err.println("IO error: " + ex.getLocalizedMessage());
}
</code></pre><p>The <code>Process</code> class gives access to the process output through an input stream represented by the <code>InputStream</code> class. With this input stream, we can read what the process writes on its output. We will use a <code>BufferedReader</code> class to read that output line by line.</p><pre><code class="lang-java">import java.io.*;
...
final InputStream is = p.getInputStream();
final BufferedReader reader = new BufferedReader(new InputStreamReader(is));
</code></pre><p>By using the <code>readLine</code> method, we can read a new line after each call. Once the whole stream is read, we have to close it. Closing the <code>BufferedReader</code> will close the <code>InputStream</code> associated with it.</p><pre><code class="lang-java"> String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
</code></pre><p>Last step is to wait for the process termination and get the exit status: we can use the <code>waitFor</code> method. Since this method can be interrupted, we have to catch the <code>InterruptedException</code>.</p><pre><code class="lang-java"> try {
...
final int exit = p.waitFor();
if (exit != 0) {
System.err.printf("Command exited with status %d\n", exit);
}
} catch (final InterruptedException ex) {
System.err.println("Launch was interrupted...");
}
</code></pre><p>You can get the complete source from the file: <a href="/images/samples/Launch.java">Launch.java</a></p><h2>Ada Process Creation</h2><p>For the Ada example, we will create an application that invokes the <code>nslookup</code> utility to resolve a set of host names. The list of host names is provided to <code>nslookup</code> by writing on its standard input and the result is collected by reading the output.</p><p>We will use the <code>Pipe_Stream</code> to launch the process, write on its input and read its output at the same time. The process is launched by calling the <code>Open</code> procedure and specifying the pipe redirection modes: <code>READ</code> is for reading the process output, <code>WRITE</code> is for writing to its input and <code>READ_WRITE</code> is for both.</p><pre><code class="lang-ada">with Util.Processes;
with Util.Streams.Pipes;
...
Pipe : aliased Util.Streams.Pipes.Pipe_Stream;
Pipe.Open ("nslookup", Util.Processes.READ_WRITE);
</code></pre><p>We can read or write on the pipe directly but using a <code>Print_Stream</code> to write the text and the <code>Buffered_Stream</code> to read the result simplifies the implementation. Both of them are connected to the pipe: the <code>Print_Stream</code> will use the pipe output stream and the <code>Buffered_Stream</code> will use the pipe input stream.</p><pre><code class="lang-ada">with Util.Streams.Buffered;
with Util.Streams.Texts;
...
Buffer : Util.Streams.Buffered.Buffered_Stream;
Print : Util.Streams.Texts.Print_Stream;
begin
-- Write on the process input stream
Buffer.Initialize (null, Pipe'Unchecked_Access, 1024);
Print.Initialize (Pipe'Unchecked_Access);
</code></pre><p>Before reading the process output, we send the input data to be solved by the process. By closing the print stream, we also close the pipe output stream, thus closing the process standard input.</p><pre><code class="lang-ada"> Print.Write ("www.google.com" & ASCII.LF);
Print.Write ("set type=NS" & ASCII.LF);
Print.Write ("www.google.com" & ASCII.LF);
Print.Write ("set type=MX" & ASCII.LF);
Print.Write ("www.google.com" & ASCII.LF);
Print.Close;
</code></pre><p>We can now read the program output by using the <code>Read</code> procedure and get the result in the <code>Content</code> string. The <code>Close</code> procedure is invoked on the pipe to close the pipe (input and output) and wait for the application termination.</p><pre><code class="lang-ada"> Content : Unbounded_String;
-- Read the 'nslookup' output.
Buffer.Read (Content);
Pipe.Close;
</code></pre><p>Once the process has terminated, we can get the exit status by using the <code>Get_Exit_Status</code> function.</p><pre><code class="lang-ada"> Ada.Text_IO.Put_Line ("Exit status: "
& Integer'Image (Pipe.Get_Exit_Status));
</code></pre><h4>References</h4><p><a href="http://code.google.com/p/ada-util/source/browse/trunk/samples/launch.adb">launch.adb</a><br><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-streams-pipes.ads">util-streams-pipes.ads</a><br><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-streams-buffered.ads">util-streams-buffered.ads</a><br><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-streams-texts.ads">util-streams-texts.ads</a></p></div> Thread safe object pool to manage scarce resource in application serversurn:md5:03da6d22b8b5beba466e5b66f76388682011-05-29T21:31:58+00:002011-05-29T21:31:58+00:00Stephane CarrezAdaJavaTutorialmultiprocessing
<div class="post-text"><h3>Problem Description</h3><p>In application servers some resources are expensive and they must be shared. This is the case for a database connection, a frame buffer used for image processing, a connection to a remote server, and so on. The problem is to make available these scarce resources in such a way that:</p><ul><li>a resource is used by only one thread at a time,</li><li>we can control the maximum number of resources used at a time,</li><li>we have some flexibility to define such maximum when configuring the application server,</li><li>and of course the final solution is thread safe.</li></ul><p>The common pattern used in such situation is to use a thread-safe <a href="http://en.wikipedia.org/wiki/Object_pool_pattern">pool of objects</a>. Objects are picked from the pool when needed and restored back to the pool when they are no longer used.</p><h4>Java thread safe object pool</h4><p>Let's see how to implement our object pool in Java. We will use a generic class declaration into which we define a fixed array of objects. The pool array is allocated by the constructor and we will assume it will never change (hence the <code>final</code> keyword).</p><pre><code class="lang-java">public class Pool<T> {
private final T[] objects;
public Pool<T>(int size) {
objects = new T[size];
}
...
}
</code></pre><p>First, we need a <code>getInstance</code> method that picks an object from the pool. The method must be thread safe and it is protected by the <code>synchronized</code> keyword. It there is no object, it has to wait until an object is available. For this, the method invokes <code>wait</code> to sleep until another thread releases an object. To keep track of the number of available objects, we will use an available counter that is decremented each time an object is used.</p><pre><code class="lang-java"> private int available = 0;
private int waiting = 0;
public synchronized T getInstance() {
while (available == 0) {
waiting++;
wait();
waiting--;
}
available--;
return objects[available];
}
</code></pre><p>To know when to wakeup a thread, we keep track of the number of waiters in the <code>waiting</code> counter. A loop is also necessary to make sure we have an available object after being wakeup. Indeed, there is no guarantee that after being notified, we have an available object to return. The call to <code>wait</code> will release the lock on the pool and puts the thread is wait mode.</p><p>Releasing the object is provided by <code>release</code>. The object is put backed in the pool array and the available counter incremented. If some threads are waiting, one of them is awaken by calling <code>notify</code>.</p><pre><code class="lang-java"> public synchronized void release(T obj) {
objects[available] = obj;
available++;
if (waiting) {
notify();
}
}
</code></pre><p>When the application is started, the pool is initialized and some pre-defined objects are inserted.</p><pre><code class="lang-java"> class Item { ... };
...
Pool<Item> pool = new Pool<Item>(10);
for (int i = 0; i < 10; i++) {
pool.release(new Item());
}
</code></pre><h4>Ada thread safe pool</h4><p>The Ada object pool will be defined in a generic package and we will use a <a href="http://www.adaic.org/resources/add_content/standards/05rm/html/RM-9-4.html">protected type</a>. The protected type will guarantee the thread safe behavior of the implementation by making sure that only one thread executes the procedures.</p><pre><code class="lang-ada">generic
type Element_Type is private;
package Util.Concurrent.Pools is
type Element_Array_Access is private;
Null_Element_Array : constant Element_Array_Access;
...
private
type Element_Array is array (Positive range <>) of Element_Type;
type Element_Array_Access is access all Element_Array;
Null_Element_Array : constant Element_Array_Access := null;
end Util.Concurrent.Pools;
</code></pre><p>The Ada protected type is simple with three procedures, we get the <code>Get_Instance</code> and <code>Release</code> as in the Java implementation. The <code>Set_Size</code> will take care of allocating the pool array (a job done by the Java pool constructor).</p><pre><code class="lang-ada">protected type Pool is
entry Get_Instance (Item : out Element_Type);
procedure Release (Item : in Element_Type);
procedure Set_Size (Capacity : in Positive);
private
Available : Natural := 0;
Elements : Element_Array_Access := Null_Element_Array;
end Pool;
</code></pre><p>First, the <code>Get_Instance</code> procedure is defined as an <code>entry</code> so that we can define a condition to enter in it. Indeed, we need at least one object in the pool. Since we keep track of the number of available objects, we will use it as the entry condition. Thanks to this entry condition, the Ada implementation is a lot easier.</p><pre><code class="lang-ada">protected body Pool is
entry Get_Instance (Item : out Element_Type) when Available > 0 is
begin
Item := Elements (Available);
Available := Available - 1;
end Get_Instance;
...
end Pool;
</code></pre><p>The <code>Release</code> operation is also easier as there is no need to wakeup any thread: the Ada runtime will do that for us.</p><pre><code class="lang-ada">protected body Pool is
procedure Release (Item : in Element_Type) is
begin
Available := Available + 1;
Elements (Available) := Item;
end Release;
end Pool;
</code></pre><p>The pool is instantiated:</p><pre><code class="lang-ada">type Connection is ...;
package Connection_Pool is new Util.Concurrent.Pools (Connection);
</code></pre><p>And a pool object can be declared and initialized with some default object:</p><pre><code class="lang-ada">P : Connection_Pool.Pool;
C : Connection;
...
P.Set_Size (Capacity => 10);
for I in 1 .. 10 loop
...
P.Release (C);
end loop;
</code></pre><h4>References</h4><p><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-concurrent-pools.ads">util-concurrent-pools.ads</a><br><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-concurrent-pools.adb">util-concurrent-pools.adb</a></p></div> Thread safe cache updates in Java and Adaurn:md5:14c441de71f23b78bd879da5bb69ccba2011-04-28T22:01:14+00:002011-04-28T22:01:14+00:00Stephane CarrezAdaJavamultiprocessingperformance
<div class="post-text"><h3>Problem Description</h3><p>The problem is to update a cache that is almost never modified and only read in multi-threaded context. The read performance is critical and the goal is to reduce the thread contention as much as possible to obtain a fast and non-blocking path when reading the cache.</p><h2>Cache Declaration</h2><h4>Java Implementation</h4><p>Let's define the cache using the <code>HashMap</code> class.</p><pre><code class="lang-java">public class Cache {
private HashMap<String,String> map = new HashMap<String, String>();
}
</code></pre><h4>Ada Implementation</h4><p>In Ada, let's instantiate the <code>Indefinite_Hashed_Maps</code> package for the cache.</p><pre><code class="lang-ada">with Ada.Strings.Hash;
with Ada.Containers.Indefinite_Hashed_Maps;
...
package Hash_Map is
new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => String,
Element_Type => String,
Hash => Hash,
"=" => "=");
Map : Hash_Map.Map;
</code></pre><h2>Solution 1: safe and concurrent implementation</h2><p>This solution is a straightforward solution using the language thread safe constructs. In Java this solution does not allow several threads to look at the cache at the same time. The cache access will be serialized. This is not a problem with Ada, where multiple concurrent readers are allowed. Only writing locks the cache object</p><h4>Java Implementation</h4><p>The thread safe implementation is protected by the synchronized keyword. It guarantees mutual exclusions of threads invoking the <code>getCache</code> and <code>addCache</code> methods.</p><pre><code class="lang-java"> public synchronized String getCache(String key) {
return map.get(key);
}
public synchronized void addCache(String key, String value) {
map.put(key, value);
}
</code></pre><h4>Ada Implementation</h4><p>In Ada, we can use the <code>protected</code> type. The cache could be declared as follows:</p><pre><code class="lang-ada"> protected type Cache is
function Get(Key : in String) return String;
procedure Put(Key, Value: in String);
private
Map : Hash_Map.Map;
end Cache;
</code></pre><p>and the implementation is straightforward:</p><pre><code class="lang-ada"> protected body Cache is
function Get(Key : in String) return String is
begin
return Map.Element (Key);
end Get;
procedure Put(Key, Value: in String) is
begin
Map.Insert (Key, Value);
end Put;
end Cache;
</code></pre><h4>Pros and Cons</h4><p>+: This implementation is thread safe.</p><p>-: In Java, thread contention is high as only one thread can look in the cache at a time.</p><p>-: In Ada, thread contention occurs only if another thread updates the cache (which is far better than Java but could be annoying for realtime performance if the <code>Put</code> operation takes time).</p><p>-: <del>Thread contention is high as only one thread can look in the cache at a time.</del></p><h2>Solution 2: weak but efficient implementation</h2><p>The Solution 1 does not allow multiple threads to access the cache at the same time, thus providing a contention point. The second solution proposed here, removes this contention point by relaxing some thread safety condition at the expense of cache behavior.</p><p>In this second solution, several threads can read the cache at the same time. The cache can be updated by one or several threads but the update does not guarantee that all entries added will be present in the cache. In other words, if two threads update the cache at the same time, the updated cache will contain only one of the new entry. This behavior can be acceptable in some cases and it may not fit for all uses. It must be used with great care.</p><h3>Java Implementation</h3><p>A cache entry can be added in a thread-safe manner using the following code:</p><pre><code class="lang-java"> private volatile HashMap<String, String> map = new HashMap<String, String>();
public String getCache(String key) {
return map.get(key);
}
public void addCache(String key, String value) {
HashMap<String, String> newMap = new HashMap<String, String>(map);
newMap.put(newKey, newValue);
map = newMap;
}
</code></pre><p>This implementation is thread safe because the hash map is never modified. If a modification is made, it is done on a separate hash map object. The new hash map is then installed by the <code>map = newMap</code> assignment operation which is atomic. Again this code extract does not guarantee that all the cache entries added will be part of the cache.</p><h3>Ada Implementation</h3><p>The Ada implementation is slightly more complex basically because there is no garbage collector. If we allocate a new hash map and update the access pointer, we still have to free the old hash map when no other thread is accessing it.</p><p>The first step is to use a reference counter to automatically release the hash table when the last thread finishes its work. The reference counter will handle memory management issues for us. An implementation of thread-safe reference counter is provided by <a href="http://code.google.com/p/ada-util">Ada Util</a>. In this implementation, counters are updated using specific instruction (See <a href="http://blog.vacs.fr/index.php?post/2011/02/01/Showing-multiprocessor-issue-when-updating-a-shared-counter">Showing multiprocessor issue when updating a shared counter</a>).</p><pre><code class="lang-ada">with Util.Refs;
...
type Cache is new Util.Refs.Ref_Entity with record
Map : Hash_Map.Map;
end record;
type Cache_Access is access all Cache;
package Cache_Ref is new Util.Refs.References (Element_Type => Cache,
Element_Access => Cache_Access);
C : Cache_Ref.Atomic_Ref;
</code></pre><p>Source: <a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-refs.ads">Util.Refs.ads</a>, <a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-refs.adb">Util.Refs.adb</a></p><p>The <code>References</code> package defines a <code>Ref</code> type representing the reference to a <code>Cache</code> instance. To be able to replace a reference by another one in an atomic manner, it is necessary to use the <code>Atomic_Ref</code> type. This is necessary because the Ada assignment of an <code>Ref</code> type is not atomic (the assignment copy and the call to the <code>Adjust</code> operation to update the reference counter are not atomic). The <code>Atomic_Ref</code> type is a protected type that provides a getter and a setter. Their use guarantees the atomicity.</p><pre><code class="lang-ada"> function Get(Key : in String) return String is
R : constant Cache_Ref.Ref := C.Get;
begin
return R.Value.Map.Element (Key); -- concurrent access
end Get;
procedure Put(Key, Value: in String) is
R : constant Cache_Ref.Ref := C.Get;
N : constant Cache_Ref.Ref := Cache_Ref.Create;
begin
N.Value.all.Map := R.Value.Map;
N.Value.all.Insert (Key, Value);
C.Set (N); -- install the new map atomically
end Put;
</code></pre><h4>Pros and Cons</h4><p>+: high performance in SMP environments<br>+: no thread contention in Java<br>-: cache update can loose some entries<br>-: still some thread contention in Ada but limited to copying a reference (C.Set)</p></div> Fault tolerant EJB interceptor: a solution to optimistic locking errors and other transient faultsurn:md5:8086d00efd517532d156d67ba2362edb2010-09-02T07:16:58+00:002010-09-02T07:16:58+00:00Stephane CarrezHibernateJ2EEJavaMysqlejbhigh availability
<div class="post-text"><p>Fault tolerance is often necessary in application servers. The J2EE standard defines an interceptor mechanism that can be used to implement the first steps for fault tolerance. The pattern that I present in this article is the solution that I have implemented for the <a href="http://www.planzone.com">Planzone service</a> and which is used with success for the last two years.</p><h2>Identify the Fault to recover</h2><p>The first step is to identify the faults that can be recovered from others. Our application is using MySQL and Hibernate and we have identified the following three transient faults (or recoverable faults).</p><h5>StaleObjectStateException (Optimistic Locking)</h5><p><i>Optimistic locking</i> is a pattern used to optimize database transactions. Instead of locking the database tables and rows when values are updated, we allow other transactions to access these values. Concurrent writes are possible and they must be detected. For this <i>optimistic locking</i> uses a version counter, or a timestamp or state comparison to detect concurrent writes.</p><p>When a concurrent write is detected, Hibernate raises a <code>StaleObjectStateException</code> exception. When such exception occurs, the state of objects associated with the current hibernate session is unknown. (See <a href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html">Transactions and Concurrency</a>)</p><p>As far as Planzone is concerned, we get 3 exceptions per 10000 calls.</p><h5>LockAcquisitionException (Database deadlocks)</h5><p>On the database side, the server can detect deadlock situation and report an error. When a deadlock is detected between two clients, the server generates an error for one client and the second one can proceed. When such error is reported, the client can retry the operation (See <a href="http://dev.mysql.com/doc/refman/5.1/en/innodb-lock-modes.html">InnoDB Lock Modes</a>).</p><p>As far as Planzone is concerned, we get 1 or 2 exceptions per 10000 calls.</p><h5>JDBCConnectionException (Connection failure)</h5><p>Sometimes the connection to the database is lost either because the database server crashed or because it was restarted due to maintenance reasons. Server crash is rare but it can occur. For Planzone, we had 3 crashes during the last 2 years (one crash every 240 day). During the same period we also had to stop and restart the server 2 times for a server upgrade.</p><p>Restarting the call after a database connection failure is a little bit more complex. It is necessary to sleep some time before retrying.</p><h2>EJB Interceptor</h2><p>To create our fault tolerant mechanism we use an EJB interceptor which is invoked for each EJB method call. For this the interceptor defines a method marked with the @<code>ArroundInvoke</code> annotation. Its role is to catch the transient faults and retry the call. The example below retries the call at most 10 times.</p><p>The EJB interceptor method receives an <code>InvocationContext</code> parameter which allows to have access to the target object, parameters and method to invoke. The <code>proceed</code> method allows to transfer the control to the next interceptor and to the EJB method. The real implementation is a little bit more complex due to logging but the overall idea is here.</p><pre><code>class RetryInterceptor {
@AroundInvoke
public Object retry(InvocationContext context) throws Exception {
for (int retry = 0; ; retry++) {
try {
return context.proceed();
} catch (LockAcquisitionException ex) {
if (retry > 10) {
throw ex;
}
} catch (StaleObjectStateException ex) {
if (retry > 10) {
throw ex;
}
} catch (final JDBCConnectionException ex) {
if (retry > 10) {
throw ex;
}
Thread.sleep(500L + retry * 1000L);
}
}
}
</code></pre><h2>EJB Interface</h2><p>For the purpose of this article, the EJB interface is declared as follows. Our choice was to define an <code>ILocal</code> and an <code>IRemote</code> interface to allow the creation of local and remote services.</p><pre><code>public interface Service {
...
@Local
interface ILocal extends Service {
}
@Remote
interface IRemote extends Service {
}
}
</code></pre><h3>EJB Declaration</h3><p>The interceptor is associated with the EJB implementation class by using the @<code>Interceptors</code> annotation. The same interceptor class can be associated with several EJBs.</p><pre><code>@Stateless(name = "Service")
@Interceptors(RetryInterceptor.class)
public class ServiceBean
implements Service.ILocal, Service.IRemote {
...
}
</code></pre><h3>Testing</h3><p>To test the solution, I recommend to write a unit test. The unit test I wrote did the following:</p><ul><li>A first thread executes the EJB method call.</li><li>The transaction <code>commit</code> operation is overriden by the unit test.</li><li>When the <code>commit</code> is called, a second thread is activated to simulate the concurrent call before committing.</li><li>The second thread performs the EJB method call in such a way that it will trigger the <code>StaleObjectStateException</code> when the first thread resumes</li><li>When the second thread finished, the first thread can perform the real commit and the <code>StaleObjectStateException</code> is raised by Hibernate because the object was modified.</li><li>The interceptor catches the exception and retries the call which will succeed.</li></ul><p>The full design of such test is outside of the scope of this article. It is also specific to each application.</p></div>