Browse Source

OfficeFloor 3.28.2 (#6138)

* 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

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

+ 1 - 1
frameworks/Java/officefloor/officefloor-async.dockerfile

@@ -2,7 +2,7 @@ FROM maven:slim as maven
 WORKDIR /officefloor
 COPY src src
 WORKDIR /officefloor/src/woof_benchmark_async
-RUN mvn -q clean package
+RUN mvn -B clean package
 
 FROM openjdk:slim
 WORKDIR /officefloor

+ 1 - 1
frameworks/Java/officefloor/officefloor-micro.dockerfile

@@ -2,7 +2,7 @@ FROM maven:slim as maven
 WORKDIR /officefloor
 COPY src src
 WORKDIR /officefloor/src/woof_benchmark_micro
-RUN mvn -q clean package
+RUN mvn -B clean package
 
 FROM openjdk:slim
 WORKDIR /officefloor

+ 3 - 3
frameworks/Java/officefloor/officefloor-netty.dockerfile

@@ -2,11 +2,11 @@ FROM maven:slim as maven
 WORKDIR /officefloor
 COPY src src
 WORKDIR /officefloor/src
-RUN mvn -q -N clean install
+RUN mvn -B -N clean install
 WORKDIR /officefloor/src/woof_benchmark
-RUN mvn -q clean install
+RUN mvn -B clean install
 WORKDIR /officefloor/src/woof_benchmark_netty
-RUN mvn -q clean package
+RUN mvn -B clean package
 
 FROM openjdk:slim
 WORKDIR /officefloor

+ 1 - 1
frameworks/Java/officefloor/officefloor-raw.dockerfile

@@ -2,7 +2,7 @@ FROM maven:slim as maven
 WORKDIR /officefloor
 COPY src src
 WORKDIR /officefloor/src/woof_benchmark_raw
-RUN mvn -q clean package
+RUN mvn -B clean package
 
 FROM openjdk:slim
 RUN apt-get update && apt-get install -y libjna-java

+ 1 - 1
frameworks/Java/officefloor/officefloor-spring_data.dockerfile

@@ -2,7 +2,7 @@ FROM maven:slim as maven
 WORKDIR /officefloor
 COPY src src
 WORKDIR /officefloor/src/woof_benchmark_spring
-RUN mvn -q clean package
+RUN mvn -B clean package
 
 FROM openjdk:slim
 WORKDIR /officefloor

+ 3 - 3
frameworks/Java/officefloor/officefloor-thread_affinity.dockerfile

@@ -2,11 +2,11 @@ FROM maven:slim as maven
 WORKDIR /officefloor
 COPY src src
 WORKDIR /officefloor/src
-RUN mvn -q -N clean install
+RUN mvn -B -N clean install
 WORKDIR /officefloor/src/woof_benchmark_micro
-RUN mvn -q clean install
+RUN mvn -B clean install
 WORKDIR /officefloor/src/woof_benchmark_thread_affinity
-RUN mvn -q clean package
+RUN mvn -B clean package
 
 FROM openjdk:slim
 RUN apt-get update && apt-get install -y libjna-java

+ 3 - 3
frameworks/Java/officefloor/officefloor-undertow.dockerfile

@@ -2,11 +2,11 @@ FROM maven:slim as maven
 WORKDIR /officefloor
 COPY src src
 WORKDIR /officefloor/src
-RUN mvn -q -N clean install
+RUN mvn -B -N clean install
 WORKDIR /officefloor/src/woof_benchmark
-RUN mvn -q clean install
+RUN mvn -B clean install
 WORKDIR /officefloor/src/woof_benchmark_undertow
-RUN mvn -q clean package
+RUN mvn -B clean package
 
 FROM openjdk:slim
 WORKDIR /officefloor

+ 1 - 1
frameworks/Java/officefloor/officefloor.dockerfile

@@ -2,7 +2,7 @@ FROM maven:slim as maven
 WORKDIR /officefloor
 COPY src src
 WORKDIR /officefloor/src/woof_benchmark
-RUN mvn -q clean package
+RUN mvn -B clean package
 
 FROM openjdk:slim
 WORKDIR /officefloor

+ 1 - 1
frameworks/Java/officefloor/src/pom.xml

@@ -39,7 +39,7 @@
 			<dependency>
 				<groupId>net.officefloor</groupId>
 				<artifactId>bom</artifactId>
-				<version>3.28.1</version>
+				<version>3.28.2</version>
 				<type>pom</type>
 				<scope>import</scope>
 			</dependency>

+ 1 - 1
frameworks/Java/officefloor/src/woof_benchmark/src/main/resources/application.teams

@@ -1,5 +1,5 @@
 <teams>
 
-	<team size="512" source="net.officefloor.frame.impl.spi.team.ExecutorCachedTeamSource" type="javax.persistence.EntityManager" />
+	<team size="256" source="net.officefloor.frame.impl.spi.team.ExecutorCachedTeamSource" type="javax.persistence.EntityManager" />
 
 </teams>

+ 1 - 1
frameworks/Java/officefloor/src/woof_benchmark/src/main/resources/datasource.properties

@@ -4,4 +4,4 @@ port=5432
 database=hello_world
 user=benchmarkdbuser
 password=benchmarkdbpass
-maximumPoolSize=512
+maximumPoolSize=256

+ 96 - 37
frameworks/Java/officefloor/src/woof_benchmark_async/src/main/java/net/officefloor/benchmark/Logic.java

@@ -6,6 +6,7 @@ import java.sql.SQLException;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Consumer;
 
 import org.apache.commons.text.StringEscapeUtils;
 
@@ -13,12 +14,19 @@ import com.github.mustachejava.DefaultMustacheFactory;
 import com.github.mustachejava.Mustache;
 import com.github.mustachejava.MustacheFactory;
 
+import io.r2dbc.spi.Batch;
+import io.r2dbc.spi.R2dbcTransientResourceException;
 import lombok.AllArgsConstructor;
 import lombok.Data;
+import net.officefloor.frame.api.escalate.AsynchronousFlowTimedOutEscalation;
 import net.officefloor.frame.api.function.AsynchronousFlow;
+import net.officefloor.frame.api.function.FlowCallback;
+import net.officefloor.plugin.clazz.FlowInterface;
 import net.officefloor.r2dbc.R2dbcSource;
+import net.officefloor.server.http.HttpException;
 import net.officefloor.server.http.HttpHeaderValue;
 import net.officefloor.server.http.HttpResponse;
+import net.officefloor.server.http.HttpStatus;
 import net.officefloor.server.http.ServerHttpConnection;
 import net.officefloor.web.HttpQueryParameter;
 import net.officefloor.web.ObjectResponse;
@@ -32,9 +40,20 @@ public class Logic {
 
 	static {
 		// Increase the buffer size
-		System.setProperty("reactor.bufferSize.small", String.valueOf(10000));
+		System.setProperty("reactor.bufferSize.small", String.valueOf(512));
 	}
 
+	private static final FlowCallback callback = (error) -> {
+		if (error == null) {
+			return;
+		} else if (error instanceof AsynchronousFlowTimedOutEscalation) {
+			throw new HttpException(HttpStatus.SERVICE_UNAVAILABLE);
+		} else {
+			System.out.println("ERROR: " + error.getClass().getName());
+			throw error;
+		}
+	};
+
 	/**
 	 * {@link Mustache} for /fortunes.
 	 */
@@ -84,7 +103,17 @@ public class Logic {
 		private int randomNumber;
 	}
 
-	public void db(AsynchronousFlow async, R2dbcSource source, ObjectResponse<World> response) throws SQLException {
+	@FlowInterface
+	public static interface DbFlows {
+		void dbService(FlowCallback callback);
+	}
+
+	public void db(DbFlows flows) {
+		flows.dbService(callback);
+	}
+
+	public void dbService(AsynchronousFlow async, R2dbcSource source, ObjectResponse<World> response)
+			throws SQLException {
 		source.getConnection().flatMap(
 				connection -> Mono.from(connection.createStatement("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID = $1")
 						.bind(0, ThreadLocalRandom.current().nextInt(1, 10001)).execute()))
@@ -94,15 +123,22 @@ public class Logic {
 					return new World(id, number);
 				}))).subscribe(world -> async.complete(() -> {
 					response.send(world);
-				}), error -> async.complete(() -> {
-					throw error;
-				}));
+				}), handleError(async));
 	}
 
 	// ========== QUERIES ==================
 
-	public void queries(@HttpQueryParameter("queries") String queries, AsynchronousFlow async, R2dbcSource source,
-			ObjectResponse<List<World>> response) {
+	@FlowInterface
+	public static interface QueriesFlows {
+		void queriesService(FlowCallback callback);
+	}
+
+	public void queries(QueriesFlows flows) {
+		flows.queriesService(callback);
+	}
+
+	public void queriesService(@HttpQueryParameter("queries") String queries, AsynchronousFlow async,
+			R2dbcSource source, ObjectResponse<List<World>> response) {
 		int queryCount = getQueryCount(queries);
 		source.getConnection().flatMap(connection -> {
 			return Flux.range(1, queryCount)
@@ -115,14 +151,21 @@ public class Logic {
 					}))).collectList();
 		}).subscribe(worlds -> async.complete(() -> {
 			response.send(worlds);
-		}), error -> async.complete(() -> {
-			throw error;
-		}));
+		}), handleError(async));
 	}
 
 	// =========== UPDATES ===================
 
-	public void update(@HttpQueryParameter("queries") String queries, AsynchronousFlow async, R2dbcSource source,
+	@FlowInterface
+	public static interface UpdateFlows {
+		void updateService(FlowCallback callback);
+	}
+
+	public void update(UpdateFlows flows) {
+		flows.updateService(callback);
+	}
+
+	public void updateService(@HttpQueryParameter("queries") String queries, AsynchronousFlow async, R2dbcSource source,
 			ObjectResponse<List<World>> response) {
 		int queryCount = getQueryCount(queries);
 		source.getConnection().flatMap(connection -> {
@@ -133,18 +176,19 @@ public class Logic {
 						Integer id = row.get(0, Integer.class);
 						Integer number = row.get(1, Integer.class);
 						return new World(id, number);
-					}))).flatMap(world -> {
-						world.randomNumber = ThreadLocalRandom.current().nextInt(1, 10001);
-						return Flux
-								.from(connection.createStatement("UPDATE WORLD SET RANDOMNUMBER = $1 WHERE ID = $2")
-										.bind(0, world.randomNumber).bind(1, world.id).execute())
-								.flatMap(result -> Flux.from(result.getRowsUpdated()).map(updated -> world));
-					}).collectList();
+					}))).collectList().flatMap(worlds -> {
+						Collections.sort(worlds, (a, b) -> a.id - b.id);
+						Batch batch = connection.createBatch();
+						for (World world : worlds) {
+							world.randomNumber = ThreadLocalRandom.current().nextInt(1, 10001);
+							batch.add("UPDATE WORLD SET RANDOMNUMBER = " + world.randomNumber + " WHERE ID = "
+									+ world.id);
+						}
+						return Mono.from(batch.execute()).map((result) -> worlds);
+					});
 		}).subscribe(worlds -> async.complete(() -> {
 			response.send(worlds);
-		}), error -> async.complete(() -> {
-			throw error;
-		}));
+		}), handleError(async));
 	}
 
 	// =========== FORTUNES ==================
@@ -160,7 +204,16 @@ public class Logic {
 		private String message;
 	}
 
-	public void fortunes(AsynchronousFlow async, R2dbcSource source, ServerHttpConnection httpConnection)
+	@FlowInterface
+	public static interface FortunesFlows {
+		void fortunesService(FlowCallback callback);
+	}
+
+	public void fortunes(FortunesFlows flows) {
+		flows.fortunesService(callback);
+	}
+
+	public void fortunesService(AsynchronousFlow async, R2dbcSource source, ServerHttpConnection httpConnection)
 			throws IOException, SQLException {
 		source.getConnection().flatMap(connection -> {
 			return Flux.from(connection.createStatement("SELECT ID, MESSAGE FROM FORTUNE").execute())
@@ -170,21 +223,17 @@ public class Logic {
 						return new Fortune(id, message);
 					}))).collectList();
 		}).subscribe(fortunes -> async.complete(() -> {
-			try {
-				// Additional fortunes
-				fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-				Collections.sort(fortunes, (a, b) -> a.message.compareTo(b.message));
-
-				// Send response
-				HttpResponse response = httpConnection.getResponse();
-				response.setContentType(TEXT_HTML, null);
-				this.fortuneMustache.execute(response.getEntityWriter(), fortunes);
-			} catch (IOException ex) {
-				ex.printStackTrace();
-			}
-		}), error -> async.complete(() -> {
-			throw error;
-		}));
+
+			// Additional fortunes
+			fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+			Collections.sort(fortunes, (a, b) -> a.message.compareTo(b.message));
+
+			// Send response
+			HttpResponse response = httpConnection.getResponse();
+			response.setContentType(TEXT_HTML, null);
+			this.fortuneMustache.execute(response.getEntityWriter(), fortunes);
+
+		}), handleError(async));
 	}
 
 	// =========== helper ===================
@@ -198,4 +247,14 @@ public class Logic {
 		}
 	}
 
+	private static Consumer<Throwable> handleError(AsynchronousFlow async) {
+		return (error) -> async.complete(() -> {
+			try {
+				throw error;
+			} catch (R2dbcTransientResourceException | AsynchronousFlowTimedOutEscalation overloadEx) {
+				throw new HttpException(HttpStatus.SERVICE_UNAVAILABLE);
+			}
+		});
+	}
+
 }

+ 4 - 0
frameworks/Java/officefloor/src/woof_benchmark_async/src/main/resources/application.woof

@@ -26,11 +26,15 @@
   <sections>
     <section name="Logic" source="net.officefloor.plugin.section.clazz.ClassSectionSource" location="net.officefloor.benchmark.Logic" x="281" y="140">
       <input name="db" parameter-type=""/>
+      <input name="dbService" parameter-type=""/>
       <input name="fortunes" parameter-type=""/>
+      <input name="fortunesService" parameter-type=""/>
       <input name="json" parameter-type=""/>
       <input name="plaintext" parameter-type=""/>
       <input name="queries" parameter-type=""/>
+      <input name="queriesService" parameter-type=""/>
       <input name="update" parameter-type=""/>
+      <input name="updateService" parameter-type=""/>
     </section>
   </sections>
   <procedures>

+ 1 - 1
frameworks/Java/officefloor/src/woof_benchmark_micro/src/main/resources/application.teams

@@ -1,5 +1,5 @@
 <teams>
 
-	<team size="512" source="net.officefloor.frame.impl.spi.team.ExecutorCachedTeamSource" type="javax.sql.DataSource" />
+	<team size="256" source="net.officefloor.frame.impl.spi.team.ExecutorCachedTeamSource" type="javax.sql.DataSource" />
 
 </teams>

+ 1 - 1
frameworks/Java/officefloor/src/woof_benchmark_micro/src/main/resources/datasource.properties

@@ -4,4 +4,4 @@ port=5432
 database=hello_world
 user=benchmarkdbuser
 password=benchmarkdbpass
-maximumPoolSize=512
+maximumPoolSize=256

+ 182 - 13
frameworks/Java/officefloor/src/woof_benchmark_raw/src/main/java/net/officefloor/benchmark/RawOfficeFloorMain.java

@@ -18,6 +18,7 @@
 package net.officefloor.benchmark;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.io.Writer;
 import java.nio.ByteBuffer;
 import java.time.ZoneOffset;
@@ -29,6 +30,7 @@ import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Logger;
 
 import org.apache.commons.text.StringEscapeUtils;
@@ -39,10 +41,12 @@ import com.github.mustachejava.DefaultMustacheFactory;
 import com.github.mustachejava.Mustache;
 import com.github.mustachejava.MustacheFactory;
 
+import io.r2dbc.spi.Batch;
 import io.r2dbc.spi.Connection;
 import io.r2dbc.spi.ConnectionFactories;
 import io.r2dbc.spi.ConnectionFactory;
 import io.r2dbc.spi.ConnectionFactoryOptions;
+import io.r2dbc.spi.R2dbcTransientResourceException;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import net.officefloor.frame.api.manage.OfficeFloor;
@@ -76,6 +80,11 @@ import reactor.core.publisher.Mono;
  */
 public class RawOfficeFloorMain {
 
+	/**
+	 * Buffer size of queries.
+	 */
+	private static final int QUERY_BUFFER_SIZE = 512;
+
 	/**
 	 * {@link SocketManager}.
 	 */
@@ -104,8 +113,8 @@ public class RawOfficeFloorMain {
 				"tfb-database");
 		System.out.println("Starting server on port " + port + " talking to database " + server);
 
-		// Increase the buffer size
-		System.setProperty("reactor.bufferSize.small", String.valueOf(10000));
+		// Increase the buffer size (note: too high and cause OOM issues)
+		System.setProperty("reactor.bufferSize.small", String.valueOf(QUERY_BUFFER_SIZE));
 
 		// Build the connection pool
 		ConnectionFactoryOptions factoryOptions = ConnectionFactoryOptions.builder()
@@ -174,6 +183,10 @@ public class RawOfficeFloorMain {
 
 		private static final String UPDATE_PATH_PREFIX = "/update?queries=";
 
+		private static final R2dbcTransientResourceException THROTTLED = new R2dbcTransientResourceException();
+
+		private static final int LARGE_QUERY = 100;
+
 		/**
 		 * <code>Date</code> {@link HttpHeaderValue}.
 		 */
@@ -223,6 +236,26 @@ public class RawOfficeFloorMain {
 		 */
 		private final ThreadLocal<Connection> threadLocalConnection;
 
+		/**
+		 * db {@link ThreadLocalRateLimit}.
+		 */
+		private final ThreadLocalRateLimit dbRateLimit = new ThreadLocalRateLimit();
+
+		/**
+		 * queries {@link ThreadLocalRateLimit}.
+		 */
+		private final ThreadLocalRateLimit queriesRateLimit = new ThreadLocalRateLimit();
+
+		/**
+		 * fortunes {@link ThreadLocalRateLimit}.
+		 */
+		private final ThreadLocalRateLimit fortunesRateLimit = new ThreadLocalRateLimit();
+
+		/**
+		 * updates {@link ThreadLocalRateLimit}.
+		 */
+		private final ThreadLocalRateLimit updatesRateLimit = new ThreadLocalRateLimit();
+
 		/**
 		 * {@link Mono} to service /db.
 		 */
@@ -383,6 +416,15 @@ public class RawOfficeFloorMain {
 		}
 
 		private void db(HttpResponse response, ProcessAwareServerHttpConnectionManagedObject<ByteBuffer> connection) {
+
+			// Determine if will overload queries
+			RateLimit rateLimit = this.dbRateLimit.get();
+			if (rateLimit.isLimit(1)) {
+				this.sendError(connection, THROTTLED);
+				return; // rate limited
+			}
+
+			// Service
 			this.db.subscribe(world -> {
 				try {
 					response.setContentType(APPLICATION_JSON, null);
@@ -393,16 +435,31 @@ public class RawOfficeFloorMain {
 				}
 			}, error -> {
 				this.sendError(connection, error);
+			}, () -> {
+				rateLimit.processed(1);
 			});
 		}
 
 		private void queries(String requestUri, HttpResponse response,
 				ProcessAwareServerHttpConnectionManagedObject<ByteBuffer> connection) {
+
+			// Obtain the number of queries
 			String queriesCountText = requestUri.substring(QUERIES_PATH_PREFIX.length());
 			int queryCount = getQueryCount(queriesCountText);
+
+			// Determine if will overload queries
+			RateLimit rateLimit = this.queriesRateLimit.get();
+			if (queryCount < LARGE_QUERY) {
+				if (rateLimit.isLimit(queryCount)) {
+					this.sendError(connection, THROTTLED);
+					return; // rate limited
+				}
+			}
+
+			// Service
+			Connection dbConn = this.threadLocalConnection.get();
 			Flux.range(1, queryCount)
-					.flatMap(index -> this.threadLocalConnection.get()
-							.createStatement("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID = $1")
+					.flatMap(index -> dbConn.createStatement("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID = $1")
 							.bind(0, ThreadLocalRandom.current().nextInt(1, 10001)).execute())
 					.flatMap(result -> Flux.from(result.map((row, metadata) -> {
 						Integer id = row.get(0, Integer.class);
@@ -418,11 +475,24 @@ public class RawOfficeFloorMain {
 						}
 					}, error -> {
 						this.sendError(connection, error);
+					}, () -> {
+						if (queryCount < LARGE_QUERY) {
+							rateLimit.processed(queryCount);
+						}
 					});
 		}
 
 		private void fortunes(HttpResponse response,
 				ProcessAwareServerHttpConnectionManagedObject<ByteBuffer> connection) {
+
+			// Determine if will overload queries
+			RateLimit rateLimit = this.fortunesRateLimit.get();
+			if (rateLimit.isLimit(1)) {
+				this.sendError(connection, THROTTLED);
+				return; // rate limited
+			}
+
+			// Service
 			this.fortunes.subscribe(fortunes -> {
 				try {
 					// Additional fortunes
@@ -438,13 +508,29 @@ public class RawOfficeFloorMain {
 				}
 			}, error -> {
 				this.sendError(connection, error);
+			}, () -> {
+				rateLimit.processed(1);
 			});
 		}
 
 		private void update(String requestUri, HttpResponse response,
 				ProcessAwareServerHttpConnectionManagedObject<ByteBuffer> connection) {
+
+			// Obtain the number of queries
 			String queriesCountText = requestUri.substring(UPDATE_PATH_PREFIX.length());
 			int queryCount = getQueryCount(queriesCountText);
+
+			// Determine if will overload queries
+			int executedQueryCount = queryCount * 2; // select and update
+			RateLimit rateLimit = this.updatesRateLimit.get();
+			if (queryCount < LARGE_QUERY) {
+				if (rateLimit.isLimit(executedQueryCount)) {
+					this.sendError(connection, THROTTLED);
+					return; // rate limited
+				}
+			}
+
+			// Service
 			Connection db = this.threadLocalConnection.get();
 			Flux.range(1, queryCount)
 					.flatMap(index -> db.createStatement("SELECT ID, RANDOMNUMBER FROM WORLD WHERE ID = $1")
@@ -453,13 +539,16 @@ public class RawOfficeFloorMain {
 						Integer id = row.get(0, Integer.class);
 						Integer number = row.get(1, Integer.class);
 						return new World(id, number);
-					}))).flatMap(world -> {
-						world.randomNumber = ThreadLocalRandom.current().nextInt(1, 10001);
-						return Flux
-								.from(db.createStatement("UPDATE WORLD SET RANDOMNUMBER = $1 WHERE ID = $2")
-										.bind(0, world.randomNumber).bind(1, world.id).execute())
-								.flatMap(result -> Flux.from(result.getRowsUpdated()).map(updated -> world));
-					}).collectList().subscribe(worlds -> {
+					}))).collectList().flatMap(worlds -> {
+						Collections.sort(worlds, (a, b) -> a.id - b.id);
+						Batch batch = db.createBatch();
+						for (World world : worlds) {
+							world.randomNumber = ThreadLocalRandom.current().nextInt(1, 10001);
+							batch.add("UPDATE WORLD SET RANDOMNUMBER = " + world.randomNumber + " WHERE ID = "
+									+ world.id);
+						}
+						return Mono.from(batch.execute()).map((result) -> worlds);
+					}).subscribe(worlds -> {
 						try {
 							response.setContentType(APPLICATION_JSON, null);
 							this.objectMapper.writeValue(response.getEntityWriter(), worlds);
@@ -469,18 +558,37 @@ public class RawOfficeFloorMain {
 						}
 					}, error -> {
 						this.sendError(connection, error);
+					}, () -> {
+						if (queryCount < LARGE_QUERY) {
+							rateLimit.processed(executedQueryCount);
+						}
 					});
 		}
 
 		private void sendError(ProcessAwareServerHttpConnectionManagedObject<ByteBuffer> connection,
 				Throwable failure) {
 			try {
-				failure.printStackTrace();
 
+				// Setup to send response
 				HttpResponse response = connection.getResponse();
 				response.reset();
-				response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
+
+				// Determine type of error
+				if (failure instanceof R2dbcTransientResourceException) {
+
+					// Indicate overloaded
+					response.setStatus(HttpStatus.SERVICE_UNAVAILABLE);
+
+				} else {
+					// Provide details of failure
+					response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
+					response.setContentType(TEXT_PLAIN, null);
+					failure.printStackTrace(new PrintWriter(response.getEntityWriter()));
+				}
+
+				// Send error response
 				this.send(connection);
+
 			} catch (IOException ex) {
 				ex.printStackTrace();
 			}
@@ -496,6 +604,67 @@ public class RawOfficeFloorMain {
 		}
 	}
 
+	private static class ThreadLocalRateLimit extends ThreadLocal<RateLimit> {
+		@Override
+		protected RateLimit initialValue() {
+			return new RateLimit();
+		}
+	}
+
+	private static class RateLimit {
+
+		private final int INITIAL_REQUEST_COUNT = 738 / Runtime.getRuntime().availableProcessors();
+
+		private int requestCount = 0;
+
+		private final AtomicInteger activeQueries = new AtomicInteger(0);
+
+		private boolean isActiveLimit = false;
+
+		public boolean isLimit(int queryCount) {
+
+			// Increment the request count
+			this.requestCount = this.requestCount + ((this.requestCount > INITIAL_REQUEST_COUNT) ? 0 : 1);
+
+			// Ensure initial requests are processed
+			do {
+
+				// Determine if query count reached
+				this.activeQueries.updateAndGet((count) -> {
+					int newCount = count + queryCount;
+					if (newCount > QUERY_BUFFER_SIZE) {
+						// Max limit reached
+						this.isActiveLimit = true;
+						return count;
+					} else {
+						// Allow input
+						this.isActiveLimit = false;
+						return newCount;
+					}
+				});
+				if (this.requestCount > INITIAL_REQUEST_COUNT) {
+					// Initial requests processed, so start possible throttling
+					return this.isActiveLimit;
+				}
+
+				// Allow some processing time if limit hit
+				if (this.isActiveLimit) {
+					try {
+						Thread.sleep(1);
+					} catch (InterruptedException ex) {
+						// continue processing
+					}
+				}
+
+			} while (this.isActiveLimit);
+			return false; // below limit
+		}
+
+		public void processed(int queryCount) {
+			this.activeQueries.updateAndGet((count) -> count - queryCount);
+		}
+	}
+
 	@Data
 	public static class Message {
 		private final String message;

+ 1 - 1
frameworks/Java/officefloor/src/woof_benchmark_spring/src/main/resources/application.teams

@@ -1,6 +1,6 @@
 <teams>
 
-	<team size="512" source="net.officefloor.frame.impl.spi.team.ExecutorCachedTeamSource">	
+	<team size="256" source="net.officefloor.frame.impl.spi.team.ExecutorCachedTeamSource">	
 		<auto-wire type="net.officefloor.benchmark.WorldRepository" />
 		<auto-wire type="net.officefloor.benchmark.FortuneRepository" />
 	</team>