Browse Source

OfficeFloor adding performance improvements for SQL Client (#6607)

* Removing Rapidoid as seems dead project (no commit last year)

* Downloading from SourceForge

* Bump maven-compiler-plugin in /frameworks/Java/officefloor/src

Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.8.0 to 3.8.1.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.0...maven-compiler-plugin-3.8.1)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump maven-shade-plugin in /frameworks/Java/officefloor/src

Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.2.1...maven-shade-plugin-3.2.2)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Fixing Raw OfficeFloor

* Specifying Spring plugin version

* Bump to OfficeFloor 3.21.0

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.2.4.RELEASE to 2.2.5.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.4.RELEASE...v2.2.5.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.2.5.RELEASE to 2.2.6.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.5.RELEASE...v2.2.6.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.21.0 to 3.22.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump maven-shade-plugin in /frameworks/Java/officefloor/src

Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.2.2...maven-shade-plugin-3.2.3)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.22.0 to 3.23.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.23.0 to 3.24.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.2.6.RELEASE to 2.2.7.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.6.RELEASE...v2.2.7.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.2.7.RELEASE to 2.3.0.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.2.7.RELEASE...v2.3.0.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.24.0 to 3.25.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump maven-shade-plugin in /frameworks/Java/officefloor/src

Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.2.3...maven-shade-plugin-3.2.4)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.3.0.RELEASE to 2.3.1.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.0.RELEASE...v2.3.1.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.3.1.RELEASE to 2.3.2.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.1.RELEASE...v2.3.2.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.25.0 to 3.26.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/compare/release-3.25.0...release-3.26.0)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.3.2.RELEASE to 2.3.3.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.2.RELEASE...v2.3.3.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.26.0 to 3.27.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.3.3.RELEASE to 2.3.4.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.3.RELEASE...v2.3.4.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.27.0 to 3.28.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Fixing to run with v3.28.0

* Including Undertow

* Using slim docker images

* Fixing to add all supported tests

* Increasing connection pool size

* Write up the 503 errors and why

* Fix grammer

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.3.4.RELEASE to 2.3.5.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.4.RELEASE...v2.3.5.RELEASE)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Improving chances of full processing

Increasing max thread counts to have thread per client.  Also, shading
jars correctly.

Plus adding in DB test for raw

* Fixing raw test to have database

* Ensure random numbers to avoid cached entities

* Ensuring spring random numbers to avoid entity caching

* Adding queries for raw

* officefloor-raw queries passing

* office-raw supporting all requests

* Fixed update test to ensure updates occur before responding

* Adding officefloor-async

This will demonstrate OfficeFloor running as a single thread
asynchronous server

* Upgrading to OfficeFloor 2.38.1

* Tidy up read me

* Appropriate back pressure queue

* Handle rate limiting

* Providing appropriate throttling

* Lowering threading to handle through put

* officefloor-raw passing tests

* Passing tests

* OfficeFloor 3.28.2

* Fixing rate limiting

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.28.2 to 3.29.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Fixing for bump to 3.30.0

* Increasing max direct memory

* Increasing threshold to enable verifications to pass for officefloor-raw

* Start script to take available memory into consideration

* Max direct memory aware of available memory

Also, cleaning writer threads of buffers to avoid leaks.

* Avoiding OOM by managing disable/enable reading

* DB and Fortune passing

* Ensuring free command is available

* Removing TODOs

* Increasing reactor buffer for multiple queries

* Fixing for 3.30.1

* Passing benchmark tests

* Avoiding OOM on reactor buffer sizes

* Passing validate tests

* Using defaults from 3.30.1

* Bump to 512 threads and connections

* Reverting dockerfiles to provide appropriate parameters

* openjdk:slim for apt-get available

* Providing thread affinity to raw

* Fixing rate limit throttling

* Multiplexing queries over connections per socket

* Fixing for large queries in validate

* Fixing versions of maven and java

* Providing thread affinity of DB connection thread

* Fixing for Netty event loop thread

* Tidying up compiler warnings

* Single db pool to avoid additional threads

* Use default LoopResources

* Tidying up code for warnings

* Further tidy up of code

* Revert to latest pull request

* Using parallel GC for better throughput

* Fix up for thread local buffering improvements

* OfficeFloor fortune raw

Focus of officefloor-raw is raw performance of the OfficeFloor HTTP
server.  Using raw fortune to remove mustache overheads.

* Tidy up loop

* Server name (as per discussions)

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.3.5.RELEASE to 2.4.2.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.3.5.RELEASE...v2.4.2)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.30.2 to 3.31.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.31.0 to 3.32.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.4.2 to 2.4.3.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.4.2...v2.4.3)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.4.3 to 2.4.5.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.4.3...v2.4.5)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Upgrade to GitHub-native Dependabot

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.32.0 to 3.35.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits/release-3.35.0)

Signed-off-by: dependabot-preview[bot] <[email protected]>

* Bump to OfficeFloor 3.35.0

* Allow bump of all versions

* Vertx server

Also, renaming officefloor-raw to officefloor-r2dbc to make way for
officefloor-sqlclient

* Rename to R2dbc (from raw)

* Bump net.officefloor:bom in /frameworks/Java/officefloor/src

Bumps [net.officefloor:bom](https://github.com/officefloor/OfficeFloor) from 3.35.0 to 3.36.0.
- [Release notes](https://github.com/officefloor/OfficeFloor/releases)
- [Commits](https://github.com/officefloor/OfficeFloor/commits)

Signed-off-by: dependabot[bot] <[email protected]>

* Fixing meta-data

* Can not propagate Dependabot to TechEmpower

* Abstract WoOF from database driver

This will allow introducing Vertx SQL Client

* Fixing db port

* Providing Vertx SQL Client implementation

* Using OfficeFloorVertx for vertx

Allows for tests to reset

* Fixing link in read me

* Reducing repetition in readme

* Swapped OfficeFloor async to use Vertx SQL Client

* Bump spring-boot-maven-plugin in /frameworks/Java/officefloor/src

Bumps [spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) from 2.4.5 to 2.5.0.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v2.4.5...v2.5.0)

Signed-off-by: dependabot[bot] <[email protected]>

* Increasing SQL Client pool size to 512

Also, fixing update to sort to avoid deadlocks

* Removing unnecessary logging

* Removing dependabot configuration

* Improving performance of Vertx

This is by caching queries and using native communication.

Also, adding further performance updates to update test by sorting
updates

* Fixing OfficeFloor-vertx name

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Daniel 4 years ago
parent
commit
d059402440

+ 2 - 2
frameworks/Java/officefloor/benchmark_config.json

@@ -203,7 +203,7 @@
 				"webserver": "vertx",
 				"webserver": "vertx",
 				"os": "Linux",
 				"os": "Linux",
 				"database_os": "Linux",
 				"database_os": "Linux",
-				"display_name": "OfficeFloor-undertow",
+				"display_name": "OfficeFloor-vertx",
 				"notes": "",
 				"notes": "",
 				"versus": "vertx-postgres"
 				"versus": "vertx-postgres"
 			},
 			},
@@ -232,4 +232,4 @@
 			}
 			}
 		}
 		}
 	]
 	]
-}
+}

+ 9 - 1
frameworks/Java/officefloor/src/woof_benchmark_async/src/main/java/net/officefloor/benchmark/Logic.java

@@ -139,7 +139,15 @@ public class Logic {
 								return null;
 								return null;
 							}
 							}
 							Row row = rows.next();
 							Row row = rows.next();
-							return new World(row.getInteger(0), ThreadLocalRandom.current().nextInt(1, 10001));
+
+							// Ensure change to random number to trigger update
+							int previousRandomNumber = row.getInteger(1);
+							int newRandomNumber;
+							do {
+								newRandomNumber = ThreadLocalRandom.current().nextInt(1, 10001);
+							} while (previousRandomNumber == newRandomNumber);
+
+							return new World(row.getInteger(0), newRandomNumber);
 						}));
 						}));
 			}
 			}
 			return CompositeFuture.all(futures).flatMap((compositeFuture) -> {
 			return CompositeFuture.all(futures).flatMap((compositeFuture) -> {

+ 5 - 0
frameworks/Java/officefloor/src/woof_benchmark_async/src/main/java/net/officefloor/benchmark/SetupVertxSqlClient.java

@@ -28,9 +28,14 @@ public class SetupVertxSqlClient implements VertxSqlPoolConfigurer, VertxSqlPool
 
 
 	@Override
 	@Override
 	public void configure(VertxSqlPoolConfigurerContext context) throws Exception {
 	public void configure(VertxSqlPoolConfigurerContext context) throws Exception {
+
+		// Ensure adequate number of connections
 		final int MAX_POOL_SIZE = 512;
 		final int MAX_POOL_SIZE = 512;
 		System.out.println("Setting max pool size to " + MAX_POOL_SIZE);
 		System.out.println("Setting max pool size to " + MAX_POOL_SIZE);
 		context.getPoolOptions().setMaxSize(MAX_POOL_SIZE);
 		context.getPoolOptions().setMaxSize(MAX_POOL_SIZE);
+
+		// Configure options
+		context.getSqlConnectOptions().setCachePreparedStatements(true);
 	}
 	}
 
 
 }
 }

+ 8 - 1
frameworks/Java/officefloor/src/woof_benchmark_r2dbc/src/main/java/net/officefloor/benchmark/R2dbcOfficeFloorMain.java

@@ -223,7 +223,14 @@ public class R2dbcOfficeFloorMain implements DatabaseOperations {
 					Collections.sort(worlds, (a, b) -> a.id - b.id);
 					Collections.sort(worlds, (a, b) -> a.id - b.id);
 					Batch batch = conn.connection.createBatch();
 					Batch batch = conn.connection.createBatch();
 					for (World world : worlds) {
 					for (World world : worlds) {
-						world.randomNumber = ThreadLocalRandom.current().nextInt(1, 10001);
+
+						// Ensure change to random number to trigger update
+						int newRandomNumber;
+						do {
+							newRandomNumber = ThreadLocalRandom.current().nextInt(1, 10001);
+						} while (world.randomNumber == newRandomNumber);
+						world.randomNumber = newRandomNumber;
+
 						batch.add("UPDATE WORLD SET RANDOMNUMBER = " + world.randomNumber + " WHERE ID = " + world.id);
 						batch.add("UPDATE WORLD SET RANDOMNUMBER = " + world.randomNumber + " WHERE ID = " + world.id);
 					}
 					}
 					return Mono.from(batch.execute()).map((result) -> worlds);
 					return Mono.from(batch.execute()).map((result) -> worlds);

+ 174 - 164
frameworks/Java/officefloor/src/woof_benchmark_sqlclient/src/main/java/net/officefloor/benchmark/SqlClientOfficeFloorMain.java

@@ -1,6 +1,7 @@
 package net.officefloor.benchmark;
 package net.officefloor.benchmark;
 
 
 import io.vertx.core.Vertx;
 import io.vertx.core.Vertx;
+import io.vertx.core.VertxOptions;
 import io.vertx.pgclient.PgConnectOptions;
 import io.vertx.pgclient.PgConnectOptions;
 import io.vertx.pgclient.PgConnection;
 import io.vertx.pgclient.PgConnection;
 import io.vertx.sqlclient.Row;
 import io.vertx.sqlclient.Row;
@@ -15,6 +16,7 @@ import net.officefloor.vertx.OfficeFloorVertx;
 
 
 import java.net.Socket;
 import java.net.Socket;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Queue;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.ConcurrentLinkedDeque;
@@ -27,169 +29,177 @@ import java.util.concurrent.ThreadLocalRandom;
  */
  */
 public class SqlClientOfficeFloorMain implements DatabaseOperations {
 public class SqlClientOfficeFloorMain implements DatabaseOperations {
 
 
-    /**
-     * Run application.
-     */
-    public static void main(String[] args) throws Exception {
-        RawWoof.run(args, (socketCount, server, port, database, username,
-                           password) -> new SqlClientOfficeFloorMain(socketCount, server, port, database, username, password));
-    }
-
-    /**
-     * {@link ThreadLocal} {@link PgConnection} instances.
-     */
-    private final ThreadLocal<PgConnection> threadLocalConnection;
-
-    /**
-     * Instantiate.
-     *
-     * @param socketCount Number of server {@link Socket} instances.
-     * @param server      Name of database server.
-     * @param port        Port of database.
-     * @param database    Name of database within server.
-     * @param username    Username.
-     * @param password    Password.
-     */
-    public SqlClientOfficeFloorMain(int socketCount, String server, int port, String database, String username,
-                                    String password) {
-    	
-    	// Obtain the vertx
-    	Vertx vertx = OfficeFloorVertx.getVertx();
-
-        // Create connection
-        PgConnectOptions connectOptions = new PgConnectOptions().setHost(server).setPort(port).setDatabase(database)
-                .setUser(username).setPassword(password);
-
-        // Create thread local connection
-        this.threadLocalConnection = new ThreadLocal<PgConnection>() {
-            @Override
-            protected PgConnection initialValue() {
-                try {
-                    return OfficeFloorVertx.block(PgConnection.connect(vertx, connectOptions));
-                } catch (Exception ex) {
-                    throw new IllegalStateException("Failed to setup connection", ex);
-                }
-            }
-        };
-    }
-
-    /*
-     * ===================== DatabaseOperations ======================
-     */
-
-    @Override
-    public void threadSetup(RequestHandler<HttpRequestParser> requestHandler) {
-        // Nothing thread specific to set up
-    }
-
-    @Override
-    public void db(HttpResponse response, ServerHttpConnection connection, DatabaseOperationsContext context) {
-        this.threadLocalConnection.get().preparedQuery("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID=$1")
-                .execute(Tuple.of(ThreadLocalRandom.current().nextInt(1, 10001)),
-                        result -> {
-                            if (result.failed()) {
-                                context.sendError(connection, result.cause());
-                            } else {
-                                RowIterator<Row> rows = result.result().iterator();
-                                if (!rows.hasNext()) {
-                                    context.sendError(connection, 404);
-                                } else {
-                                    Row row = rows.next();
-                                    World world = new World(row.getInteger(0), row.getInteger(1));
-                                    context.dbSend(response, connection, world);
-                                }
-                            }
-                        });
-    }
-
-    @Override
-    public void queries(int queryCount, HttpResponse response, ServerHttpConnection connection,
-                        DatabaseOperationsContext context) {
-        Queue<World> worlds = new ConcurrentLinkedDeque<>();
-        SqlConnection sqlConnection = this.threadLocalConnection.get();
-        for (int i = 0; i < queryCount; i++) {
-            sqlConnection.preparedQuery("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID=$1")
-                    .execute(Tuple.of(ThreadLocalRandom.current().nextInt(1, 10001)),
-                            result -> {
-                                if (result.failed()) {
-                                    context.sendError(connection, result.cause());
-                                } else {
-                                    RowIterator<Row> rows = result.result().iterator();
-                                    if (!rows.hasNext()) {
-                                        context.sendError(connection, 404);
-                                    } else {
-                                        Row row = rows.next();
-                                        World world = new World(row.getInteger(0), row.getInteger(1));
-                                        worlds.add(world);
-
-                                        if (worlds.size() == queryCount) {
-                                            context.queriesSend(response, connection, new ArrayList<>(worlds));
-                                        }
-                                    }
-                                }
-                            });
-        }
-    }
-
-    @Override
-    public void fortunes(HttpResponse response, ServerHttpConnection connection, DatabaseOperationsContext context) {
-        this.threadLocalConnection.get().preparedQuery("SELECT ID, MESSAGE FROM FORTUNE")
-                .execute(result -> {
-                    if (result.failed()) {
-                        context.sendError(connection, result.cause());
-                    } else {
-                        List<Fortune> fortunes = new ArrayList<>(16);
-                        RowIterator<Row> rows = result.result().iterator();
-                        while (rows.hasNext()) {
-                            Row row = rows.next();
-                            fortunes.add(new Fortune(row.getInteger(0), row.getString(1)));
-                        }
-                        context.fortunesSend(response, connection, fortunes);
-                    }
-                });
-    }
-
-    @Override
-    public void update(int queryCount, HttpResponse response, ServerHttpConnection connection,
-                       DatabaseOperationsContext context) {
-        Queue<World> worlds = new ConcurrentLinkedDeque<>();
-        SqlConnection sqlConnection = this.threadLocalConnection.get();
-        for (int i = 0; i < queryCount; i++) {
-            sqlConnection.preparedQuery("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID=$1")
-                    .execute(Tuple.of(ThreadLocalRandom.current().nextInt(1, 10001)),
-                            result -> {
-                                if (result.failed()) {
-                                    context.sendError(connection, result.cause());
-                                } else {
-                                    RowIterator<Row> rows = result.result().iterator();
-                                    if (!rows.hasNext()) {
-                                        context.sendError(connection, 404);
-                                    } else {
-                                        Row row = rows.next();
-                                        World world = new World(row.getInteger(0), ThreadLocalRandom.current().nextInt(1, 10001));
-                                        worlds.add(world);
-
-                                        if (worlds.size() == queryCount) {
-
-                                            // All worlds obtained, so run update
-                                            List<Tuple> batch = new ArrayList<>(queryCount);
-                                            for (World update : worlds) {
-                                                batch.add(Tuple.of(update.randomNumber, update.id));
-                                            }
-                                            sqlConnection.preparedQuery("UPDATE world SET randomnumber=$1 WHERE id=$2").executeBatch(batch, ar -> {
-                                                if (result.failed()) {
-                                                    context.sendError(connection, result.cause());
-                                                } else {
-
-                                                    // Updated, so send response
-                                                    context.queriesSend(response, connection, new ArrayList<>(worlds));
-                                                }
-                                            });
-                                        }
-                                    }
-                                }
-                            });
-        }
-    }
+	/**
+	 * Run application.
+	 */
+	public static void main(String[] args) throws Exception {
+		RawWoof.run(args, (socketCount, server, port, database, username,
+				password) -> new SqlClientOfficeFloorMain(socketCount, server, port, database, username, password));
+	}
+
+	/**
+	 * {@link ThreadLocal} {@link PgConnection} instances.
+	 */
+	private final ThreadLocal<PgConnection> threadLocalConnection;
+
+	/**
+	 * Instantiate.
+	 *
+	 * @param socketCount Number of server {@link Socket} instances.
+	 * @param server      Name of database server.
+	 * @param port        Port of database.
+	 * @param database    Name of database within server.
+	 * @param username    Username.
+	 * @param password    Password.
+	 */
+	public SqlClientOfficeFloorMain(int socketCount, String server, int port, String database, String username,
+			String password) {
+
+		// Obtain the vertx
+		Vertx vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(true));
+
+		// Create connection
+		PgConnectOptions connectOptions = new PgConnectOptions().setHost(server).setPort(port).setDatabase(database)
+				.setUser(username).setPassword(password).setCachePreparedStatements(true);
+
+		// Create thread local connection
+		this.threadLocalConnection = new ThreadLocal<PgConnection>() {
+			@Override
+			protected PgConnection initialValue() {
+				try {
+					return OfficeFloorVertx.block(PgConnection.connect(vertx, connectOptions));
+				} catch (Exception ex) {
+					throw new IllegalStateException("Failed to setup connection", ex);
+				}
+			}
+		};
+	}
+
+	/*
+	 * ===================== DatabaseOperations ======================
+	 */
+
+	@Override
+	public void threadSetup(RequestHandler<HttpRequestParser> requestHandler) {
+		// Nothing thread specific to set up
+	}
+
+	@Override
+	public void db(HttpResponse response, ServerHttpConnection connection, DatabaseOperationsContext context) {
+		this.threadLocalConnection.get().preparedQuery("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID=$1")
+				.execute(Tuple.of(ThreadLocalRandom.current().nextInt(1, 10001)), result -> {
+					if (result.failed()) {
+						context.sendError(connection, result.cause());
+					} else {
+						RowIterator<Row> rows = result.result().iterator();
+						if (!rows.hasNext()) {
+							context.sendError(connection, 404);
+						} else {
+							Row row = rows.next();
+							World world = new World(row.getInteger(0), row.getInteger(1));
+							context.dbSend(response, connection, world);
+						}
+					}
+				});
+	}
+
+	@Override
+	public void queries(int queryCount, HttpResponse response, ServerHttpConnection connection,
+			DatabaseOperationsContext context) {
+		Queue<World> worlds = new ConcurrentLinkedDeque<>();
+		SqlConnection sqlConnection = this.threadLocalConnection.get();
+		for (int i = 0; i < queryCount; i++) {
+			sqlConnection.preparedQuery("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID=$1")
+					.execute(Tuple.of(ThreadLocalRandom.current().nextInt(1, 10001)), result -> {
+						if (result.failed()) {
+							context.sendError(connection, result.cause());
+						} else {
+							RowIterator<Row> rows = result.result().iterator();
+							if (!rows.hasNext()) {
+								context.sendError(connection, 404);
+							} else {
+								Row row = rows.next();
+								World world = new World(row.getInteger(0), row.getInteger(1));
+								worlds.add(world);
+
+								if (worlds.size() == queryCount) {
+									context.queriesSend(response, connection, new ArrayList<>(worlds));
+								}
+							}
+						}
+					});
+		}
+	}
+
+	@Override
+	public void fortunes(HttpResponse response, ServerHttpConnection connection, DatabaseOperationsContext context) {
+		this.threadLocalConnection.get().preparedQuery("SELECT ID, MESSAGE FROM FORTUNE").execute(result -> {
+			if (result.failed()) {
+				context.sendError(connection, result.cause());
+			} else {
+				List<Fortune> fortunes = new ArrayList<>(16);
+				RowIterator<Row> rows = result.result().iterator();
+				while (rows.hasNext()) {
+					Row row = rows.next();
+					fortunes.add(new Fortune(row.getInteger(0), row.getString(1)));
+				}
+				context.fortunesSend(response, connection, fortunes);
+			}
+		});
+	}
+
+	@Override
+	public void update(int queryCount, HttpResponse response, ServerHttpConnection connection,
+			DatabaseOperationsContext context) {
+		Queue<World> worlds = new ConcurrentLinkedDeque<>();
+		SqlConnection sqlConnection = this.threadLocalConnection.get();
+		for (int i = 0; i < queryCount; i++) {
+			sqlConnection.preparedQuery("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID=$1")
+					.execute(Tuple.of(ThreadLocalRandom.current().nextInt(1, 10001)), result -> {
+						if (result.failed()) {
+							context.sendError(connection, result.cause());
+						} else {
+							RowIterator<Row> rows = result.result().iterator();
+							if (!rows.hasNext()) {
+								context.sendError(connection, 404);
+							} else {
+								Row row = rows.next();
+
+								// Ensure change to random number to trigger update
+								int previousRandomNumber = row.getInteger(1);
+								int newRandomNumber;
+								do {
+									newRandomNumber = ThreadLocalRandom.current().nextInt(1, 10001);
+								} while (previousRandomNumber == newRandomNumber);
+								World world = new World(row.getInteger(0), newRandomNumber);
+								worlds.add(world);
+
+								if (worlds.size() == queryCount) {
+
+									// All worlds obtained, so run update
+									List<Tuple> batch = new ArrayList<>(queryCount);
+									for (World update : worlds) {
+										batch.add(Tuple.of(update.randomNumber, update.id));
+									}
+
+									// Sort to avoid deadlocks on updates
+									Collections.sort(batch, (a, b) -> a.getInteger(1) - b.getInteger(1));
+
+									sqlConnection.preparedQuery("UPDATE world SET randomnumber=$1 WHERE id=$2")
+											.executeBatch(batch, ar -> {
+												if (result.failed()) {
+													context.sendError(connection, result.cause());
+												} else {
+
+													// Updated, so send response
+													context.queriesSend(response, connection, new ArrayList<>(worlds));
+												}
+											});
+								}
+							}
+						}
+					});
+		}
+	}
 
 
 }
 }